diff --git a/.deprecated_files b/.deprecated_files index 888a8e8a..2ac2e3e1 100644 --- a/.deprecated_files +++ b/.deprecated_files @@ -14,3 +14,9 @@ scripts/check_mandatory_and_static_files.py scripts/update_static_files.py docs + +setup.py +setup.cfg + +.pylintrc +.flake8 diff --git a/.devcontainer/dev_install b/.devcontainer/dev_install index 31cb590a..b5ee1890 100755 --- a/.devcontainer/dev_install +++ b/.devcontainer/dev_install @@ -6,11 +6,11 @@ cd /workspace # upgrade pip python -m pip install --upgrade pip -# install with all extras in editable mode -pip install -e .[all] - # install or upgrade dependencies for development and testing -pip install -r requirements-dev.txt +pip install --no-deps -r requirements-dev.txt + +# install the package itself in edit mode: +pip install --no-deps -e . # install pre-commit hooks to git pre-commit install diff --git a/.flake8 b/.flake8 deleted file mode 100644 index bfd9ba41..00000000 --- a/.flake8 +++ /dev/null @@ -1,21 +0,0 @@ -; Copyright 2021 - 2023 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln -; for the German Human Genome-Phenome Archive (GHGA) -; -; Licensed under the Apache License, Version 2.0 (the "License"); -; you may not use this file except in compliance with the License. -; You may obtain a copy of the License at -; -; http://www.apache.org/licenses/LICENSE-2.0 -; -; Unless required by applicable law or agreed to in writing, software -; distributed under the License is distributed on an "AS IS" BASIS, -; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -; See the License for the specific language governing permissions and -; limitations under the License. - -[flake8] -ignore = E, W - # ignore all style checks from pycodestyle - # as they are already checked by black -exclude = .git,__pycache__,build,dist -max-complexity = 10 diff --git a/.github/workflows/pypi_publish.yaml b/.github/workflows/pypi_publish.yaml index 029e60f5..3ded6694 100644 --- a/.github/workflows/pypi_publish.yaml +++ b/.github/workflows/pypi_publish.yaml @@ -18,7 +18,7 @@ jobs: - name: Verify Package Version vs Tag Version run: | - PKG_VER="$(python setup.py -V)" + PKG_VER="$(grep -oP 'version = "\K[^"]+' pyproject.toml)" TAG_VER="${GITHUB_REF##*/}" echo "Package version is $PKG_VER" >&2 echo "Tag version is $TAG_VER" >&2 diff --git a/.github/workflows/static_code_analysis.yaml b/.github/workflows/static_code_analysis.yaml index 31feccf5..401f4289 100644 --- a/.github/workflows/static_code_analysis.yaml +++ b/.github/workflows/static_code_analysis.yaml @@ -10,29 +10,19 @@ jobs: - uses: actions/checkout@v3 - id: common - uses: ghga-de/gh-action-common@v2 + uses: ghga-de/gh-action-common@v3 - uses: pre-commit/action@v3.0.0 env: SKIP: no-commit-to-branch + - name: ruff + uses: chartboost/ruff-action@v1 - name: black run: | black --check . - - name: isort - run: | - isort --check --profile black . - name: mypy run: | mypy . - - name: pylint - run: | - pylint "${{ steps.common.outputs.MAIN_SRC_DIR }}" - - name: flake8 - run: | - flake8 --config .flake8 - - name: bandit - run: | - bandit -r "${{ steps.common.outputs.MAIN_SRC_DIR }}" - name: Check license header and file run: | ./scripts/license_checker.py diff --git a/.github/workflows/unit_and_int_tests.yaml b/.github/workflows/unit_and_int_tests.yaml index d6e5ed3a..92127f34 100644 --- a/.github/workflows/unit_and_int_tests.yaml +++ b/.github/workflows/unit_and_int_tests.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - id: common - uses: ghga-de/gh-action-common@v2 + uses: ghga-de/gh-action-common@v3 - name: Run pytest run: | diff --git a/.gitignore b/.gitignore index 28b8a9dc..a71cfd2a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ coverage.xml *.py,cover .hypothesis/ .pytest_cache/ +.ruff_cache/ prof/ # Translations diff --git a/.mandatory_files b/.mandatory_files index d7cf9495..c0b51fb1 100644 --- a/.mandatory_files +++ b/.mandatory_files @@ -19,10 +19,12 @@ Dockerfile config_schema.json example_config.yaml LICENSE +pyproject.toml README.md -setup.py -setup.cfg +requirements-dev.in requirements-dev.txt +requirements.txt .description.md .design.md +.pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 31f8a5b8..ab5aca06 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,19 @@ default_language_version: minimum_pre_commit_version: 3.0.0 repos: + - repo: local + hooks: + - id: update-hook-revs + name: "ensure hooks are up to date" + language: python + additional_dependencies: + - "typer" + fail_fast: true + always_run: true + entry: ./scripts/update_hook_revs.py + files: '\.pre-commit-config.yaml' + args: [--check] + pass_filenames: false - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: @@ -36,33 +49,17 @@ repos: - id: debug-statements - id: debug-statements - id: debug-statements + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.291 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.9.1 hooks: - id: black - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - args: [--profile, black] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.0.0 + rev: v1.5.1 hooks: - id: mypy args: [--no-warn-unused-ignores] - - repo: https://github.com/PyCQA/pylint - rev: v2.16.4 - hooks: - - id: pylint - args: [--disable=E0401] - exclude: tests|.devcontainer - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - args: [--config, .flake8] - - repo: https://github.com/PyCQA/bandit - rev: 1.7.4 - hooks: - - id: bandit - exclude: tests|.devcontainer|scripts diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index cfb50dbb..00000000 --- a/.pylintrc +++ /dev/null @@ -1,546 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-allow-list= - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. (This is an alternative name to extension-pkg-allow-list -# for backward compatibility.) -extension-pkg-whitelist=pydantic - -# Specify a score threshold to be exceeded before program exits with error. -fail-under=10.0 - -# Files or directories to be skipped. They should be base names, not paths. -ignore=CVS - - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable= - duplicate-code, # is behaving strangely sometimes and cannot - # be disabled on an individual basis: - # https://github.com/PyCQA/pylint/issues/214 - - too-few-public-methods, # says that classes should always have methods - # but that is not true anymore (e.g. dataclasses) - - unnecessary-ellipsis, # often used for interfaces/protocols - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'error', 'warning', 'refactor', and 'convention' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit,argparse.parse_error - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -#notes-rgx= - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it work, -# install the 'python-enchant' package. -spelling-dict= - -# List of comma separated words that should be considered directives if they -# appear and the beginning of a comment and should not be checked. -spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Bad variable names regexes, separated by a comma. If names match any regex, -# they will always be refused -bad-names-rgxs= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class constant names. -class-const-naming-style=UPPER_CASE - -# Regular expression matching correct class constant names. Overrides class- -# const-naming-style. -#class-const-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - ex, - ok, - Run, - _, - __, - id, - db, - -# Good variable names regexes, separated by a comma. If names match any regex, -# they will always be accepted -good-names-rgxs= - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of names allowed to shadow builtins -allowed-redefined-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[DESIGN] - -# Maximum number of arguments for function / method. -max-args=5 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=5 - -# Maximum number of branch for function / method body. -max-branches=12 - -# Maximum number of locals for function / method body. -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=6 - -# Maximum number of statements in function / method body. -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules=optparse,tkinter.tix - -# Output a graph (.gv or any supported image format) of external dependencies -# to the given file (report RP0402 must not be disabled). -ext-import-graph= - -# Output a graph (.gv or any supported image format) of all (i.e. internal and -# external) dependencies to the given file (report RP0402 must not be -# disabled). -import-graph= - -# Output a graph (.gv or any supported image format) of internal dependencies -# to the given file (report RP0402 must not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[CLASSES] - -# Warn about protected attribute access inside special methods -check-protected-access-in-special-methods=no - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "builtins.BaseException, builtins.Exception". -overgeneral-exceptions=builtins.BaseException, - builtins.Exception diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 00000000..99823e81 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,62 @@ +exclude = [ + ".git", + ".devcontainer", + "__pycache__", + "build", + "dist", +] + +ignore = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings - pycodestyle covered by black + "PLW", # pylint warnings + "RUF001", # ambiguous unicode character strings + "RUF010", # explicit conversion to string or repr: !s or !r + "RUF012", # mutable class variables need typing.ClassVar annotation + "N818", # Errors need to have Error suffix + "B008", # function call in arg defaults, + "PLR2004", # magic numbers should be constants + "D205", # blank-line-after-summary + "D400", # first doc line ends in period + "D401", # non-imperative-mood + "D107", # missing docstring in __init__ +] + +line-length = 88 + +select = [ + "C90", # McCabe Complexity + "F", # pyflakes codes + "I", # isort + "S", # flake8-bandit + "B", # flake8-bugbear + "N", # pep8-naming + "UP", # pyupgrade + "PL", # pylint + "RUF", # ruff + "SIM", # flake8-simplify + "D", # pydocstyle +] + +fixable = [ + "UP", # e.g. List -> list + "I", # sort imports + "D", # pydocstyle +] + +src = ["src", "tests", "examples", "scripts"] + +target-version = "py39" + +[mccabe] +max-complexity = 10 + +[per-file-ignores] +"scripts/*" = ["PL", "S", "SIM", "D"] +"tests/*" = ["S", "SIM", "PLR"] +".devcontainer/*" = ["S", "SIM", "D"] +"examples/*" = ["S", "D"] +"__init__.py" = ["D"] + +[pydocstyle] +convention = "pep257" diff --git a/.static_files b/.static_files index 3ebfa187..2d923bb5 100644 --- a/.static_files +++ b/.static_files @@ -23,6 +23,8 @@ scripts/update_config_docs.py scripts/update_template_files.py scripts/update_openapi_docs.py scripts/update_readme.py +scripts/update_lock.py +scripts/update_hook_revs.py scripts/README.md .github/workflows/check_config_docs.yaml @@ -36,18 +38,14 @@ scripts/README.md example_data/README.md .editorconfig -.flake8 .gitattributes .gitignore -.pre-commit-config.yaml -.pylintrc .mypy.ini +.ruff.toml pytest.ini LICENSE -requirements.txt -requirements-dev-common.txt -setup.py +requirements-dev-common.in .readme_template.md readme_generation.md diff --git a/examples/stream_calc/sc_tests/integration/test_event_api.py b/examples/stream_calc/sc_tests/integration/test_event_api.py index 018b6ec1..217b457c 100644 --- a/examples/stream_calc/sc_tests/integration/test_event_api.py +++ b/examples/stream_calc/sc_tests/integration/test_event_api.py @@ -27,11 +27,11 @@ import pytest from kafka import KafkaConsumer, KafkaProducer -from stream_calc.config import Config -from stream_calc.main import main from testcontainers.kafka import KafkaContainer from hexkit.custom_types import JsonObject +from stream_calc.config import Config +from stream_calc.main import main DEFAULT_CONFIG = Config() diff --git a/examples/stream_calc/sc_tests/unit/test_calc.py b/examples/stream_calc/sc_tests/unit/test_calc.py index bca5016d..563ba6c8 100644 --- a/examples/stream_calc/sc_tests/unit/test_calc.py +++ b/examples/stream_calc/sc_tests/unit/test_calc.py @@ -20,6 +20,7 @@ from unittest.mock import AsyncMock import pytest + from stream_calc.core.calc import StreamCalculator diff --git a/examples/stream_calc/sc_tests/unit/test_eventpub.py b/examples/stream_calc/sc_tests/unit/test_eventpub.py index 5d00990e..f00de8bf 100644 --- a/examples/stream_calc/sc_tests/unit/test_eventpub.py +++ b/examples/stream_calc/sc_tests/unit/test_eventpub.py @@ -17,13 +17,13 @@ """Testing the `translators.eventpub` module.""" import pytest + +from hexkit.providers.testing.eventpub import InMemEventPublisher from stream_calc.translators.eventpub import ( EventResultEmitter, EventResultEmitterConfig, ) -from hexkit.providers.testing.eventpub import InMemEventPublisher - @pytest.mark.asyncio async def test_emit_result(): diff --git a/examples/stream_calc/sc_tests/unit/test_eventsub.py b/examples/stream_calc/sc_tests/unit/test_eventsub.py index e11713e5..b0dd9388 100644 --- a/examples/stream_calc/sc_tests/unit/test_eventsub.py +++ b/examples/stream_calc/sc_tests/unit/test_eventsub.py @@ -19,13 +19,13 @@ from unittest.mock import AsyncMock import pytest + +from hexkit.custom_types import JsonObject from stream_calc.translators.eventsub import ( EventProblemReceiver, EventProblemReceiverConfig, ) -from hexkit.custom_types import JsonObject - @pytest.mark.asyncio @pytest.mark.parametrize( diff --git a/examples/stream_calc/stream_calc/config.py b/examples/stream_calc/stream_calc/config.py index 5d275e09..a11790b3 100644 --- a/examples/stream_calc/stream_calc/config.py +++ b/examples/stream_calc/stream_calc/config.py @@ -16,11 +16,10 @@ """Config parameters.""" +from hexkit.providers.akafka import KafkaConfig from stream_calc.translators.eventpub import EventResultEmitterConfig from stream_calc.translators.eventsub import EventProblemReceiverConfig -from hexkit.providers.akafka import KafkaConfig - try: # workaround for https://github.com/pydantic/pydantic/issues/5821 from typing_extensions import Literal except ImportError: diff --git a/examples/stream_calc/stream_calc/container.py b/examples/stream_calc/stream_calc/container.py index 3dd6effe..1a57923b 100644 --- a/examples/stream_calc/stream_calc/container.py +++ b/examples/stream_calc/stream_calc/container.py @@ -22,14 +22,13 @@ """Module hosting the dependency injection container.""" # pylint: disable=wrong-import-order +from hexkit.inject import ContainerBase, get_configurator, get_constructor +from hexkit.providers.akafka import KafkaEventPublisher, KafkaEventSubscriber from stream_calc.config import Config from stream_calc.core.calc import StreamCalculator from stream_calc.translators.eventpub import EventResultEmitter from stream_calc.translators.eventsub import EventProblemReceiver -from hexkit.inject import ContainerBase, get_configurator, get_constructor -from hexkit.providers.akafka import KafkaEventPublisher, KafkaEventSubscriber - class Container(ContainerBase): """DI Container""" diff --git a/examples/stream_calc/stream_calc/translators/eventpub.py b/examples/stream_calc/stream_calc/translators/eventpub.py index 55c33aa2..45ce53dd 100644 --- a/examples/stream_calc/stream_calc/translators/eventpub.py +++ b/examples/stream_calc/stream_calc/translators/eventpub.py @@ -17,10 +17,10 @@ """Translators that target the event publishing protocol.""" from pydantic import BaseSettings -from stream_calc.ports.result_emitter import CalcResultEmitterPort from hexkit.custom_types import JsonObject from hexkit.protocols.eventpub import EventPublisherProtocol +from stream_calc.ports.result_emitter import CalcResultEmitterPort class EventResultEmitterConfig(BaseSettings): @@ -39,7 +39,7 @@ def __init__( self, *, config: EventResultEmitterConfig, - event_publisher: EventPublisherProtocol + event_publisher: EventPublisherProtocol, ) -> None: """Configure with provider for the the EventPublisherProto""" diff --git a/examples/stream_calc/stream_calc/translators/eventsub.py b/examples/stream_calc/stream_calc/translators/eventsub.py index 74b119d8..923dd44d 100644 --- a/examples/stream_calc/stream_calc/translators/eventsub.py +++ b/examples/stream_calc/stream_calc/translators/eventsub.py @@ -17,10 +17,10 @@ """Translators that target the event publishing protocol.""" from pydantic import BaseSettings -from stream_calc.ports.problem_receiver import ArithProblemHandlerPort from hexkit.custom_types import Ascii, JsonObject from hexkit.protocols.eventsub import EventSubscriberProtocol +from stream_calc.ports.problem_receiver import ArithProblemHandlerPort class EventProblemReceiverConfig(BaseSettings): diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..1eff81e5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[build-system] +requires = ["setuptools>=67.7.2"] +build-backend = "setuptools.build_meta" + +[project] +name = "hexkit" +version = "0.10.3" +description = "A Toolkit for Building Microservices using the Hexagonal Architecture" +readme = "README.md" +authors = [ + { name = "German Human Genome Phenome Archive (GHGA)", email = "contact@ghga.de" }, +] +requires-python = ">=3.9" +license = { text = "Apache 2.0" } +classifiers = [ + "Development Status :: 1 - Planning", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "License :: OSI Approved :: Apache Software License", + "Topic :: Internet :: WWW/HTTP :: HTTP Servers", + "Topic :: Software Development :: Libraries", + "Intended Audience :: Developers", +] +dependencies = [ + "pydantic >1.10.6, <2", + "PyYAML >=6.0, <7", + "dependency-injector >=4.41.0, <5", +] + +[project.optional-dependencies] +akafka = ["aiokafka~=0.8.0", "jsonschema >=4.17.3, <5"] +s3 = ["boto3 >=1.26.50, <2", "botocore >=1.29.50, <2"] +mongodb = ["motor >=3.1.1, <4"] + +test-akafka = ["hexkit[akafka]", "testcontainers[kafka] >=3.4.1, <4"] +test-s3 = ["hexkit[s3]", "testcontainers >=3.4.1, <4"] +test-mongodb = ["hexkit[mongodb]", "testcontainers[mongo] >=3.4.1, <4"] +test = ["hexkit[test-akafka,test-s3,test-mongodb]"] + +all = ["hexkit[test]"] + +[project.urls] +Repository = "https://github.com/ghga-de/hexkit" + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/requirements-dev-common.in b/requirements-dev-common.in new file mode 100644 index 00000000..1c677cd3 --- /dev/null +++ b/requirements-dev-common.in @@ -0,0 +1,34 @@ +# common requirements for development and testing of services + +pytest>=7.2.0 +pytest-asyncio>=0.20.3 +pytest-cov>=4.0.0 +pytest-profiling>=1.7.0 +snakeviz>=2.2.0 + +pre-commit>=3.1.1 + +mypy>=1.0.0 +mypy-extensions>=1.0.0 + +ruff>=0.0.290 + +black>=23.1.0 + +click>=8.1.0 +typer>=0.7.0 + +httpx>=0.23.3 +pytest-httpx>=0.21.3 + +urllib3>=1.26.15 +requests>=2.28.2 + +stringcase>=1.2.0 +jsonschema2md>=1.0.0 +setuptools>=67.7.2 + +# required since switch to pyproject.toml and pip-tools +pip-tools>=7.3.0 +tomli>=2.0.1 +tomli_w>=1.0.0 diff --git a/requirements-dev-common.txt b/requirements-dev-common.txt deleted file mode 100644 index b253cb81..00000000 --- a/requirements-dev-common.txt +++ /dev/null @@ -1,36 +0,0 @@ -# common requirements for development and testing of services - -pytest==7.2.0 -pytest-asyncio==0.20.3 -pytest-cov==4.0.0 -pytest-profiling==1.7.0 -snakeviz==2.2.0 - -pre-commit==3.1.1 - -mypy==1.0.0 -mypy-extensions==1.0.0 - -pylint==2.16.4 -flake8==6.0.0 -bandit==1.7.4 - -black==23.1.0 - -isort==5.12.0 - -click==8.1.3 -typer==0.7.0 - -httpx==0.23.3 -pytest-httpx==0.21.3 - - -# work around until this issue is solved: -# https://github.com/docker/docker-py/issues/3113 -urllib3==1.26.15 -requests==2.28.2 - -stringcase==1.2.0 -jsonschema2md==0.4.0 -setuptools==67.7.2 diff --git a/requirements-dev.in b/requirements-dev.in new file mode 100644 index 00000000..1cb47f0c --- /dev/null +++ b/requirements-dev.in @@ -0,0 +1,6 @@ +# requirements for development and testing this service + +# common requirements for development and testing +-r requirements-dev-common.in + +# additional requirements can be listed her diff --git a/requirements-dev.txt b/requirements-dev.txt index 33d3f62d..f113968e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,990 @@ -# requirements for development and testing this service +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --all-extras --generate-hashes --output-file=/workspace/requirements-dev.txt /tmp/tmpsekdwsju/pyproject.toml /workspace/requirements-dev.in +# +aiokafka==0.8.1 \ + --hash=sha256:1e24839088fd6d3ff481cc09a48ea487b997328df11630bc0a1b88255edbcfe9 \ + --hash=sha256:1f43d2afd7d3e4407ada8d754895fad7c344ca00648a8a38418d76564eaaf6cd \ + --hash=sha256:1f6044ed270b946d31f265903b5eb101940ed0ff3a902eaf8178103c943bbcc9 \ + --hash=sha256:24373bb2d519abac036d5b04ebc43452ef4ad1916953b6678b9801a9c93ba237 \ + --hash=sha256:2fa54b8b068d9d8735cb6757a0f48168f8cf9be68860b0bae6b3ed1684cef49b \ + --hash=sha256:3816bcfc3c57dfa4ed77fe1dc3a9a464e17b6400061348155115f282c8150c47 \ + --hash=sha256:45cd28af6590d6a999bb706803166570121ba8a5a0d06c51ebd8a59fab53593c \ + --hash=sha256:4693fbe3c10f125bf3e2df8a8ccbca3eff2bdaaa6589d28c7532c10e7d84598b \ + --hash=sha256:4fccd599ab6b3fda4f4187d854b343f153b40d05d6774be9acf238618da50031 \ + --hash=sha256:6421ee81084532f915501074a132acb2afc8cb88bf5ddb11e584230a30f6f006 \ + --hash=sha256:673c163dee62dfe45146d5250af0e395da5cc92b63f8878c592abc7dc1862899 \ + --hash=sha256:7d327d66b41c4e3bafff7f9efb71936a08f940aa665680717e20862e4272a068 \ + --hash=sha256:7f09784322c0d2c4fcc222add4337a5ac394aa30a248eb4e0e4587a125573c75 \ + --hash=sha256:90960356513f3979754261b132b12a96b0d9e3c6eb44420e3a90a7c31156a81a \ + --hash=sha256:935da8c4da9a00a1e16020d88e578206097b4bb72ebc2a25fbd2cb817907ef28 \ + --hash=sha256:9f19d90b7360bc2239fcd8b147508ae39c3e5b1acfc8e6a2a9b0f306070f7ffe \ + --hash=sha256:a8a641a8102c51422afe111d4bc70c51f335f38fc5906e4c839bd17afeaf3cb2 \ + --hash=sha256:af6df9a41e08b61d7e62c0a416feeabd81bad76fa5c70d499b083d6af9ce72c3 \ + --hash=sha256:b2bf97548fa77ad31062ca580368d346b16ba9fdca5856c435f256f3699ab12b \ + --hash=sha256:bbffc431d9285328c0bc108949132ae11cec863f1dd5a43a1fc3d45a69ffb8a9 \ + --hash=sha256:bf7473c55dc7959d4b7f9d750fa6017b325813d6cb761e488c2d9ea44e922954 \ + --hash=sha256:c4332d37cb9d52181cfda4236566b4028c7c188549277f87bcc3027577d72b1b \ + --hash=sha256:d300188e358cd29989c817f6ee2a2965a039e5a71de8ade6f80f02ebb9bd07b8 \ + --hash=sha256:fd8f9e17bc9cd2ea664a7f5133aede39a8fffebffe0c450252d475dbdedb4a35 \ + --hash=sha256:ff318d29ecbeea8c58d69c91c24d48d7ed4a8d3e829b607e670d118a9a35d5ba + # via hexkit (pyproject.toml) +anyio==4.0.0 \ + --hash=sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f \ + --hash=sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a + # via httpcore +async-timeout==4.0.3 \ + --hash=sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f \ + --hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028 + # via aiokafka +attrs==23.1.0 \ + --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ + --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015 + # via + # jsonschema + # referencing +black==23.9.1 \ + --hash=sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f \ + --hash=sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7 \ + --hash=sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100 \ + --hash=sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573 \ + --hash=sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d \ + --hash=sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f \ + --hash=sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9 \ + --hash=sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300 \ + --hash=sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948 \ + --hash=sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325 \ + --hash=sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9 \ + --hash=sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71 \ + --hash=sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186 \ + --hash=sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f \ + --hash=sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe \ + --hash=sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855 \ + --hash=sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80 \ + --hash=sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393 \ + --hash=sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c \ + --hash=sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204 \ + --hash=sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377 \ + --hash=sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301 + # via -r /workspace/requirements-dev-common.in +boto3==1.28.56 \ + --hash=sha256:b927a7ed335d543c33c15fa63f1076f3fa8422959771c2187da74bc4395ab6e3 \ + --hash=sha256:f5fcb27cdbd08ca38d699f2d2e32d96d1d9fab3368c15c6bc326256612d2cfd7 + # via hexkit (pyproject.toml) +botocore==1.31.56 \ + --hash=sha256:66c686e4eda7051ffcc9357d9075390c8ab2f95a2977669039618ee186fb533b \ + --hash=sha256:70252cd8abc2fe9b791328e187620f5a3911545e2520486b01ecfad31f41b9cb + # via + # boto3 + # hexkit (pyproject.toml) + # s3transfer +build==1.0.3 \ + --hash=sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b \ + --hash=sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f + # via pip-tools +certifi==2023.7.22 \ + --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ + --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 + # via + # httpcore + # httpx + # requests +cfgv==3.4.0 \ + --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \ + --hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560 + # via pre-commit +charset-normalizer==3.2.0 \ + --hash=sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96 \ + --hash=sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c \ + --hash=sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710 \ + --hash=sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706 \ + --hash=sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020 \ + --hash=sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252 \ + --hash=sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad \ + --hash=sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329 \ + --hash=sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a \ + --hash=sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f \ + --hash=sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6 \ + --hash=sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4 \ + --hash=sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a \ + --hash=sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46 \ + --hash=sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2 \ + --hash=sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23 \ + --hash=sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace \ + --hash=sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd \ + --hash=sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982 \ + --hash=sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10 \ + --hash=sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2 \ + --hash=sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea \ + --hash=sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09 \ + --hash=sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5 \ + --hash=sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149 \ + --hash=sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489 \ + --hash=sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9 \ + --hash=sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80 \ + --hash=sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592 \ + --hash=sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3 \ + --hash=sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6 \ + --hash=sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed \ + --hash=sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c \ + --hash=sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200 \ + --hash=sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a \ + --hash=sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e \ + --hash=sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d \ + --hash=sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6 \ + --hash=sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623 \ + --hash=sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669 \ + --hash=sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3 \ + --hash=sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa \ + --hash=sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9 \ + --hash=sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2 \ + --hash=sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f \ + --hash=sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1 \ + --hash=sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4 \ + --hash=sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a \ + --hash=sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8 \ + --hash=sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3 \ + --hash=sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029 \ + --hash=sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f \ + --hash=sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959 \ + --hash=sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22 \ + --hash=sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7 \ + --hash=sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952 \ + --hash=sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346 \ + --hash=sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e \ + --hash=sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d \ + --hash=sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299 \ + --hash=sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd \ + --hash=sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a \ + --hash=sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3 \ + --hash=sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037 \ + --hash=sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94 \ + --hash=sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c \ + --hash=sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858 \ + --hash=sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a \ + --hash=sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449 \ + --hash=sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c \ + --hash=sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918 \ + --hash=sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1 \ + --hash=sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c \ + --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \ + --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa + # via requests +click==8.1.7 \ + --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ + --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de + # via + # -r /workspace/requirements-dev-common.in + # black + # pip-tools + # typer +coverage[toml]==7.3.1 \ + --hash=sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375 \ + --hash=sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344 \ + --hash=sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e \ + --hash=sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745 \ + --hash=sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f \ + --hash=sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194 \ + --hash=sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a \ + --hash=sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f \ + --hash=sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760 \ + --hash=sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8 \ + --hash=sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392 \ + --hash=sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d \ + --hash=sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc \ + --hash=sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40 \ + --hash=sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981 \ + --hash=sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0 \ + --hash=sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92 \ + --hash=sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3 \ + --hash=sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0 \ + --hash=sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086 \ + --hash=sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7 \ + --hash=sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465 \ + --hash=sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140 \ + --hash=sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952 \ + --hash=sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3 \ + --hash=sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8 \ + --hash=sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f \ + --hash=sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593 \ + --hash=sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0 \ + --hash=sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204 \ + --hash=sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037 \ + --hash=sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276 \ + --hash=sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9 \ + --hash=sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26 \ + --hash=sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce \ + --hash=sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7 \ + --hash=sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136 \ + --hash=sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a \ + --hash=sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4 \ + --hash=sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c \ + --hash=sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f \ + --hash=sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832 \ + --hash=sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3 \ + --hash=sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969 \ + --hash=sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520 \ + --hash=sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887 \ + --hash=sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3 \ + --hash=sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6 \ + --hash=sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1 \ + --hash=sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff \ + --hash=sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981 \ + --hash=sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e + # via pytest-cov +dependency-injector==4.41.0 \ + --hash=sha256:02620454ee8101f77a317f3229935ce687480883d72a40858ff4b0c87c935cce \ + --hash=sha256:059fbb48333148143e8667a5323d162628dfe27c386bd0ed3deeecfc390338bf \ + --hash=sha256:05e15ea0f2b14c1127e8b0d1597fef13f98845679f63bf670ba12dbfc12a16ef \ + --hash=sha256:12e91ac0333e7e589421943ff6c6bf9cf0d9ac9703301cec37ccff3723406332 \ + --hash=sha256:1662e2ef60ac6e681b9e11b5d8b7c17a0f733688916cf695f9540f8f50a61b1e \ + --hash=sha256:168334cba3f1cbf55299ef38f0f2e31879115cc767b780c859f7814a52d80abb \ + --hash=sha256:16de2797dcfcc2263b8672bf0751166f7c7b369ca2ff9246ceb67b65f8e1d802 \ + --hash=sha256:1baee908f21190bdc46a65ce4c417a5175e9397ca62354928694fce218f84487 \ + --hash=sha256:22b11dbf696e184f0b3d5ac4e5418aeac3c379ba4ea758c04a83869b7e5d1cbf \ + --hash=sha256:300838e9d4f3fbf539892a5a4072851728e23b37a1f467afcf393edd994d88f0 \ + --hash=sha256:3055b3fc47a0d6e5f27defb4166c0d37543a4967c279549b154afaf506ce6efc \ + --hash=sha256:33a724e0a737baadb4378f5dc1b079867cc3a88552fcca719b3dba84716828b2 \ + --hash=sha256:3535d06416251715b45f8412482b58ec1c6196a4a3baa207f947f0b03a7c4b44 \ + --hash=sha256:3588bd887b051d16b8bcabaae1127eb14059a0719a8fe34c8a75ba59321b352c \ + --hash=sha256:3744c327d18408e74781bd6d8b7738745ee80ef89f2c8daecf9ebd098cb84972 \ + --hash=sha256:37d5954026e3831663518d78bdf4be9c2dbfea691edcb73c813aa3093aa4363a \ + --hash=sha256:40936d9384363331910abd59dd244158ec3572abf9d37322f15095315ac99893 \ + --hash=sha256:409441122f40e1b4b8582845fdd76deb9dc5c9d6eb74a057b85736ef9e9c671f \ + --hash=sha256:48b6886a87b4ceb9b9f78550f77b2a5c7d2ce33bc83efd886556ad468cc9c85a \ + --hash=sha256:4a31d9d60be4b585585081109480cfb2ef564d3b851cb32a139bf8408411a93a \ + --hash=sha256:4a44ca3ce5867513a70b31855b218be3d251f5068ce1c480cc3a4ad24ffd3280 \ + --hash=sha256:51217cb384b468d7cc355544cec20774859f00812f9a1a71ed7fa701c957b2a7 \ + --hash=sha256:5168dc59808317dc4cdd235aa5d7d556d33e5600156acaf224cead236b48a3e8 \ + --hash=sha256:54032d62610cf2f4421c9d92cef52957215aaa0bca403cda580c58eb3f726eda \ + --hash=sha256:56d37b9d2f50a18f059d9abdbea7669a7518bd42b81603c21a27910a2b3f1657 \ + --hash=sha256:586a0821720b15932addbefb00f7370fbcd5831d6ebbd6494d774b44ff96d23a \ + --hash=sha256:5fa3ed8f0700e47a0e7363f949b4525ffa8277aa1c5b10ca5b41fce4dea61bb9 \ + --hash=sha256:63bfba21f8bff654a80e9b9d06dd6c43a442990b73bf89cd471314c11c541ec2 \ + --hash=sha256:67b369592c57549ccdcad0d5fef1ddb9d39af7fed8083d76e789ab0111fc6389 \ + --hash=sha256:6b29abac56ce347d2eb58a560723e1663ee2125cf5cc38866ed92b84319927ec \ + --hash=sha256:6b98945edae88e777091bf0848f869fb94bd76dfa4066d7c870a5caa933391d0 \ + --hash=sha256:6ee9810841c6e0599356cb884d16453bfca6ab739d0e4f0248724ed8f9ee0d79 \ + --hash=sha256:740a8e8106a04d3f44b52b25b80570fdac96a8a3934423de7c9202c5623e7936 \ + --hash=sha256:75280dfa23f7c88e1bf56c3920d58a43516816de6f6ab2a6650bb8a0f27d5c2c \ + --hash=sha256:75e7a733b372db3144a34020c4233f6b94db2c6342d6d16bc5245b1b941ee2bd \ + --hash=sha256:76b94c8310929e54136f3cb3de3adc86d1a657b3984299f40bf1cd2ba0bae548 \ + --hash=sha256:786f7aac592e191c9caafc47732161d807bad65c62f260cd84cd73c7e2d67d6d \ + --hash=sha256:7a92680bea1c260e5c0d2d6cd60b0c913cba76a456a147db5ac047ecfcfcc758 \ + --hash=sha256:7dcba8665cafec825b7095d5dd80afb5cf14404450eca3fe8b66e1edbf4dbc10 \ + --hash=sha256:7fa4970f12a3fc95d8796938b11c41276ad1ff4c447b0e589212eab3fc527a90 \ + --hash=sha256:87be84084a1b922c4ba15e2e5aa900ee24b78a5467997cb7aec0a1d6cdb4a00b \ + --hash=sha256:89c67edffe7007cf33cee79ecbca38f48efcc2add5c280717af434db6c789377 \ + --hash=sha256:8b51efeaebacaf79ef68edfc65e9687699ccffb3538c4a3ab30d0d77e2db7189 \ + --hash=sha256:8b8cf1c6c56f5c18bdbd9f5e93b52ca29cb4d99606d4056e91f0c761eef496dc \ + --hash=sha256:8d670a844268dcd758195e58e9a5b39fc74bb8648aba99a13135a4a10ec9cfac \ + --hash=sha256:8f0090ff14038f17a026ca408a3a0b0e7affb6aa7498b2b59d670f40ac970fbe \ + --hash=sha256:939dfc657104bc3e66b67afd3fb2ebb0850c9a1e73d0d26066f2bbdd8735ff9c \ + --hash=sha256:953bfac819d32dc72b963767589e0ed372e5e9e78b03fb6b89419d0500d34bbe \ + --hash=sha256:99ed73b1521bf249e2823a08a730c9f9413a58f4b4290da022e0ad4fb333ba3d \ + --hash=sha256:9e3b9d41e0eff4c8e16fea1e33de66ff0030fe51137ca530f3c52ce110447914 \ + --hash=sha256:a2381a251b04244125148298212550750e6e1403e9b2850cc62e0e829d050ad3 \ + --hash=sha256:a2dee5d4abdd21f1a30a51d46645c095be9dcc404c7c6e9f81d0a01415a49e64 \ + --hash=sha256:a4f113e5d4c3070973ad76e5bda7317e500abae6083d78689f0b6e37cf403abf \ + --hash=sha256:a8686fa330c83251c75c8238697686f7a0e0f6d40658538089165dc72df9bcff \ + --hash=sha256:ac79f3c05747f9724bd56c06985e78331fc6c85eb50f3e3f1a35e0c60f9977e9 \ + --hash=sha256:b0c9c966ff66c77364a2d43d08de9968aff7e3903938fe912ba49796b2133344 \ + --hash=sha256:b2440b32474d4e747209528ca3ae48f42563b2fbe3d74dbfe949c11dfbfef7c4 \ + --hash=sha256:b365a8548e9a49049fa6acb24d3cd939f619eeb8e300ca3e156e44402dcc07ec \ + --hash=sha256:b37f36ecb0c1227f697e1d4a029644e3eda8dd0f0716aa63ad04d96dbb15bbbb \ + --hash=sha256:b3890a12423ae3a9eade035093beba487f8d092ee6c6cb8706f4e7080a56e819 \ + --hash=sha256:b8b61a15bc46a3aa7b29bd8a7384b650aa3a7ef943491e93c49a0540a0b3dda4 \ + --hash=sha256:bc852da612c7e347f2fcf921df2eca2718697a49f648a28a63db3ab504fd9510 \ + --hash=sha256:c71d30b6708438050675f338edb9a25bea6c258478dbe5ec8405286756a2d347 \ + --hash=sha256:d03f5fa0fa98a18bd0dfce846db80e2798607f0b861f1f99c97f441f7669d7a2 \ + --hash=sha256:d09c08c944a25dabfb454238c1a889acd85102b93ae497de523bf9ab7947b28a \ + --hash=sha256:d283aee588a72072439e6721cb64aa6cba5bc18c576ef0ab28285a6ec7a9d655 \ + --hash=sha256:d557e40673de984f78dab13ebd68d27fbb2f16d7c4e3b663ea2fa2f9fae6765b \ + --hash=sha256:e3229d83e99e255451605d5276604386e06ad948e3d60f31ddd796781c77f76f \ + --hash=sha256:f2842e15bae664a9f69932e922b02afa055c91efec959cb1896f6c499bf68180 \ + --hash=sha256:f89a507e389b7e4d4892dd9a6f5f4da25849e24f73275478634ac594d621ab3f + # via hexkit (pyproject.toml) +deprecation==2.1.0 \ + --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ + --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a + # via testcontainers +distlib==0.3.7 \ + --hash=sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057 \ + --hash=sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8 + # via virtualenv +dnspython==2.4.2 \ + --hash=sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8 \ + --hash=sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984 + # via pymongo +docker==6.1.3 \ + --hash=sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20 \ + --hash=sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9 + # via testcontainers +exceptiongroup==1.1.3 \ + --hash=sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9 \ + --hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3 + # via + # anyio + # pytest +filelock==3.12.4 \ + --hash=sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4 \ + --hash=sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd + # via virtualenv +gprof2dot==2022.7.29 \ + --hash=sha256:45b4d298bd36608fccf9511c3fd88a773f7a1abc04d6cd39445b11ba43133ec5 \ + --hash=sha256:f165b3851d3c52ee4915eb1bd6cca571e5759823c2cd0f71a79bda93c2dc85d6 + # via pytest-profiling +h11==0.14.0 \ + --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ + --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 + # via httpcore +httpcore==0.18.0 \ + --hash=sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9 \ + --hash=sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced + # via httpx +httpx==0.25.0 \ + --hash=sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100 \ + --hash=sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875 + # via + # -r /workspace/requirements-dev-common.in + # pytest-httpx +identify==2.5.29 \ + --hash=sha256:24437fbf6f4d3fe6efd0eb9d67e24dd9106db99af5ceb27996a5f7895f24bf1b \ + --hash=sha256:d43d52b86b15918c137e3a74fff5224f60385cd0e9c38e99d07c257f02f151a5 + # via pre-commit +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via + # anyio + # httpx + # requests +importlib-metadata==6.8.0 \ + --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \ + --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743 + # via build +iniconfig==2.0.0 \ + --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ + --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 + # via pytest +jmespath==1.0.1 \ + --hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \ + --hash=sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe + # via + # boto3 + # botocore +jsonschema==4.19.1 \ + --hash=sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e \ + --hash=sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf + # via hexkit (pyproject.toml) +jsonschema-specifications==2023.7.1 \ + --hash=sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1 \ + --hash=sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb + # via jsonschema +jsonschema2md==1.0.0 \ + --hash=sha256:01e18693c8dcdd643aa79ce757c5896877c590493422d23fd0bd76562fbc424b \ + --hash=sha256:1d1333cb6d55a152ce10051b05651bc71122c0ec5e572b3433046825661569d4 + # via -r /workspace/requirements-dev-common.in +kafka-python==2.0.2 \ + --hash=sha256:04dfe7fea2b63726cd6f3e79a2d86e709d608d74406638c5da33a01d45a9d7e3 \ + --hash=sha256:2d92418c7cb1c298fa6c7f0fb3519b520d0d7526ac6cb7ae2a4fc65a51a94b6e + # via + # aiokafka + # testcontainers +motor==3.3.1 \ + --hash=sha256:a0dee83ad0d47b353932ac37467ba397b1e649ce7e3eea7f5a90554883d7cdbe \ + --hash=sha256:c5eb400e27d722a3db03a9826656b6d13acf9b6c70c2fb4604f474eac9da5be4 + # via hexkit (pyproject.toml) +mypy==1.5.1 \ + --hash=sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315 \ + --hash=sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0 \ + --hash=sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373 \ + --hash=sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a \ + --hash=sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161 \ + --hash=sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275 \ + --hash=sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693 \ + --hash=sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb \ + --hash=sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65 \ + --hash=sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4 \ + --hash=sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb \ + --hash=sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243 \ + --hash=sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14 \ + --hash=sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4 \ + --hash=sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1 \ + --hash=sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a \ + --hash=sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160 \ + --hash=sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25 \ + --hash=sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12 \ + --hash=sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d \ + --hash=sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92 \ + --hash=sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770 \ + --hash=sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2 \ + --hash=sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70 \ + --hash=sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb \ + --hash=sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5 \ + --hash=sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f + # via -r /workspace/requirements-dev-common.in +mypy-extensions==1.0.0 \ + --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ + --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 + # via + # -r /workspace/requirements-dev-common.in + # black + # mypy +nodeenv==1.8.0 \ + --hash=sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2 \ + --hash=sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec + # via pre-commit +packaging==23.1 \ + --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ + --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f + # via + # aiokafka + # black + # build + # deprecation + # docker + # pytest +pathspec==0.11.2 \ + --hash=sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20 \ + --hash=sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3 + # via black +pip-tools==7.3.0 \ + --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \ + --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d + # via -r /workspace/requirements-dev-common.in +platformdirs==3.10.0 \ + --hash=sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d \ + --hash=sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d + # via + # black + # virtualenv +pluggy==1.3.0 \ + --hash=sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12 \ + --hash=sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7 + # via pytest +pre-commit==3.4.0 \ + --hash=sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522 \ + --hash=sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945 + # via -r /workspace/requirements-dev-common.in +pydantic==1.10.13 \ + --hash=sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548 \ + --hash=sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80 \ + --hash=sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340 \ + --hash=sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01 \ + --hash=sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132 \ + --hash=sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599 \ + --hash=sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1 \ + --hash=sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8 \ + --hash=sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe \ + --hash=sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0 \ + --hash=sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17 \ + --hash=sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953 \ + --hash=sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f \ + --hash=sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f \ + --hash=sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d \ + --hash=sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127 \ + --hash=sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8 \ + --hash=sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f \ + --hash=sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580 \ + --hash=sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6 \ + --hash=sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691 \ + --hash=sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87 \ + --hash=sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd \ + --hash=sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96 \ + --hash=sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687 \ + --hash=sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33 \ + --hash=sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69 \ + --hash=sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653 \ + --hash=sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78 \ + --hash=sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261 \ + --hash=sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f \ + --hash=sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9 \ + --hash=sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d \ + --hash=sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737 \ + --hash=sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5 \ + --hash=sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0 + # via hexkit (pyproject.toml) +pymongo==4.5.0 \ + --hash=sha256:076afa0a4a96ca9f77fec0e4a0d241200b3b3a1766f8d7be9a905ecf59a7416b \ + --hash=sha256:08819da7864f9b8d4a95729b2bea5fffed08b63d3b9c15b4fea47de655766cf5 \ + --hash=sha256:0a1f26bc1f5ce774d99725773901820dfdfd24e875028da4a0252a5b48dcab5c \ + --hash=sha256:0f4b125b46fe377984fbaecf2af40ed48b05a4b7676a2ff98999f2016d66b3ec \ + --hash=sha256:1240edc1a448d4ada4bf1a0e55550b6292420915292408e59159fd8bbdaf8f63 \ + --hash=sha256:152259f0f1a60f560323aacf463a3642a65a25557683f49cfa08c8f1ecb2395a \ + --hash=sha256:168172ef7856e20ec024fe2a746bfa895c88b32720138e6438fd765ebd2b62dd \ + --hash=sha256:1b1d7d9aabd8629a31d63cd106d56cca0e6420f38e50563278b520f385c0d86e \ + --hash=sha256:1d40ad09d9f5e719bc6f729cc6b17f31c0b055029719406bd31dde2f72fca7e7 \ + --hash=sha256:21b953da14549ff62ea4ae20889c71564328958cbdf880c64a92a48dda4c9c53 \ + --hash=sha256:23cc6d7eb009c688d70da186b8f362d61d5dd1a2c14a45b890bd1e91e9c451f2 \ + --hash=sha256:2988ef5e6b360b3ff1c6d55c53515499de5f48df31afd9f785d788cdacfbe2d3 \ + --hash=sha256:2a0aade2b11dc0c326ccd429ee4134d2d47459ff68d449c6d7e01e74651bd255 \ + --hash=sha256:2b0176f9233a5927084c79ff80b51bd70bfd57e4f3d564f50f80238e797f0c8a \ + --hash=sha256:2d4fa1b01fa7e5b7bb8d312e3542e211b320eb7a4e3d8dc884327039d93cb9e0 \ + --hash=sha256:3236cf89d69679eaeb9119c840f5c7eb388a2110b57af6bb6baf01a1da387c18 \ + --hash=sha256:33faa786cc907de63f745f587e9879429b46033d7d97a7b84b37f4f8f47b9b32 \ + --hash=sha256:37df8f6006286a5896d1cbc3efb8471ced42e3568d38e6cb00857277047b0d63 \ + --hash=sha256:3a7166d57dc74d679caa7743b8ecf7dc3a1235a9fd178654dddb2b2a627ae229 \ + --hash=sha256:3d79ae3bb1ff041c0db56f138c88ce1dfb0209f3546d8d6e7c3f74944ecd2439 \ + --hash=sha256:3e33064f1984db412b34d51496f4ea785a9cff621c67de58e09fb28da6468a52 \ + --hash=sha256:3fa3648e4f1e63ddfe53563ee111079ea3ab35c3b09cd25bc22dadc8269a495f \ + --hash=sha256:40d5f6e853ece9bfc01e9129b228df446f49316a4252bb1fbfae5c3c9dedebad \ + --hash=sha256:41771b22dd2822540f79a877c391283d4e6368125999a5ec8beee1ce566f3f82 \ + --hash=sha256:435228d3c16a375274ac8ab9c4f9aef40c5e57ddb8296e20ecec9e2461da1017 \ + --hash=sha256:44ee985194c426ddf781fa784f31ffa29cb59657b2dba09250a4245431847d73 \ + --hash=sha256:465fd5b040206f8bce7016b01d7e7f79d2fcd7c2b8e41791be9632a9df1b4999 \ + --hash=sha256:496c9cbcb4951183d4503a9d7d2c1e3694aab1304262f831d5e1917e60386036 \ + --hash=sha256:49dce6957598975d8b8d506329d2a3a6c4aee911fa4bbcf5e52ffc6897122950 \ + --hash=sha256:4c42748ccc451dfcd9cef6c5447a7ab727351fd9747ad431db5ebb18a9b78a4d \ + --hash=sha256:505f8519c4c782a61d94a17b0da50be639ec462128fbd10ab0a34889218fdee3 \ + --hash=sha256:53f2dda54d76a98b43a410498bd12f6034b2a14b6844ca08513733b2b20b7ad8 \ + --hash=sha256:56320c401f544d762fc35766936178fbceb1d9261cd7b24fbfbc8fb6f67aa8a5 \ + --hash=sha256:58a63a26a1e3dc481dd3a18d6d9f8bd1d576cd1ffe0d479ba7dd38b0aeb20066 \ + --hash=sha256:5caee7bd08c3d36ec54617832b44985bd70c4cbd77c5b313de6f7fce0bb34f93 \ + --hash=sha256:631492573a1bef2f74f9ac0f9d84e0ce422c251644cd81207530af4aa2ee1980 \ + --hash=sha256:63d8019eee119df308a075b8a7bdb06d4720bf791e2b73d5ab0e7473c115d79c \ + --hash=sha256:6422b6763b016f2ef2beedded0e546d6aa6ba87910f9244d86e0ac7690f75c96 \ + --hash=sha256:681f252e43b3ef054ca9161635f81b730f4d8cadd28b3f2b2004f5a72f853982 \ + --hash=sha256:6d64878d1659d2a5bdfd0f0a4d79bafe68653c573681495e424ab40d7b6d6d41 \ + --hash=sha256:74c0da07c04d0781490b2915e7514b1adb265ef22af039a947988c331ee7455b \ + --hash=sha256:7591a3beea6a9a4fa3080d27d193b41f631130e3ffa76b88c9ccea123f26dc59 \ + --hash=sha256:76a262c41c1a7cbb84a3b11976578a7eb8e788c4b7bfbd15c005fb6ca88e6e50 \ + --hash=sha256:77cfff95c1fafd09e940b3fdcb7b65f11442662fad611d0e69b4dd5d17a81c60 \ + --hash=sha256:8027c9063579083746147cf401a7072a9fb6829678076cd3deff28bb0e0f50c8 \ + --hash=sha256:80a167081c75cf66b32f30e2f1eaee9365af935a86dbd76788169911bed9b5d5 \ + --hash=sha256:840eaf30ccac122df260b6005f9dfae4ac287c498ee91e3e90c56781614ca238 \ + --hash=sha256:8543253adfaa0b802bfa88386db1009c6ebb7d5684d093ee4edc725007553d21 \ + --hash=sha256:89b3f2da57a27913d15d2a07d58482f33d0a5b28abd20b8e643ab4d625e36257 \ + --hash=sha256:8e559116e4128630ad3b7e788e2e5da81cbc2344dee246af44471fa650486a70 \ + --hash=sha256:9aff6279e405dc953eeb540ab061e72c03cf38119613fce183a8e94f31be608f \ + --hash=sha256:9c04b9560872fa9a91251030c488e0a73bce9321a70f991f830c72b3f8115d0d \ + --hash=sha256:9d2346b00af524757576cc2406414562cced1d4349c92166a0ee377a2a483a80 \ + --hash=sha256:a253b765b7cbc4209f1d8ee16c7287c4268d3243070bf72d7eec5aa9dfe2a2c2 \ + --hash=sha256:a8127437ebc196a6f5e8fddd746bd0903a400dc6b5ae35df672dd1ccc7170a2a \ + --hash=sha256:b25f7bea162b3dbec6d33c522097ef81df7c19a9300722fa6853f5b495aecb77 \ + --hash=sha256:b33c17d9e694b66d7e96977e9e56df19d662031483efe121a24772a44ccbbc7e \ + --hash=sha256:b4fe46b58010115514b842c669a0ed9b6a342017b15905653a5b1724ab80917f \ + --hash=sha256:b520aafc6cb148bac09ccf532f52cbd31d83acf4d3e5070d84efe3c019a1adbf \ + --hash=sha256:b5bbb87fa0511bd313d9a2c90294c88db837667c2bda2ea3fa7a35b59fd93b1f \ + --hash=sha256:b6d2a56fc2354bb6378f3634402eec788a8f3facf0b3e7d468db5f2b5a78d763 \ + --hash=sha256:bbd705d5f3c3d1ff2d169e418bb789ff07ab3c70d567cc6ba6b72b04b9143481 \ + --hash=sha256:bc5d8c3647b8ae28e4312f1492b8f29deebd31479cd3abaa989090fb1d66db83 \ + --hash=sha256:c3c3525ea8658ee1192cdddf5faf99b07ebe1eeaa61bf32821126df6d1b8072b \ + --hash=sha256:c9a9a39b7cac81dca79fca8c2a6479ef4c7b1aab95fad7544cc0e8fd943595a2 \ + --hash=sha256:cd4c8d6aa91d3e35016847cbe8d73106e3d1c9a4e6578d38e2c346bfe8edb3ca \ + --hash=sha256:cf62da7a4cdec9a4b2981fcbd5e08053edffccf20e845c0b6ec1e77eb7fab61d \ + --hash=sha256:d67225f05f6ea27c8dc57f3fa6397c96d09c42af69d46629f71e82e66d33fa4f \ + --hash=sha256:dfcd2b9f510411de615ccedd47462dae80e82fdc09fe9ab0f0f32f11cf57eeb5 \ + --hash=sha256:e1f61355c821e870fb4c17cdb318669cfbcf245a291ce5053b41140870c3e5cc \ + --hash=sha256:e249190b018d63c901678053b4a43e797ca78b93fb6d17633e3567d4b3ec6107 \ + --hash=sha256:e2654d1278384cff75952682d17c718ecc1ad1d6227bb0068fd826ba47d426a5 \ + --hash=sha256:e57d859b972c75ee44ea2ef4758f12821243e99de814030f69a3decb2aa86807 \ + --hash=sha256:e5a27f348909235a106a3903fc8e70f573d89b41d723a500869c6569a391cff7 \ + --hash=sha256:ead4f19d0257a756b21ac2e0e85a37a7245ddec36d3b6008d5bfe416525967dc \ + --hash=sha256:f076b779aa3dc179aa3ed861be063a313ed4e48ae9f6a8370a9b1295d4502111 \ + --hash=sha256:f1bb3a62395ffe835dbef3a1cbff48fbcce709c78bd1f52e896aee990928432b \ + --hash=sha256:f2227a08b091bd41df5aadee0a5037673f691e2aa000e1968b1ea2342afc6880 \ + --hash=sha256:f3754acbd7efc7f1b529039fcffc092a15e1cf045e31f22f6c9c5950c613ec4d \ + --hash=sha256:fe48f50fb6348511a3268a893bfd4ab5f263f5ac220782449d03cd05964d1ae7 \ + --hash=sha256:fff7d17d30b2cd45afd654b3fc117755c5d84506ed25fda386494e4e0a3416e1 + # via + # motor + # testcontainers +pyproject-hooks==1.0.0 \ + --hash=sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8 \ + --hash=sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5 + # via build +pytest==7.4.2 \ + --hash=sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002 \ + --hash=sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069 + # via + # -r /workspace/requirements-dev-common.in + # pytest-asyncio + # pytest-cov + # pytest-httpx + # pytest-profiling +pytest-asyncio==0.21.1 \ + --hash=sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d \ + --hash=sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b + # via -r /workspace/requirements-dev-common.in +pytest-cov==4.1.0 \ + --hash=sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6 \ + --hash=sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a + # via -r /workspace/requirements-dev-common.in +pytest-httpx==0.26.0 \ + --hash=sha256:b489c5a7bb847551943eaee601bc35053b35dc4f5961c944305120f14a1d770a \ + --hash=sha256:ca372b94c569c0aca2f06240f6f78cc223dfbc3ab97b5700d4e14c9a73eab17a + # via -r /workspace/requirements-dev-common.in +pytest-profiling==1.7.0 \ + --hash=sha256:93938f147662225d2b8bd5af89587b979652426a8a6ffd7e73ec4a23e24b7f29 \ + --hash=sha256:999cc9ac94f2e528e3f5d43465da277429984a1c237ae9818f8cfd0b06acb019 + # via -r /workspace/requirements-dev-common.in +python-dateutil==2.8.2 \ + --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ + --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 + # via botocore +pyyaml==6.0.1 \ + --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ + --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ + --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ + --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ + --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ + --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ + --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ + --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ + --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ + --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ + --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ + --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ + --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ + --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ + --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ + --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ + --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ + --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ + --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ + --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ + --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ + --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ + --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ + --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ + --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ + --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ + --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ + --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ + --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ + --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ + --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ + --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ + --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ + --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ + --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ + --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ + --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ + --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ + --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ + --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ + --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ + --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ + --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ + --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ + --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ + --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ + --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ + --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ + --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ + --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f + # via + # hexkit (pyproject.toml) + # jsonschema2md + # pre-commit +referencing==0.30.2 \ + --hash=sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf \ + --hash=sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 \ + --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ + --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 + # via + # -r /workspace/requirements-dev-common.in + # docker +rpds-py==0.10.3 \ + --hash=sha256:015de2ce2af1586ff5dc873e804434185199a15f7d96920ce67e50604592cae9 \ + --hash=sha256:061c3ff1f51ecec256e916cf71cc01f9975af8fb3af9b94d3c0cc8702cfea637 \ + --hash=sha256:08a80cf4884920863623a9ee9a285ee04cef57ebedc1cc87b3e3e0f24c8acfe5 \ + --hash=sha256:09362f86ec201288d5687d1dc476b07bf39c08478cde837cb710b302864e7ec9 \ + --hash=sha256:0bb4f48bd0dd18eebe826395e6a48b7331291078a879295bae4e5d053be50d4c \ + --hash=sha256:106af1653007cc569d5fbb5f08c6648a49fe4de74c2df814e234e282ebc06957 \ + --hash=sha256:11fdd1192240dda8d6c5d18a06146e9045cb7e3ba7c06de6973000ff035df7c6 \ + --hash=sha256:16a472300bc6c83fe4c2072cc22b3972f90d718d56f241adabc7ae509f53f154 \ + --hash=sha256:176287bb998fd1e9846a9b666e240e58f8d3373e3bf87e7642f15af5405187b8 \ + --hash=sha256:177914f81f66c86c012311f8c7f46887ec375cfcfd2a2f28233a3053ac93a569 \ + --hash=sha256:177c9dd834cdf4dc39c27436ade6fdf9fe81484758885f2d616d5d03c0a83bd2 \ + --hash=sha256:187700668c018a7e76e89424b7c1042f317c8df9161f00c0c903c82b0a8cac5c \ + --hash=sha256:1d9b5ee46dcb498fa3e46d4dfabcb531e1f2e76b477e0d99ef114f17bbd38453 \ + --hash=sha256:22da15b902f9f8e267020d1c8bcfc4831ca646fecb60254f7bc71763569f56b1 \ + --hash=sha256:24cd91a03543a0f8d09cb18d1cb27df80a84b5553d2bd94cba5979ef6af5c6e7 \ + --hash=sha256:255f1a10ae39b52122cce26ce0781f7a616f502feecce9e616976f6a87992d6b \ + --hash=sha256:271c360fdc464fe6a75f13ea0c08ddf71a321f4c55fc20a3fe62ea3ef09df7d9 \ + --hash=sha256:2ed83d53a8c5902ec48b90b2ac045e28e1698c0bea9441af9409fc844dc79496 \ + --hash=sha256:2f3e1867dd574014253b4b8f01ba443b9c914e61d45f3674e452a915d6e929a3 \ + --hash=sha256:35fbd23c1c8732cde7a94abe7fb071ec173c2f58c0bd0d7e5b669fdfc80a2c7b \ + --hash=sha256:37d0c59548ae56fae01c14998918d04ee0d5d3277363c10208eef8c4e2b68ed6 \ + --hash=sha256:39d05e65f23a0fe897b6ac395f2a8d48c56ac0f583f5d663e0afec1da89b95da \ + --hash=sha256:3ad59efe24a4d54c2742929001f2d02803aafc15d6d781c21379e3f7f66ec842 \ + --hash=sha256:3aed39db2f0ace76faa94f465d4234aac72e2f32b009f15da6492a561b3bbebd \ + --hash=sha256:3bbac1953c17252f9cc675bb19372444aadf0179b5df575ac4b56faaec9f6294 \ + --hash=sha256:40bc802a696887b14c002edd43c18082cb7b6f9ee8b838239b03b56574d97f71 \ + --hash=sha256:42f712b4668831c0cd85e0a5b5a308700fe068e37dcd24c0062904c4e372b093 \ + --hash=sha256:448a66b8266de0b581246ca7cd6a73b8d98d15100fb7165974535fa3b577340e \ + --hash=sha256:485301ee56ce87a51ccb182a4b180d852c5cb2b3cb3a82f7d4714b4141119d8c \ + --hash=sha256:485747ee62da83366a44fbba963c5fe017860ad408ccd6cd99aa66ea80d32b2e \ + --hash=sha256:4cf0855a842c5b5c391dd32ca273b09e86abf8367572073bd1edfc52bc44446b \ + --hash=sha256:4eca20917a06d2fca7628ef3c8b94a8c358f6b43f1a621c9815243462dcccf97 \ + --hash=sha256:4ed172d0c79f156c1b954e99c03bc2e3033c17efce8dd1a7c781bc4d5793dfac \ + --hash=sha256:5267cfda873ad62591b9332fd9472d2409f7cf02a34a9c9cb367e2c0255994bf \ + --hash=sha256:52b5cbc0469328e58180021138207e6ec91d7ca2e037d3549cc9e34e2187330a \ + --hash=sha256:53d7a3cd46cdc1689296348cb05ffd4f4280035770aee0c8ead3bbd4d6529acc \ + --hash=sha256:563646d74a4b4456d0cf3b714ca522e725243c603e8254ad85c3b59b7c0c4bf0 \ + --hash=sha256:570cc326e78ff23dec7f41487aa9c3dffd02e5ee9ab43a8f6ccc3df8f9327623 \ + --hash=sha256:5aca759ada6b1967fcfd4336dcf460d02a8a23e6abe06e90ea7881e5c22c4de6 \ + --hash=sha256:5de11c041486681ce854c814844f4ce3282b6ea1656faae19208ebe09d31c5b8 \ + --hash=sha256:5e271dd97c7bb8eefda5cca38cd0b0373a1fea50f71e8071376b46968582af9b \ + --hash=sha256:642ed0a209ced4be3a46f8cb094f2d76f1f479e2a1ceca6de6346a096cd3409d \ + --hash=sha256:6446002739ca29249f0beaaf067fcbc2b5aab4bc7ee8fb941bd194947ce19aff \ + --hash=sha256:691d50c99a937709ac4c4cd570d959a006bd6a6d970a484c84cc99543d4a5bbb \ + --hash=sha256:69b857a7d8bd4f5d6e0db4086da8c46309a26e8cefdfc778c0c5cc17d4b11e08 \ + --hash=sha256:6ac3fefb0d168c7c6cab24fdfc80ec62cd2b4dfd9e65b84bdceb1cb01d385c33 \ + --hash=sha256:6c9141af27a4e5819d74d67d227d5047a20fa3c7d4d9df43037a955b4c748ec5 \ + --hash=sha256:7170cbde4070dc3c77dec82abf86f3b210633d4f89550fa0ad2d4b549a05572a \ + --hash=sha256:763ad59e105fca09705d9f9b29ecffb95ecdc3b0363be3bb56081b2c6de7977a \ + --hash=sha256:77076bdc8776a2b029e1e6ffbe6d7056e35f56f5e80d9dc0bad26ad4a024a762 \ + --hash=sha256:7cd020b1fb41e3ab7716d4d2c3972d4588fdfbab9bfbbb64acc7078eccef8860 \ + --hash=sha256:821392559d37759caa67d622d0d2994c7a3f2fb29274948ac799d496d92bca73 \ + --hash=sha256:829e91f3a8574888b73e7a3feb3b1af698e717513597e23136ff4eba0bc8387a \ + --hash=sha256:850c272e0e0d1a5c5d73b1b7871b0a7c2446b304cec55ccdb3eaac0d792bb065 \ + --hash=sha256:87d9b206b1bd7a0523375dc2020a6ce88bca5330682ae2fe25e86fd5d45cea9c \ + --hash=sha256:8bd01ff4032abaed03f2db702fa9a61078bee37add0bd884a6190b05e63b028c \ + --hash=sha256:8d54bbdf5d56e2c8cf81a1857250f3ea132de77af543d0ba5dce667183b61fec \ + --hash=sha256:8efaeb08ede95066da3a3e3c420fcc0a21693fcd0c4396d0585b019613d28515 \ + --hash=sha256:8f94fdd756ba1f79f988855d948ae0bad9ddf44df296770d9a58c774cfbcca72 \ + --hash=sha256:95cde244e7195b2c07ec9b73fa4c5026d4a27233451485caa1cd0c1b55f26dbd \ + --hash=sha256:975382d9aa90dc59253d6a83a5ca72e07f4ada3ae3d6c0575ced513db322b8ec \ + --hash=sha256:9dd9d9d9e898b9d30683bdd2b6c1849449158647d1049a125879cb397ee9cd12 \ + --hash=sha256:a019a344312d0b1f429c00d49c3be62fa273d4a1094e1b224f403716b6d03be1 \ + --hash=sha256:a4d9bfda3f84fc563868fe25ca160c8ff0e69bc4443c5647f960d59400ce6557 \ + --hash=sha256:a657250807b6efd19b28f5922520ae002a54cb43c2401e6f3d0230c352564d25 \ + --hash=sha256:a771417c9c06c56c9d53d11a5b084d1de75de82978e23c544270ab25e7c066ff \ + --hash=sha256:aad6ed9e70ddfb34d849b761fb243be58c735be6a9265b9060d6ddb77751e3e8 \ + --hash=sha256:ae87137951bb3dc08c7d8bfb8988d8c119f3230731b08a71146e84aaa919a7a9 \ + --hash=sha256:af247fd4f12cca4129c1b82090244ea5a9d5bb089e9a82feb5a2f7c6a9fe181d \ + --hash=sha256:b5d4bdd697195f3876d134101c40c7d06d46c6ab25159ed5cbd44105c715278a \ + --hash=sha256:b9255e7165083de7c1d605e818025e8860636348f34a79d84ec533546064f07e \ + --hash=sha256:c22211c165166de6683de8136229721f3d5c8606cc2c3d1562da9a3a5058049c \ + --hash=sha256:c55f9821f88e8bee4b7a72c82cfb5ecd22b6aad04033334f33c329b29bfa4da0 \ + --hash=sha256:c7aed97f2e676561416c927b063802c8a6285e9b55e1b83213dfd99a8f4f9e48 \ + --hash=sha256:cd2163f42868865597d89399a01aa33b7594ce8e2c4a28503127c81a2f17784e \ + --hash=sha256:ce5e7504db95b76fc89055c7f41e367eaadef5b1d059e27e1d6eabf2b55ca314 \ + --hash=sha256:cff7351c251c7546407827b6a37bcef6416304fc54d12d44dbfecbb717064717 \ + --hash=sha256:d27aa6bbc1f33be920bb7adbb95581452cdf23005d5611b29a12bb6a3468cc95 \ + --hash=sha256:d3b52a67ac66a3a64a7e710ba629f62d1e26ca0504c29ee8cbd99b97df7079a8 \ + --hash=sha256:de61e424062173b4f70eec07e12469edde7e17fa180019a2a0d75c13a5c5dc57 \ + --hash=sha256:e10e6a1ed2b8661201e79dff5531f8ad4cdd83548a0f81c95cf79b3184b20c33 \ + --hash=sha256:e1a0ffc39f51aa5f5c22114a8f1906b3c17eba68c5babb86c5f77d8b1bba14d1 \ + --hash=sha256:e22491d25f97199fc3581ad8dd8ce198d8c8fdb8dae80dea3512e1ce6d5fa99f \ + --hash=sha256:e626b864725680cd3904414d72e7b0bd81c0e5b2b53a5b30b4273034253bb41f \ + --hash=sha256:e8c71ea77536149e36c4c784f6d420ffd20bea041e3ba21ed021cb40ce58e2c9 \ + --hash=sha256:e8d0f0eca087630d58b8c662085529781fd5dc80f0a54eda42d5c9029f812599 \ + --hash=sha256:ea65b59882d5fa8c74a23f8960db579e5e341534934f43f3b18ec1839b893e41 \ + --hash=sha256:ea93163472db26ac6043e8f7f93a05d9b59e0505c760da2a3cd22c7dd7111391 \ + --hash=sha256:eab75a8569a095f2ad470b342f2751d9902f7944704f0571c8af46bede438475 \ + --hash=sha256:ed8313809571a5463fd7db43aaca68ecb43ca7a58f5b23b6e6c6c5d02bdc7882 \ + --hash=sha256:ef5fddfb264e89c435be4adb3953cef5d2936fdeb4463b4161a6ba2f22e7b740 \ + --hash=sha256:ef750a20de1b65657a1425f77c525b0183eac63fe7b8f5ac0dd16f3668d3e64f \ + --hash=sha256:efb9ece97e696bb56e31166a9dd7919f8f0c6b31967b454718c6509f29ef6fee \ + --hash=sha256:f4c179a7aeae10ddf44c6bac87938134c1379c49c884529f090f9bf05566c836 \ + --hash=sha256:f602881d80ee4228a2355c68da6b296a296cd22bbb91e5418d54577bbf17fa7c \ + --hash=sha256:fc2200e79d75b5238c8d69f6a30f8284290c777039d331e7340b6c17cad24a5a \ + --hash=sha256:fcc1ebb7561a3e24a6588f7c6ded15d80aec22c66a070c757559b57b17ffd1cb + # via + # jsonschema + # referencing +ruff==0.0.291 \ + --hash=sha256:13f0d88e5f367b2dc8c7d90a8afdcfff9dd7d174e324fd3ed8e0b5cb5dc9b7f6 \ + --hash=sha256:1d5f0616ae4cdc7a938b493b6a1a71c8a47d0300c0d65f6e41c281c2f7490ad3 \ + --hash=sha256:5383ba67ad360caf6060d09012f1fb2ab8bd605ab766d10ca4427a28ab106e0b \ + --hash=sha256:6ab44ea607967171e18aa5c80335237be12f3a1523375fa0cede83c5cf77feb4 \ + --hash=sha256:6c06006350c3bb689765d71f810128c9cdf4a1121fd01afc655c87bab4fb4f83 \ + --hash=sha256:87671e33175ae949702774071b35ed4937da06f11851af75cd087e1b5a488ac4 \ + --hash=sha256:8a69bfbde72db8ca1c43ee3570f59daad155196c3fbe357047cd9b77de65f15b \ + --hash=sha256:8d5b56bc3a2f83a7a1d7f4447c54d8d3db52021f726fdd55d549ca87bca5d747 \ + --hash=sha256:a04b384f2d36f00d5fb55313d52a7d66236531195ef08157a09c4728090f2ef0 \ + --hash=sha256:b09b94efdcd162fe32b472b2dd5bf1c969fcc15b8ff52f478b048f41d4590e09 \ + --hash=sha256:b3eeee1b1a45a247758ecdc3ab26c307336d157aafc61edb98b825cadb153df3 \ + --hash=sha256:b727c219b43f903875b7503a76c86237a00d1a39579bb3e21ce027eec9534051 \ + --hash=sha256:b75f5801547f79b7541d72a211949754c21dc0705c70eddf7f21c88a64de8b97 \ + --hash=sha256:b97d0d7c136a85badbc7fd8397fdbb336e9409b01c07027622f28dcd7db366f2 \ + --hash=sha256:c61109661dde9db73469d14a82b42a88c7164f731e6a3b0042e71394c1c7ceed \ + --hash=sha256:d867384a4615b7f30b223a849b52104214442b5ba79b473d7edd18da3cde22d6 \ + --hash=sha256:fd17220611047de247b635596e3174f3d7f2becf63bd56301fc758778df9b629 + # via -r /workspace/requirements-dev-common.in +s3transfer==0.7.0 \ + --hash=sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a \ + --hash=sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e + # via boto3 +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via + # dependency-injector + # pytest-profiling + # python-dateutil +snakeviz==2.2.0 \ + --hash=sha256:569e2d71c47f80a886aa6e70d6405cb6d30aa3520969ad956b06f824c5f02b8e \ + --hash=sha256:7bfd00be7ae147eb4a170a471578e1cd3f41f803238958b6b8efcf2c698a6aa9 + # via -r /workspace/requirements-dev-common.in +sniffio==1.3.0 \ + --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \ + --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384 + # via + # anyio + # httpcore + # httpx +stringcase==1.2.0 \ + --hash=sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008 + # via -r /workspace/requirements-dev-common.in +testcontainers[kafka,mongo]==3.7.1 \ + --hash=sha256:7f48cef4bf0ccd78f1a4534d4b701a003a3bace851f24eae58a32f9e3f0aeba0 + # via hexkit (pyproject.toml) +tomli==2.0.1 \ + --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ + --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f + # via + # -r /workspace/requirements-dev-common.in + # black + # build + # coverage + # mypy + # pip-tools + # pyproject-hooks + # pytest +tomli-w==1.0.0 \ + --hash=sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463 \ + --hash=sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9 + # via -r /workspace/requirements-dev-common.in +tornado==6.3.3 \ + --hash=sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f \ + --hash=sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5 \ + --hash=sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d \ + --hash=sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3 \ + --hash=sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2 \ + --hash=sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a \ + --hash=sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16 \ + --hash=sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a \ + --hash=sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17 \ + --hash=sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0 \ + --hash=sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe + # via snakeviz +typer==0.9.0 \ + --hash=sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2 \ + --hash=sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee + # via -r /workspace/requirements-dev-common.in +typing-extensions==4.8.0 \ + --hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \ + --hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef + # via + # black + # mypy + # pydantic + # typer +urllib3==1.26.16 \ + --hash=sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f \ + --hash=sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14 + # via + # -r /workspace/requirements-dev-common.in + # botocore + # docker + # requests +virtualenv==20.24.5 \ + --hash=sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b \ + --hash=sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752 + # via pre-commit +websocket-client==1.6.3 \ + --hash=sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f \ + --hash=sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03 + # via docker +wheel==0.41.2 \ + --hash=sha256:0c5ac5ff2afb79ac23ab82bab027a0be7b5dbcf2e54dc50efe4bf507de1f7985 \ + --hash=sha256:75909db2664838d015e3d9139004ee16711748a52c8f336b52882266540215d8 + # via pip-tools +wrapt==1.15.0 \ + --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \ + --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \ + --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \ + --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \ + --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \ + --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \ + --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \ + --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \ + --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \ + --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \ + --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \ + --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \ + --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \ + --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \ + --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \ + --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \ + --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \ + --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \ + --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \ + --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \ + --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \ + --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \ + --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \ + --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \ + --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \ + --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \ + --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \ + --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \ + --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \ + --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \ + --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \ + --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \ + --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \ + --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \ + --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \ + --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \ + --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \ + --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \ + --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \ + --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \ + --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \ + --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \ + --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \ + --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \ + --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \ + --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \ + --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \ + --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \ + --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \ + --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \ + --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \ + --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \ + --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \ + --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \ + --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \ + --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \ + --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \ + --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \ + --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \ + --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \ + --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \ + --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \ + --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \ + --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \ + --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \ + --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \ + --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \ + --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \ + --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \ + --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \ + --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \ + --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \ + --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \ + --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \ + --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639 + # via testcontainers +zipp==3.17.0 \ + --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \ + --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0 + # via importlib-metadata -# common requirements for development and testing --r requirements-dev-common.txt - -# additional requirements for testing hexkit providers -.[test-all] +# WARNING: The following packages were not pinned, but pip requires them to be +# pinned when the requirements file includes hashes and the requirement is not +# satisfied by a package already installed. Consider using the --allow-unsafe flag. +# pip +# setuptools diff --git a/requirements.txt b/requirements.txt index 1783b07c..19f54dea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,703 @@ -# requirements for complete installation - -# the ".[all]" states that requirements including the "all" extra are taken from setup.cfg -.[all] +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --all-extras --constraint=/workspace/requirements-dev.txt --generate-hashes --output-file=/workspace/requirements.txt /tmp/tmpsekdwsju/pyproject.toml +# +aiokafka==0.8.1 \ + --hash=sha256:1e24839088fd6d3ff481cc09a48ea487b997328df11630bc0a1b88255edbcfe9 \ + --hash=sha256:1f43d2afd7d3e4407ada8d754895fad7c344ca00648a8a38418d76564eaaf6cd \ + --hash=sha256:1f6044ed270b946d31f265903b5eb101940ed0ff3a902eaf8178103c943bbcc9 \ + --hash=sha256:24373bb2d519abac036d5b04ebc43452ef4ad1916953b6678b9801a9c93ba237 \ + --hash=sha256:2fa54b8b068d9d8735cb6757a0f48168f8cf9be68860b0bae6b3ed1684cef49b \ + --hash=sha256:3816bcfc3c57dfa4ed77fe1dc3a9a464e17b6400061348155115f282c8150c47 \ + --hash=sha256:45cd28af6590d6a999bb706803166570121ba8a5a0d06c51ebd8a59fab53593c \ + --hash=sha256:4693fbe3c10f125bf3e2df8a8ccbca3eff2bdaaa6589d28c7532c10e7d84598b \ + --hash=sha256:4fccd599ab6b3fda4f4187d854b343f153b40d05d6774be9acf238618da50031 \ + --hash=sha256:6421ee81084532f915501074a132acb2afc8cb88bf5ddb11e584230a30f6f006 \ + --hash=sha256:673c163dee62dfe45146d5250af0e395da5cc92b63f8878c592abc7dc1862899 \ + --hash=sha256:7d327d66b41c4e3bafff7f9efb71936a08f940aa665680717e20862e4272a068 \ + --hash=sha256:7f09784322c0d2c4fcc222add4337a5ac394aa30a248eb4e0e4587a125573c75 \ + --hash=sha256:90960356513f3979754261b132b12a96b0d9e3c6eb44420e3a90a7c31156a81a \ + --hash=sha256:935da8c4da9a00a1e16020d88e578206097b4bb72ebc2a25fbd2cb817907ef28 \ + --hash=sha256:9f19d90b7360bc2239fcd8b147508ae39c3e5b1acfc8e6a2a9b0f306070f7ffe \ + --hash=sha256:a8a641a8102c51422afe111d4bc70c51f335f38fc5906e4c839bd17afeaf3cb2 \ + --hash=sha256:af6df9a41e08b61d7e62c0a416feeabd81bad76fa5c70d499b083d6af9ce72c3 \ + --hash=sha256:b2bf97548fa77ad31062ca580368d346b16ba9fdca5856c435f256f3699ab12b \ + --hash=sha256:bbffc431d9285328c0bc108949132ae11cec863f1dd5a43a1fc3d45a69ffb8a9 \ + --hash=sha256:bf7473c55dc7959d4b7f9d750fa6017b325813d6cb761e488c2d9ea44e922954 \ + --hash=sha256:c4332d37cb9d52181cfda4236566b4028c7c188549277f87bcc3027577d72b1b \ + --hash=sha256:d300188e358cd29989c817f6ee2a2965a039e5a71de8ade6f80f02ebb9bd07b8 \ + --hash=sha256:fd8f9e17bc9cd2ea664a7f5133aede39a8fffebffe0c450252d475dbdedb4a35 \ + --hash=sha256:ff318d29ecbeea8c58d69c91c24d48d7ed4a8d3e829b607e670d118a9a35d5ba + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +async-timeout==4.0.3 \ + --hash=sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f \ + --hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028 + # via + # -c /workspace/requirements-dev.txt + # aiokafka +attrs==23.1.0 \ + --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ + --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015 + # via + # -c /workspace/requirements-dev.txt + # jsonschema + # referencing +boto3==1.28.56 \ + --hash=sha256:b927a7ed335d543c33c15fa63f1076f3fa8422959771c2187da74bc4395ab6e3 \ + --hash=sha256:f5fcb27cdbd08ca38d699f2d2e32d96d1d9fab3368c15c6bc326256612d2cfd7 + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +botocore==1.31.56 \ + --hash=sha256:66c686e4eda7051ffcc9357d9075390c8ab2f95a2977669039618ee186fb533b \ + --hash=sha256:70252cd8abc2fe9b791328e187620f5a3911545e2520486b01ecfad31f41b9cb + # via + # -c /workspace/requirements-dev.txt + # boto3 + # hexkit (pyproject.toml) + # s3transfer +certifi==2023.7.22 \ + --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ + --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 + # via + # -c /workspace/requirements-dev.txt + # requests +charset-normalizer==3.2.0 \ + --hash=sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96 \ + --hash=sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c \ + --hash=sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710 \ + --hash=sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706 \ + --hash=sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020 \ + --hash=sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252 \ + --hash=sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad \ + --hash=sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329 \ + --hash=sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a \ + --hash=sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f \ + --hash=sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6 \ + --hash=sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4 \ + --hash=sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a \ + --hash=sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46 \ + --hash=sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2 \ + --hash=sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23 \ + --hash=sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace \ + --hash=sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd \ + --hash=sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982 \ + --hash=sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10 \ + --hash=sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2 \ + --hash=sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea \ + --hash=sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09 \ + --hash=sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5 \ + --hash=sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149 \ + --hash=sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489 \ + --hash=sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9 \ + --hash=sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80 \ + --hash=sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592 \ + --hash=sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3 \ + --hash=sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6 \ + --hash=sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed \ + --hash=sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c \ + --hash=sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200 \ + --hash=sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a \ + --hash=sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e \ + --hash=sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d \ + --hash=sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6 \ + --hash=sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623 \ + --hash=sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669 \ + --hash=sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3 \ + --hash=sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa \ + --hash=sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9 \ + --hash=sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2 \ + --hash=sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f \ + --hash=sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1 \ + --hash=sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4 \ + --hash=sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a \ + --hash=sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8 \ + --hash=sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3 \ + --hash=sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029 \ + --hash=sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f \ + --hash=sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959 \ + --hash=sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22 \ + --hash=sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7 \ + --hash=sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952 \ + --hash=sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346 \ + --hash=sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e \ + --hash=sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d \ + --hash=sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299 \ + --hash=sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd \ + --hash=sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a \ + --hash=sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3 \ + --hash=sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037 \ + --hash=sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94 \ + --hash=sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c \ + --hash=sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858 \ + --hash=sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a \ + --hash=sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449 \ + --hash=sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c \ + --hash=sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918 \ + --hash=sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1 \ + --hash=sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c \ + --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \ + --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa + # via + # -c /workspace/requirements-dev.txt + # requests +dependency-injector==4.41.0 \ + --hash=sha256:02620454ee8101f77a317f3229935ce687480883d72a40858ff4b0c87c935cce \ + --hash=sha256:059fbb48333148143e8667a5323d162628dfe27c386bd0ed3deeecfc390338bf \ + --hash=sha256:05e15ea0f2b14c1127e8b0d1597fef13f98845679f63bf670ba12dbfc12a16ef \ + --hash=sha256:12e91ac0333e7e589421943ff6c6bf9cf0d9ac9703301cec37ccff3723406332 \ + --hash=sha256:1662e2ef60ac6e681b9e11b5d8b7c17a0f733688916cf695f9540f8f50a61b1e \ + --hash=sha256:168334cba3f1cbf55299ef38f0f2e31879115cc767b780c859f7814a52d80abb \ + --hash=sha256:16de2797dcfcc2263b8672bf0751166f7c7b369ca2ff9246ceb67b65f8e1d802 \ + --hash=sha256:1baee908f21190bdc46a65ce4c417a5175e9397ca62354928694fce218f84487 \ + --hash=sha256:22b11dbf696e184f0b3d5ac4e5418aeac3c379ba4ea758c04a83869b7e5d1cbf \ + --hash=sha256:300838e9d4f3fbf539892a5a4072851728e23b37a1f467afcf393edd994d88f0 \ + --hash=sha256:3055b3fc47a0d6e5f27defb4166c0d37543a4967c279549b154afaf506ce6efc \ + --hash=sha256:33a724e0a737baadb4378f5dc1b079867cc3a88552fcca719b3dba84716828b2 \ + --hash=sha256:3535d06416251715b45f8412482b58ec1c6196a4a3baa207f947f0b03a7c4b44 \ + --hash=sha256:3588bd887b051d16b8bcabaae1127eb14059a0719a8fe34c8a75ba59321b352c \ + --hash=sha256:3744c327d18408e74781bd6d8b7738745ee80ef89f2c8daecf9ebd098cb84972 \ + --hash=sha256:37d5954026e3831663518d78bdf4be9c2dbfea691edcb73c813aa3093aa4363a \ + --hash=sha256:40936d9384363331910abd59dd244158ec3572abf9d37322f15095315ac99893 \ + --hash=sha256:409441122f40e1b4b8582845fdd76deb9dc5c9d6eb74a057b85736ef9e9c671f \ + --hash=sha256:48b6886a87b4ceb9b9f78550f77b2a5c7d2ce33bc83efd886556ad468cc9c85a \ + --hash=sha256:4a31d9d60be4b585585081109480cfb2ef564d3b851cb32a139bf8408411a93a \ + --hash=sha256:4a44ca3ce5867513a70b31855b218be3d251f5068ce1c480cc3a4ad24ffd3280 \ + --hash=sha256:51217cb384b468d7cc355544cec20774859f00812f9a1a71ed7fa701c957b2a7 \ + --hash=sha256:5168dc59808317dc4cdd235aa5d7d556d33e5600156acaf224cead236b48a3e8 \ + --hash=sha256:54032d62610cf2f4421c9d92cef52957215aaa0bca403cda580c58eb3f726eda \ + --hash=sha256:56d37b9d2f50a18f059d9abdbea7669a7518bd42b81603c21a27910a2b3f1657 \ + --hash=sha256:586a0821720b15932addbefb00f7370fbcd5831d6ebbd6494d774b44ff96d23a \ + --hash=sha256:5fa3ed8f0700e47a0e7363f949b4525ffa8277aa1c5b10ca5b41fce4dea61bb9 \ + --hash=sha256:63bfba21f8bff654a80e9b9d06dd6c43a442990b73bf89cd471314c11c541ec2 \ + --hash=sha256:67b369592c57549ccdcad0d5fef1ddb9d39af7fed8083d76e789ab0111fc6389 \ + --hash=sha256:6b29abac56ce347d2eb58a560723e1663ee2125cf5cc38866ed92b84319927ec \ + --hash=sha256:6b98945edae88e777091bf0848f869fb94bd76dfa4066d7c870a5caa933391d0 \ + --hash=sha256:6ee9810841c6e0599356cb884d16453bfca6ab739d0e4f0248724ed8f9ee0d79 \ + --hash=sha256:740a8e8106a04d3f44b52b25b80570fdac96a8a3934423de7c9202c5623e7936 \ + --hash=sha256:75280dfa23f7c88e1bf56c3920d58a43516816de6f6ab2a6650bb8a0f27d5c2c \ + --hash=sha256:75e7a733b372db3144a34020c4233f6b94db2c6342d6d16bc5245b1b941ee2bd \ + --hash=sha256:76b94c8310929e54136f3cb3de3adc86d1a657b3984299f40bf1cd2ba0bae548 \ + --hash=sha256:786f7aac592e191c9caafc47732161d807bad65c62f260cd84cd73c7e2d67d6d \ + --hash=sha256:7a92680bea1c260e5c0d2d6cd60b0c913cba76a456a147db5ac047ecfcfcc758 \ + --hash=sha256:7dcba8665cafec825b7095d5dd80afb5cf14404450eca3fe8b66e1edbf4dbc10 \ + --hash=sha256:7fa4970f12a3fc95d8796938b11c41276ad1ff4c447b0e589212eab3fc527a90 \ + --hash=sha256:87be84084a1b922c4ba15e2e5aa900ee24b78a5467997cb7aec0a1d6cdb4a00b \ + --hash=sha256:89c67edffe7007cf33cee79ecbca38f48efcc2add5c280717af434db6c789377 \ + --hash=sha256:8b51efeaebacaf79ef68edfc65e9687699ccffb3538c4a3ab30d0d77e2db7189 \ + --hash=sha256:8b8cf1c6c56f5c18bdbd9f5e93b52ca29cb4d99606d4056e91f0c761eef496dc \ + --hash=sha256:8d670a844268dcd758195e58e9a5b39fc74bb8648aba99a13135a4a10ec9cfac \ + --hash=sha256:8f0090ff14038f17a026ca408a3a0b0e7affb6aa7498b2b59d670f40ac970fbe \ + --hash=sha256:939dfc657104bc3e66b67afd3fb2ebb0850c9a1e73d0d26066f2bbdd8735ff9c \ + --hash=sha256:953bfac819d32dc72b963767589e0ed372e5e9e78b03fb6b89419d0500d34bbe \ + --hash=sha256:99ed73b1521bf249e2823a08a730c9f9413a58f4b4290da022e0ad4fb333ba3d \ + --hash=sha256:9e3b9d41e0eff4c8e16fea1e33de66ff0030fe51137ca530f3c52ce110447914 \ + --hash=sha256:a2381a251b04244125148298212550750e6e1403e9b2850cc62e0e829d050ad3 \ + --hash=sha256:a2dee5d4abdd21f1a30a51d46645c095be9dcc404c7c6e9f81d0a01415a49e64 \ + --hash=sha256:a4f113e5d4c3070973ad76e5bda7317e500abae6083d78689f0b6e37cf403abf \ + --hash=sha256:a8686fa330c83251c75c8238697686f7a0e0f6d40658538089165dc72df9bcff \ + --hash=sha256:ac79f3c05747f9724bd56c06985e78331fc6c85eb50f3e3f1a35e0c60f9977e9 \ + --hash=sha256:b0c9c966ff66c77364a2d43d08de9968aff7e3903938fe912ba49796b2133344 \ + --hash=sha256:b2440b32474d4e747209528ca3ae48f42563b2fbe3d74dbfe949c11dfbfef7c4 \ + --hash=sha256:b365a8548e9a49049fa6acb24d3cd939f619eeb8e300ca3e156e44402dcc07ec \ + --hash=sha256:b37f36ecb0c1227f697e1d4a029644e3eda8dd0f0716aa63ad04d96dbb15bbbb \ + --hash=sha256:b3890a12423ae3a9eade035093beba487f8d092ee6c6cb8706f4e7080a56e819 \ + --hash=sha256:b8b61a15bc46a3aa7b29bd8a7384b650aa3a7ef943491e93c49a0540a0b3dda4 \ + --hash=sha256:bc852da612c7e347f2fcf921df2eca2718697a49f648a28a63db3ab504fd9510 \ + --hash=sha256:c71d30b6708438050675f338edb9a25bea6c258478dbe5ec8405286756a2d347 \ + --hash=sha256:d03f5fa0fa98a18bd0dfce846db80e2798607f0b861f1f99c97f441f7669d7a2 \ + --hash=sha256:d09c08c944a25dabfb454238c1a889acd85102b93ae497de523bf9ab7947b28a \ + --hash=sha256:d283aee588a72072439e6721cb64aa6cba5bc18c576ef0ab28285a6ec7a9d655 \ + --hash=sha256:d557e40673de984f78dab13ebd68d27fbb2f16d7c4e3b663ea2fa2f9fae6765b \ + --hash=sha256:e3229d83e99e255451605d5276604386e06ad948e3d60f31ddd796781c77f76f \ + --hash=sha256:f2842e15bae664a9f69932e922b02afa055c91efec959cb1896f6c499bf68180 \ + --hash=sha256:f89a507e389b7e4d4892dd9a6f5f4da25849e24f73275478634ac594d621ab3f + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +deprecation==2.1.0 \ + --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ + --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a + # via + # -c /workspace/requirements-dev.txt + # testcontainers +dnspython==2.4.2 \ + --hash=sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8 \ + --hash=sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984 + # via + # -c /workspace/requirements-dev.txt + # pymongo +docker==6.1.3 \ + --hash=sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20 \ + --hash=sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9 + # via + # -c /workspace/requirements-dev.txt + # testcontainers +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via + # -c /workspace/requirements-dev.txt + # requests +jmespath==1.0.1 \ + --hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \ + --hash=sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe + # via + # -c /workspace/requirements-dev.txt + # boto3 + # botocore +jsonschema==4.19.1 \ + --hash=sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e \ + --hash=sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +jsonschema-specifications==2023.7.1 \ + --hash=sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1 \ + --hash=sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb + # via + # -c /workspace/requirements-dev.txt + # jsonschema +kafka-python==2.0.2 \ + --hash=sha256:04dfe7fea2b63726cd6f3e79a2d86e709d608d74406638c5da33a01d45a9d7e3 \ + --hash=sha256:2d92418c7cb1c298fa6c7f0fb3519b520d0d7526ac6cb7ae2a4fc65a51a94b6e + # via + # -c /workspace/requirements-dev.txt + # aiokafka + # testcontainers +motor==3.3.1 \ + --hash=sha256:a0dee83ad0d47b353932ac37467ba397b1e649ce7e3eea7f5a90554883d7cdbe \ + --hash=sha256:c5eb400e27d722a3db03a9826656b6d13acf9b6c70c2fb4604f474eac9da5be4 + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +packaging==23.1 \ + --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ + --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f + # via + # -c /workspace/requirements-dev.txt + # aiokafka + # deprecation + # docker +pydantic==1.10.13 \ + --hash=sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548 \ + --hash=sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80 \ + --hash=sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340 \ + --hash=sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01 \ + --hash=sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132 \ + --hash=sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599 \ + --hash=sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1 \ + --hash=sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8 \ + --hash=sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe \ + --hash=sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0 \ + --hash=sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17 \ + --hash=sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953 \ + --hash=sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f \ + --hash=sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f \ + --hash=sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d \ + --hash=sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127 \ + --hash=sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8 \ + --hash=sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f \ + --hash=sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580 \ + --hash=sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6 \ + --hash=sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691 \ + --hash=sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87 \ + --hash=sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd \ + --hash=sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96 \ + --hash=sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687 \ + --hash=sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33 \ + --hash=sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69 \ + --hash=sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653 \ + --hash=sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78 \ + --hash=sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261 \ + --hash=sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f \ + --hash=sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9 \ + --hash=sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d \ + --hash=sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737 \ + --hash=sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5 \ + --hash=sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0 + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +pymongo==4.5.0 \ + --hash=sha256:076afa0a4a96ca9f77fec0e4a0d241200b3b3a1766f8d7be9a905ecf59a7416b \ + --hash=sha256:08819da7864f9b8d4a95729b2bea5fffed08b63d3b9c15b4fea47de655766cf5 \ + --hash=sha256:0a1f26bc1f5ce774d99725773901820dfdfd24e875028da4a0252a5b48dcab5c \ + --hash=sha256:0f4b125b46fe377984fbaecf2af40ed48b05a4b7676a2ff98999f2016d66b3ec \ + --hash=sha256:1240edc1a448d4ada4bf1a0e55550b6292420915292408e59159fd8bbdaf8f63 \ + --hash=sha256:152259f0f1a60f560323aacf463a3642a65a25557683f49cfa08c8f1ecb2395a \ + --hash=sha256:168172ef7856e20ec024fe2a746bfa895c88b32720138e6438fd765ebd2b62dd \ + --hash=sha256:1b1d7d9aabd8629a31d63cd106d56cca0e6420f38e50563278b520f385c0d86e \ + --hash=sha256:1d40ad09d9f5e719bc6f729cc6b17f31c0b055029719406bd31dde2f72fca7e7 \ + --hash=sha256:21b953da14549ff62ea4ae20889c71564328958cbdf880c64a92a48dda4c9c53 \ + --hash=sha256:23cc6d7eb009c688d70da186b8f362d61d5dd1a2c14a45b890bd1e91e9c451f2 \ + --hash=sha256:2988ef5e6b360b3ff1c6d55c53515499de5f48df31afd9f785d788cdacfbe2d3 \ + --hash=sha256:2a0aade2b11dc0c326ccd429ee4134d2d47459ff68d449c6d7e01e74651bd255 \ + --hash=sha256:2b0176f9233a5927084c79ff80b51bd70bfd57e4f3d564f50f80238e797f0c8a \ + --hash=sha256:2d4fa1b01fa7e5b7bb8d312e3542e211b320eb7a4e3d8dc884327039d93cb9e0 \ + --hash=sha256:3236cf89d69679eaeb9119c840f5c7eb388a2110b57af6bb6baf01a1da387c18 \ + --hash=sha256:33faa786cc907de63f745f587e9879429b46033d7d97a7b84b37f4f8f47b9b32 \ + --hash=sha256:37df8f6006286a5896d1cbc3efb8471ced42e3568d38e6cb00857277047b0d63 \ + --hash=sha256:3a7166d57dc74d679caa7743b8ecf7dc3a1235a9fd178654dddb2b2a627ae229 \ + --hash=sha256:3d79ae3bb1ff041c0db56f138c88ce1dfb0209f3546d8d6e7c3f74944ecd2439 \ + --hash=sha256:3e33064f1984db412b34d51496f4ea785a9cff621c67de58e09fb28da6468a52 \ + --hash=sha256:3fa3648e4f1e63ddfe53563ee111079ea3ab35c3b09cd25bc22dadc8269a495f \ + --hash=sha256:40d5f6e853ece9bfc01e9129b228df446f49316a4252bb1fbfae5c3c9dedebad \ + --hash=sha256:41771b22dd2822540f79a877c391283d4e6368125999a5ec8beee1ce566f3f82 \ + --hash=sha256:435228d3c16a375274ac8ab9c4f9aef40c5e57ddb8296e20ecec9e2461da1017 \ + --hash=sha256:44ee985194c426ddf781fa784f31ffa29cb59657b2dba09250a4245431847d73 \ + --hash=sha256:465fd5b040206f8bce7016b01d7e7f79d2fcd7c2b8e41791be9632a9df1b4999 \ + --hash=sha256:496c9cbcb4951183d4503a9d7d2c1e3694aab1304262f831d5e1917e60386036 \ + --hash=sha256:49dce6957598975d8b8d506329d2a3a6c4aee911fa4bbcf5e52ffc6897122950 \ + --hash=sha256:4c42748ccc451dfcd9cef6c5447a7ab727351fd9747ad431db5ebb18a9b78a4d \ + --hash=sha256:505f8519c4c782a61d94a17b0da50be639ec462128fbd10ab0a34889218fdee3 \ + --hash=sha256:53f2dda54d76a98b43a410498bd12f6034b2a14b6844ca08513733b2b20b7ad8 \ + --hash=sha256:56320c401f544d762fc35766936178fbceb1d9261cd7b24fbfbc8fb6f67aa8a5 \ + --hash=sha256:58a63a26a1e3dc481dd3a18d6d9f8bd1d576cd1ffe0d479ba7dd38b0aeb20066 \ + --hash=sha256:5caee7bd08c3d36ec54617832b44985bd70c4cbd77c5b313de6f7fce0bb34f93 \ + --hash=sha256:631492573a1bef2f74f9ac0f9d84e0ce422c251644cd81207530af4aa2ee1980 \ + --hash=sha256:63d8019eee119df308a075b8a7bdb06d4720bf791e2b73d5ab0e7473c115d79c \ + --hash=sha256:6422b6763b016f2ef2beedded0e546d6aa6ba87910f9244d86e0ac7690f75c96 \ + --hash=sha256:681f252e43b3ef054ca9161635f81b730f4d8cadd28b3f2b2004f5a72f853982 \ + --hash=sha256:6d64878d1659d2a5bdfd0f0a4d79bafe68653c573681495e424ab40d7b6d6d41 \ + --hash=sha256:74c0da07c04d0781490b2915e7514b1adb265ef22af039a947988c331ee7455b \ + --hash=sha256:7591a3beea6a9a4fa3080d27d193b41f631130e3ffa76b88c9ccea123f26dc59 \ + --hash=sha256:76a262c41c1a7cbb84a3b11976578a7eb8e788c4b7bfbd15c005fb6ca88e6e50 \ + --hash=sha256:77cfff95c1fafd09e940b3fdcb7b65f11442662fad611d0e69b4dd5d17a81c60 \ + --hash=sha256:8027c9063579083746147cf401a7072a9fb6829678076cd3deff28bb0e0f50c8 \ + --hash=sha256:80a167081c75cf66b32f30e2f1eaee9365af935a86dbd76788169911bed9b5d5 \ + --hash=sha256:840eaf30ccac122df260b6005f9dfae4ac287c498ee91e3e90c56781614ca238 \ + --hash=sha256:8543253adfaa0b802bfa88386db1009c6ebb7d5684d093ee4edc725007553d21 \ + --hash=sha256:89b3f2da57a27913d15d2a07d58482f33d0a5b28abd20b8e643ab4d625e36257 \ + --hash=sha256:8e559116e4128630ad3b7e788e2e5da81cbc2344dee246af44471fa650486a70 \ + --hash=sha256:9aff6279e405dc953eeb540ab061e72c03cf38119613fce183a8e94f31be608f \ + --hash=sha256:9c04b9560872fa9a91251030c488e0a73bce9321a70f991f830c72b3f8115d0d \ + --hash=sha256:9d2346b00af524757576cc2406414562cced1d4349c92166a0ee377a2a483a80 \ + --hash=sha256:a253b765b7cbc4209f1d8ee16c7287c4268d3243070bf72d7eec5aa9dfe2a2c2 \ + --hash=sha256:a8127437ebc196a6f5e8fddd746bd0903a400dc6b5ae35df672dd1ccc7170a2a \ + --hash=sha256:b25f7bea162b3dbec6d33c522097ef81df7c19a9300722fa6853f5b495aecb77 \ + --hash=sha256:b33c17d9e694b66d7e96977e9e56df19d662031483efe121a24772a44ccbbc7e \ + --hash=sha256:b4fe46b58010115514b842c669a0ed9b6a342017b15905653a5b1724ab80917f \ + --hash=sha256:b520aafc6cb148bac09ccf532f52cbd31d83acf4d3e5070d84efe3c019a1adbf \ + --hash=sha256:b5bbb87fa0511bd313d9a2c90294c88db837667c2bda2ea3fa7a35b59fd93b1f \ + --hash=sha256:b6d2a56fc2354bb6378f3634402eec788a8f3facf0b3e7d468db5f2b5a78d763 \ + --hash=sha256:bbd705d5f3c3d1ff2d169e418bb789ff07ab3c70d567cc6ba6b72b04b9143481 \ + --hash=sha256:bc5d8c3647b8ae28e4312f1492b8f29deebd31479cd3abaa989090fb1d66db83 \ + --hash=sha256:c3c3525ea8658ee1192cdddf5faf99b07ebe1eeaa61bf32821126df6d1b8072b \ + --hash=sha256:c9a9a39b7cac81dca79fca8c2a6479ef4c7b1aab95fad7544cc0e8fd943595a2 \ + --hash=sha256:cd4c8d6aa91d3e35016847cbe8d73106e3d1c9a4e6578d38e2c346bfe8edb3ca \ + --hash=sha256:cf62da7a4cdec9a4b2981fcbd5e08053edffccf20e845c0b6ec1e77eb7fab61d \ + --hash=sha256:d67225f05f6ea27c8dc57f3fa6397c96d09c42af69d46629f71e82e66d33fa4f \ + --hash=sha256:dfcd2b9f510411de615ccedd47462dae80e82fdc09fe9ab0f0f32f11cf57eeb5 \ + --hash=sha256:e1f61355c821e870fb4c17cdb318669cfbcf245a291ce5053b41140870c3e5cc \ + --hash=sha256:e249190b018d63c901678053b4a43e797ca78b93fb6d17633e3567d4b3ec6107 \ + --hash=sha256:e2654d1278384cff75952682d17c718ecc1ad1d6227bb0068fd826ba47d426a5 \ + --hash=sha256:e57d859b972c75ee44ea2ef4758f12821243e99de814030f69a3decb2aa86807 \ + --hash=sha256:e5a27f348909235a106a3903fc8e70f573d89b41d723a500869c6569a391cff7 \ + --hash=sha256:ead4f19d0257a756b21ac2e0e85a37a7245ddec36d3b6008d5bfe416525967dc \ + --hash=sha256:f076b779aa3dc179aa3ed861be063a313ed4e48ae9f6a8370a9b1295d4502111 \ + --hash=sha256:f1bb3a62395ffe835dbef3a1cbff48fbcce709c78bd1f52e896aee990928432b \ + --hash=sha256:f2227a08b091bd41df5aadee0a5037673f691e2aa000e1968b1ea2342afc6880 \ + --hash=sha256:f3754acbd7efc7f1b529039fcffc092a15e1cf045e31f22f6c9c5950c613ec4d \ + --hash=sha256:fe48f50fb6348511a3268a893bfd4ab5f263f5ac220782449d03cd05964d1ae7 \ + --hash=sha256:fff7d17d30b2cd45afd654b3fc117755c5d84506ed25fda386494e4e0a3416e1 + # via + # -c /workspace/requirements-dev.txt + # motor + # testcontainers +python-dateutil==2.8.2 \ + --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ + --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 + # via + # -c /workspace/requirements-dev.txt + # botocore +pyyaml==6.0.1 \ + --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ + --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ + --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ + --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ + --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ + --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ + --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ + --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ + --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ + --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ + --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ + --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ + --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ + --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ + --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ + --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ + --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ + --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ + --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ + --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ + --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ + --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ + --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ + --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ + --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ + --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ + --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ + --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ + --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ + --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ + --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ + --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ + --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ + --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ + --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ + --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ + --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ + --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ + --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ + --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ + --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ + --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ + --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ + --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ + --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ + --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ + --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ + --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ + --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ + --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +referencing==0.30.2 \ + --hash=sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf \ + --hash=sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0 + # via + # -c /workspace/requirements-dev.txt + # jsonschema + # jsonschema-specifications +requests==2.31.0 \ + --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ + --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 + # via + # -c /workspace/requirements-dev.txt + # docker +rpds-py==0.10.3 \ + --hash=sha256:015de2ce2af1586ff5dc873e804434185199a15f7d96920ce67e50604592cae9 \ + --hash=sha256:061c3ff1f51ecec256e916cf71cc01f9975af8fb3af9b94d3c0cc8702cfea637 \ + --hash=sha256:08a80cf4884920863623a9ee9a285ee04cef57ebedc1cc87b3e3e0f24c8acfe5 \ + --hash=sha256:09362f86ec201288d5687d1dc476b07bf39c08478cde837cb710b302864e7ec9 \ + --hash=sha256:0bb4f48bd0dd18eebe826395e6a48b7331291078a879295bae4e5d053be50d4c \ + --hash=sha256:106af1653007cc569d5fbb5f08c6648a49fe4de74c2df814e234e282ebc06957 \ + --hash=sha256:11fdd1192240dda8d6c5d18a06146e9045cb7e3ba7c06de6973000ff035df7c6 \ + --hash=sha256:16a472300bc6c83fe4c2072cc22b3972f90d718d56f241adabc7ae509f53f154 \ + --hash=sha256:176287bb998fd1e9846a9b666e240e58f8d3373e3bf87e7642f15af5405187b8 \ + --hash=sha256:177914f81f66c86c012311f8c7f46887ec375cfcfd2a2f28233a3053ac93a569 \ + --hash=sha256:177c9dd834cdf4dc39c27436ade6fdf9fe81484758885f2d616d5d03c0a83bd2 \ + --hash=sha256:187700668c018a7e76e89424b7c1042f317c8df9161f00c0c903c82b0a8cac5c \ + --hash=sha256:1d9b5ee46dcb498fa3e46d4dfabcb531e1f2e76b477e0d99ef114f17bbd38453 \ + --hash=sha256:22da15b902f9f8e267020d1c8bcfc4831ca646fecb60254f7bc71763569f56b1 \ + --hash=sha256:24cd91a03543a0f8d09cb18d1cb27df80a84b5553d2bd94cba5979ef6af5c6e7 \ + --hash=sha256:255f1a10ae39b52122cce26ce0781f7a616f502feecce9e616976f6a87992d6b \ + --hash=sha256:271c360fdc464fe6a75f13ea0c08ddf71a321f4c55fc20a3fe62ea3ef09df7d9 \ + --hash=sha256:2ed83d53a8c5902ec48b90b2ac045e28e1698c0bea9441af9409fc844dc79496 \ + --hash=sha256:2f3e1867dd574014253b4b8f01ba443b9c914e61d45f3674e452a915d6e929a3 \ + --hash=sha256:35fbd23c1c8732cde7a94abe7fb071ec173c2f58c0bd0d7e5b669fdfc80a2c7b \ + --hash=sha256:37d0c59548ae56fae01c14998918d04ee0d5d3277363c10208eef8c4e2b68ed6 \ + --hash=sha256:39d05e65f23a0fe897b6ac395f2a8d48c56ac0f583f5d663e0afec1da89b95da \ + --hash=sha256:3ad59efe24a4d54c2742929001f2d02803aafc15d6d781c21379e3f7f66ec842 \ + --hash=sha256:3aed39db2f0ace76faa94f465d4234aac72e2f32b009f15da6492a561b3bbebd \ + --hash=sha256:3bbac1953c17252f9cc675bb19372444aadf0179b5df575ac4b56faaec9f6294 \ + --hash=sha256:40bc802a696887b14c002edd43c18082cb7b6f9ee8b838239b03b56574d97f71 \ + --hash=sha256:42f712b4668831c0cd85e0a5b5a308700fe068e37dcd24c0062904c4e372b093 \ + --hash=sha256:448a66b8266de0b581246ca7cd6a73b8d98d15100fb7165974535fa3b577340e \ + --hash=sha256:485301ee56ce87a51ccb182a4b180d852c5cb2b3cb3a82f7d4714b4141119d8c \ + --hash=sha256:485747ee62da83366a44fbba963c5fe017860ad408ccd6cd99aa66ea80d32b2e \ + --hash=sha256:4cf0855a842c5b5c391dd32ca273b09e86abf8367572073bd1edfc52bc44446b \ + --hash=sha256:4eca20917a06d2fca7628ef3c8b94a8c358f6b43f1a621c9815243462dcccf97 \ + --hash=sha256:4ed172d0c79f156c1b954e99c03bc2e3033c17efce8dd1a7c781bc4d5793dfac \ + --hash=sha256:5267cfda873ad62591b9332fd9472d2409f7cf02a34a9c9cb367e2c0255994bf \ + --hash=sha256:52b5cbc0469328e58180021138207e6ec91d7ca2e037d3549cc9e34e2187330a \ + --hash=sha256:53d7a3cd46cdc1689296348cb05ffd4f4280035770aee0c8ead3bbd4d6529acc \ + --hash=sha256:563646d74a4b4456d0cf3b714ca522e725243c603e8254ad85c3b59b7c0c4bf0 \ + --hash=sha256:570cc326e78ff23dec7f41487aa9c3dffd02e5ee9ab43a8f6ccc3df8f9327623 \ + --hash=sha256:5aca759ada6b1967fcfd4336dcf460d02a8a23e6abe06e90ea7881e5c22c4de6 \ + --hash=sha256:5de11c041486681ce854c814844f4ce3282b6ea1656faae19208ebe09d31c5b8 \ + --hash=sha256:5e271dd97c7bb8eefda5cca38cd0b0373a1fea50f71e8071376b46968582af9b \ + --hash=sha256:642ed0a209ced4be3a46f8cb094f2d76f1f479e2a1ceca6de6346a096cd3409d \ + --hash=sha256:6446002739ca29249f0beaaf067fcbc2b5aab4bc7ee8fb941bd194947ce19aff \ + --hash=sha256:691d50c99a937709ac4c4cd570d959a006bd6a6d970a484c84cc99543d4a5bbb \ + --hash=sha256:69b857a7d8bd4f5d6e0db4086da8c46309a26e8cefdfc778c0c5cc17d4b11e08 \ + --hash=sha256:6ac3fefb0d168c7c6cab24fdfc80ec62cd2b4dfd9e65b84bdceb1cb01d385c33 \ + --hash=sha256:6c9141af27a4e5819d74d67d227d5047a20fa3c7d4d9df43037a955b4c748ec5 \ + --hash=sha256:7170cbde4070dc3c77dec82abf86f3b210633d4f89550fa0ad2d4b549a05572a \ + --hash=sha256:763ad59e105fca09705d9f9b29ecffb95ecdc3b0363be3bb56081b2c6de7977a \ + --hash=sha256:77076bdc8776a2b029e1e6ffbe6d7056e35f56f5e80d9dc0bad26ad4a024a762 \ + --hash=sha256:7cd020b1fb41e3ab7716d4d2c3972d4588fdfbab9bfbbb64acc7078eccef8860 \ + --hash=sha256:821392559d37759caa67d622d0d2994c7a3f2fb29274948ac799d496d92bca73 \ + --hash=sha256:829e91f3a8574888b73e7a3feb3b1af698e717513597e23136ff4eba0bc8387a \ + --hash=sha256:850c272e0e0d1a5c5d73b1b7871b0a7c2446b304cec55ccdb3eaac0d792bb065 \ + --hash=sha256:87d9b206b1bd7a0523375dc2020a6ce88bca5330682ae2fe25e86fd5d45cea9c \ + --hash=sha256:8bd01ff4032abaed03f2db702fa9a61078bee37add0bd884a6190b05e63b028c \ + --hash=sha256:8d54bbdf5d56e2c8cf81a1857250f3ea132de77af543d0ba5dce667183b61fec \ + --hash=sha256:8efaeb08ede95066da3a3e3c420fcc0a21693fcd0c4396d0585b019613d28515 \ + --hash=sha256:8f94fdd756ba1f79f988855d948ae0bad9ddf44df296770d9a58c774cfbcca72 \ + --hash=sha256:95cde244e7195b2c07ec9b73fa4c5026d4a27233451485caa1cd0c1b55f26dbd \ + --hash=sha256:975382d9aa90dc59253d6a83a5ca72e07f4ada3ae3d6c0575ced513db322b8ec \ + --hash=sha256:9dd9d9d9e898b9d30683bdd2b6c1849449158647d1049a125879cb397ee9cd12 \ + --hash=sha256:a019a344312d0b1f429c00d49c3be62fa273d4a1094e1b224f403716b6d03be1 \ + --hash=sha256:a4d9bfda3f84fc563868fe25ca160c8ff0e69bc4443c5647f960d59400ce6557 \ + --hash=sha256:a657250807b6efd19b28f5922520ae002a54cb43c2401e6f3d0230c352564d25 \ + --hash=sha256:a771417c9c06c56c9d53d11a5b084d1de75de82978e23c544270ab25e7c066ff \ + --hash=sha256:aad6ed9e70ddfb34d849b761fb243be58c735be6a9265b9060d6ddb77751e3e8 \ + --hash=sha256:ae87137951bb3dc08c7d8bfb8988d8c119f3230731b08a71146e84aaa919a7a9 \ + --hash=sha256:af247fd4f12cca4129c1b82090244ea5a9d5bb089e9a82feb5a2f7c6a9fe181d \ + --hash=sha256:b5d4bdd697195f3876d134101c40c7d06d46c6ab25159ed5cbd44105c715278a \ + --hash=sha256:b9255e7165083de7c1d605e818025e8860636348f34a79d84ec533546064f07e \ + --hash=sha256:c22211c165166de6683de8136229721f3d5c8606cc2c3d1562da9a3a5058049c \ + --hash=sha256:c55f9821f88e8bee4b7a72c82cfb5ecd22b6aad04033334f33c329b29bfa4da0 \ + --hash=sha256:c7aed97f2e676561416c927b063802c8a6285e9b55e1b83213dfd99a8f4f9e48 \ + --hash=sha256:cd2163f42868865597d89399a01aa33b7594ce8e2c4a28503127c81a2f17784e \ + --hash=sha256:ce5e7504db95b76fc89055c7f41e367eaadef5b1d059e27e1d6eabf2b55ca314 \ + --hash=sha256:cff7351c251c7546407827b6a37bcef6416304fc54d12d44dbfecbb717064717 \ + --hash=sha256:d27aa6bbc1f33be920bb7adbb95581452cdf23005d5611b29a12bb6a3468cc95 \ + --hash=sha256:d3b52a67ac66a3a64a7e710ba629f62d1e26ca0504c29ee8cbd99b97df7079a8 \ + --hash=sha256:de61e424062173b4f70eec07e12469edde7e17fa180019a2a0d75c13a5c5dc57 \ + --hash=sha256:e10e6a1ed2b8661201e79dff5531f8ad4cdd83548a0f81c95cf79b3184b20c33 \ + --hash=sha256:e1a0ffc39f51aa5f5c22114a8f1906b3c17eba68c5babb86c5f77d8b1bba14d1 \ + --hash=sha256:e22491d25f97199fc3581ad8dd8ce198d8c8fdb8dae80dea3512e1ce6d5fa99f \ + --hash=sha256:e626b864725680cd3904414d72e7b0bd81c0e5b2b53a5b30b4273034253bb41f \ + --hash=sha256:e8c71ea77536149e36c4c784f6d420ffd20bea041e3ba21ed021cb40ce58e2c9 \ + --hash=sha256:e8d0f0eca087630d58b8c662085529781fd5dc80f0a54eda42d5c9029f812599 \ + --hash=sha256:ea65b59882d5fa8c74a23f8960db579e5e341534934f43f3b18ec1839b893e41 \ + --hash=sha256:ea93163472db26ac6043e8f7f93a05d9b59e0505c760da2a3cd22c7dd7111391 \ + --hash=sha256:eab75a8569a095f2ad470b342f2751d9902f7944704f0571c8af46bede438475 \ + --hash=sha256:ed8313809571a5463fd7db43aaca68ecb43ca7a58f5b23b6e6c6c5d02bdc7882 \ + --hash=sha256:ef5fddfb264e89c435be4adb3953cef5d2936fdeb4463b4161a6ba2f22e7b740 \ + --hash=sha256:ef750a20de1b65657a1425f77c525b0183eac63fe7b8f5ac0dd16f3668d3e64f \ + --hash=sha256:efb9ece97e696bb56e31166a9dd7919f8f0c6b31967b454718c6509f29ef6fee \ + --hash=sha256:f4c179a7aeae10ddf44c6bac87938134c1379c49c884529f090f9bf05566c836 \ + --hash=sha256:f602881d80ee4228a2355c68da6b296a296cd22bbb91e5418d54577bbf17fa7c \ + --hash=sha256:fc2200e79d75b5238c8d69f6a30f8284290c777039d331e7340b6c17cad24a5a \ + --hash=sha256:fcc1ebb7561a3e24a6588f7c6ded15d80aec22c66a070c757559b57b17ffd1cb + # via + # -c /workspace/requirements-dev.txt + # jsonschema + # referencing +s3transfer==0.7.0 \ + --hash=sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a \ + --hash=sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e + # via + # -c /workspace/requirements-dev.txt + # boto3 +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via + # -c /workspace/requirements-dev.txt + # dependency-injector + # python-dateutil +testcontainers[kafka,mongo]==3.7.1 \ + --hash=sha256:7f48cef4bf0ccd78f1a4534d4b701a003a3bace851f24eae58a32f9e3f0aeba0 + # via + # -c /workspace/requirements-dev.txt + # hexkit (pyproject.toml) +typing-extensions==4.8.0 \ + --hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \ + --hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef + # via + # -c /workspace/requirements-dev.txt + # pydantic +urllib3==1.26.16 \ + --hash=sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f \ + --hash=sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14 + # via + # -c /workspace/requirements-dev.txt + # botocore + # docker + # requests +websocket-client==1.6.3 \ + --hash=sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f \ + --hash=sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03 + # via + # -c /workspace/requirements-dev.txt + # docker +wrapt==1.15.0 \ + --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \ + --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \ + --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \ + --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \ + --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \ + --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \ + --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \ + --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \ + --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \ + --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \ + --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \ + --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \ + --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \ + --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \ + --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \ + --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \ + --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \ + --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \ + --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \ + --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \ + --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \ + --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \ + --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \ + --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \ + --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \ + --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \ + --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \ + --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \ + --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \ + --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \ + --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \ + --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \ + --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \ + --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \ + --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \ + --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \ + --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \ + --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \ + --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \ + --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \ + --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \ + --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \ + --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \ + --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \ + --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \ + --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \ + --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \ + --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \ + --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \ + --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \ + --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \ + --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \ + --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \ + --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \ + --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \ + --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \ + --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \ + --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \ + --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \ + --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \ + --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \ + --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \ + --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \ + --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \ + --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \ + --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \ + --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \ + --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \ + --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \ + --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \ + --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \ + --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \ + --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \ + --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \ + --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639 + # via + # -c /workspace/requirements-dev.txt + # testcontainers diff --git a/scripts/get_package_name.py b/scripts/get_package_name.py index 7c6b79a0..84d15fd9 100755 --- a/scripts/get_package_name.py +++ b/scripts/get_package_name.py @@ -15,24 +15,24 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Extracts the package name from the setup.cfg""" +"""Extracts the package name from pyproject.toml""" from pathlib import Path REPO_ROOT_DIR = Path(__file__).parent.parent.resolve() -SETUP_CFG_PATH = REPO_ROOT_DIR / "setup.cfg" +PYPROJECT_TOML_PATH = REPO_ROOT_DIR / "pyproject.toml" NAME_PREFIX = "name = " def get_package_name() -> str: """Extracts the package name""" - with open(SETUP_CFG_PATH, "r", encoding="utf8") as setup_cfg: - for line in setup_cfg.readlines(): + with open(PYPROJECT_TOML_PATH, encoding="utf8") as pyproject_toml: + for line in pyproject_toml.readlines(): line_stripped = line.strip() if line_stripped.startswith(NAME_PREFIX): package_name = line_stripped[len(NAME_PREFIX) :] - return package_name + return package_name.strip('"') raise RuntimeError("Could not find package name.") diff --git a/scripts/license_checker.py b/scripts/license_checker.py index 0d43a1b4..52944330 100755 --- a/scripts/license_checker.py +++ b/scripts/license_checker.py @@ -26,7 +26,7 @@ import sys from datetime import date from pathlib import Path -from typing import List, Optional, Tuple, Union +from typing import Optional, Union # root directory of the package: ROOT_DIR = Path(__file__).parent.parent.resolve() @@ -53,6 +53,8 @@ ".flake8", ".gitignore", ".pylintrc", + ".ruff.toml", + ".ruff_cache", "example_config.yaml", "config_schema.json", "LICENSE", # is checked but not for the license header @@ -74,6 +76,7 @@ # exclude file by file ending from license header check: EXCLUDE_ENDINGS = [ "html", + "in", "ini", "jinja", "json", @@ -81,6 +84,7 @@ "pub", "pyc", "sec", + "toml", "txt", "xml", "yaml", @@ -163,10 +167,10 @@ def __init__(self, file_path: Union[str, Path]): def get_target_files( target_dir: Path, - exclude: List[str] = EXCLUDE, - exclude_endings: List[str] = EXCLUDE_ENDINGS, - exclude_pattern: List[str] = EXCLUDE_PATTERN, -) -> List[Path]: + exclude: list[str] = EXCLUDE, + exclude_endings: list[str] = EXCLUDE_ENDINGS, + exclude_pattern: list[str] = EXCLUDE_PATTERN, +) -> list[Path]: """Get target files that are not match the exclude conditions. Args: target_dir (pathlib.Path): The target dir to search. @@ -200,7 +204,7 @@ def get_target_files( return target_files -def normalized_line(line: str, chars_to_trim: List[str] = COMMENT_CHARS) -> str: +def normalized_line(line: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str: norm_line = line.strip() for char in chars_to_trim: @@ -209,11 +213,11 @@ def normalized_line(line: str, chars_to_trim: List[str] = COMMENT_CHARS) -> str: return norm_line.strip("\n").strip("\t").strip() -def normalized_text(text: str, chars_to_trim: List[str] = COMMENT_CHARS) -> str: +def normalized_text(text: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str: "Normalize a license header text." lines = text.split("\n") - norm_lines: List[str] = [] + norm_lines: list[str] = [] for line in lines: stripped_line = line.strip() @@ -239,7 +243,7 @@ def format_copyright_template(copyright_template: str, author: str) -> str: return normalized_text(copyright_template.replace("{author}", author)) -def is_commented_line(line: str, comment_chars: List[str] = COMMENT_CHARS) -> bool: +def is_commented_line(line: str, comment_chars: list[str] = COMMENT_CHARS) -> bool: """Checks whether a line is a comment.""" line_stripped = line.strip() for comment_char in comment_chars: @@ -254,12 +258,12 @@ def is_empty_line(line: str) -> bool: return line.strip("\n").strip("\t").strip() == "" -def get_header(file_path: Path, comment_chars: List[str] = COMMENT_CHARS): +def get_header(file_path: Path, comment_chars: list[str] = COMMENT_CHARS): """Extracts the header from a file and normalizes it.""" - header_lines: List[str] = [] + header_lines: list[str] = [] try: - with open(file_path, "r") as file: + with open(file_path) as file: for line in file: if is_commented_line( line, comment_chars=comment_chars @@ -307,7 +311,7 @@ def check_copyright_notice( global_copyright: GlobalCopyrightNotice, copyright_template: str = COPYRIGHT_TEMPLATE, author: str = AUTHOR, - comment_chars: List[str] = COMMENT_CHARS, + comment_chars: list[str] = COMMENT_CHARS, min_year: int = MIN_YEAR, ) -> bool: """Checks the specified copyright text against a template. @@ -372,12 +376,12 @@ def check_file_headers( global_copyright: GlobalCopyrightNotice, copyright_template: str = COPYRIGHT_TEMPLATE, author: str = AUTHOR, - exclude: List[str] = EXCLUDE, - exclude_endings: List[str] = EXCLUDE_ENDINGS, - exclude_pattern: List[str] = EXCLUDE_PATTERN, - comment_chars: List[str] = COMMENT_CHARS, + exclude: list[str] = EXCLUDE, + exclude_endings: list[str] = EXCLUDE_ENDINGS, + exclude_pattern: list[str] = EXCLUDE_PATTERN, + comment_chars: list[str] = COMMENT_CHARS, min_year: int = MIN_YEAR, -) -> Tuple[List[Path], List[Path]]: +) -> tuple[list[Path], list[Path]]: """Check files for presence of a license header and verify that the copyright notice is up to date (correct year). @@ -414,8 +418,8 @@ def check_file_headers( ) # check if license header present in file: - passed_files: List[Path] = [] - failed_files: List[Path] = [] + passed_files: list[Path] = [] + failed_files: list[Path] = [] for target_file in target_files: try: @@ -443,7 +447,7 @@ def check_license_file( global_copyright: GlobalCopyrightNotice, copyright_template: str = COPYRIGHT_TEMPLATE, author: str = AUTHOR, - comment_chars: List[str] = COMMENT_CHARS, + comment_chars: list[str] = COMMENT_CHARS, min_year: int = MIN_YEAR, ) -> bool: """Currently only checks if the copyright notice in the @@ -470,7 +474,7 @@ def check_license_file( print(f'Could not find license file "{str(license_file)}".') return False - with open(license_file, "r") as file_: + with open(license_file) as file_: license_text = normalized_text(file_.read()) # Extract the copyright notice: @@ -522,7 +526,7 @@ def run(): global_copyright = GlobalCopyrightNotice() # get global copyright from .devcontainer/license_header.txt file: - with open(GLOBAL_COPYRIGHT_FILE_PATH, "r") as global_copyright_file: + with open(GLOBAL_COPYRIGHT_FILE_PATH) as global_copyright_file: global_copyright.text = normalized_text(global_copyright_file.read()) if args.no_license_file_check: diff --git a/scripts/update_hook_revs.py b/scripts/update_hook_revs.py new file mode 100755 index 00000000..931d0ce5 --- /dev/null +++ b/scripts/update_hook_revs.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 + +# Copyright 2021 - 2023 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln +# for the German Human Genome-Phenome Archive (GHGA) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Script to ensure the pre-commit hook revs match what is installed.""" +import re +import sys +from functools import partial +from pathlib import Path + +from script_utils import cli + +REPO_ROOT_DIR = Path(__file__).parent.parent.resolve() +PRE_COMMIT_CFG_PATH = REPO_ROOT_DIR / ".pre-commit-config.yaml" +LOCK_FILE_PATH = REPO_ROOT_DIR / "requirements-dev.txt" + + +def inspect_lock() -> dict[str, str]: + """Inspect the lock file to get the resolved dependencies""" + dependency_pattern = re.compile(r"([^=\s]+)==([^\s]*?)\s") + dependencies = {} + + # Get the set of dependencies from the requirements-dev.txt lock file + with open(LOCK_FILE_PATH, encoding="utf-8") as lock_file: + lines = lock_file.readlines() + + for line in lines: + match = re.match(dependency_pattern, line) + if match: + package, version = match.groups() + dependencies[package] = version + + return dependencies + + +def get_repl_value(match, dependencies: dict[str, str], outdated_hooks: list[str]): + """Look up pre-commit hook id in list of dependencies. If there's a match, update + `outdated_hooks` and return the hook version stored in the dictionary""" + ver, name = match.groups() + if name in dependencies: + new_ver = dependencies[name].strip() + + # Use the v prefix if it was used before + if ver.startswith("v"): + new_ver = ver[0] + new_ver + + # Make a list of what's outdated + if new_ver != ver: + msg = f"\t{name} (configured: {ver}, expected: {new_ver})" + outdated_hooks.append(msg) + return new_ver + return ver + + +def get_config(): + """Obtain the current pre-commit hook config from .pre-commit-config.yaml""" + with open(PRE_COMMIT_CFG_PATH, encoding="utf-8") as pre_commit_config: + return pre_commit_config.read() + + +def process_config(dependencies: dict[str, str], config: str) -> tuple[str, list[str]]: + """Compare pre-commit config with lock file dependencies. + + Create a modified copy of the existing config file contents with the hook versions + synchronized to the lock file dependencies. + + Returns: + `new_config`: the updated/synchronized pre-commit config. + + `outdated_hooks`: a list of any outdated hooks with version discrepancy details. + """ + outdated_hooks: list[str] = [] + + hook_rev = re.compile(r"([^\s\n]+)(?=\s*hooks:\s*- id: ([^\s]+))") + + new_config = re.sub( + hook_rev, + repl=partial( + get_repl_value, dependencies=dependencies, outdated_hooks=outdated_hooks + ), + string=config, + ) + + return new_config, outdated_hooks + + +def update_config(new_config: str): + """Write `new_config` to .pre-commit-config.yaml""" + with open(PRE_COMMIT_CFG_PATH, "w", encoding="utf-8") as pre_commit_config: + pre_commit_config.write(new_config) + cli.echo_success(f"Updated '{PRE_COMMIT_CFG_PATH}'") + + +def output_failure(outdated_hooks: list[str]): + """Notify the user that some pre-commit hooks are outdated, and list those hooks.""" + cli.echo_failure("The following pre-commit hook versions are outdated:") + for hook in outdated_hooks: + print(hook) + print("Run 'scripts/update_hook_revs.py' to update") + sys.exit(1) + + +def main(check: bool = False): + """Compare configured pre-commit hooks with the installed dependencies. + + For the set that overlap (e.g. `black`, `mypy`, `ruff`, etc.), make sure the + versions match. If running with `--check`, exit with status code 1 if anything is + outdated. If running without `--check`, update `.pre-commit-config.yaml` as needed. + """ + + dependencies = inspect_lock() + config = get_config() + new_config, outdated_hooks = process_config(dependencies, config) + + if config != new_config: + if check: + output_failure(outdated_hooks) + else: + update_config(new_config) + else: + cli.echo_success("Pre-commit hooks are up to date.") + + +if __name__ == "__main__": + cli.run(main) diff --git a/scripts/update_lock.py b/scripts/update_lock.py new file mode 100755 index 00000000..8206cb62 --- /dev/null +++ b/scripts/update_lock.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 + +# Copyright 2021 - 2023 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln +# for the German Human Genome-Phenome Archive (GHGA) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +"""Update the dependency lock files located at 'requirements.txt' and +'requirements-dev.txt'. +""" + +import os +import re +import subprocess +from copy import deepcopy +from pathlib import Path +from tempfile import TemporaryDirectory + +import stringcase +import tomli +import tomli_w + +from script_utils import cli + +REPO_ROOT_DIR = Path(__file__).parent.parent.resolve() + +PYPROJECT_TOML_PATH = REPO_ROOT_DIR / "pyproject.toml" +DEV_DEPS_PATH = REPO_ROOT_DIR / "requirements-dev.in" +OUTPUT_LOCK_PATH = REPO_ROOT_DIR / "requirements.txt" +OUTPUT_DEV_LOCK_PATH = REPO_ROOT_DIR / "requirements-dev.txt" + + +def exclude_from_dependency_list(*, package_name: str, dependencies: list) -> list: + """Exclude the specified package from the provided dependency list.""" + + return [ + dependency + for dependency in dependencies + if not dependency.startswith(package_name) + ] + + +def remove_self_dependencies(pyproject: dict) -> dict: + """Filter out self dependencies (dependencies of the package on it self) from the + dependencies and optional-dependencies in the provided pyproject metadata.""" + + if "project" not in pyproject: + return pyproject + + modified_pyproject = deepcopy(pyproject) + + project_metadata = modified_pyproject["project"] + + package_name = stringcase.spinalcase(project_metadata.get("name")) + + if not package_name: + raise ValueError("The provided project metadata does not contain a name.") + + if "dependencies" in project_metadata: + project_metadata["dependencies"] = exclude_from_dependency_list( + package_name=package_name, dependencies=project_metadata["dependencies"] + ) + + if "optional-dependencies" in project_metadata: + for group in project_metadata["optional-dependencies"]: + project_metadata["optional-dependencies"][ + group + ] = exclude_from_dependency_list( + package_name=package_name, + dependencies=project_metadata["optional-dependencies"][group], + ) + + return modified_pyproject + + +def fix_temp_dir_comments(file_path: Path): + """Fix the temp_dir comments so they don't cause a noisy diff + + This will leave the top compile message intact as a point of sanity to verify that + the requirements are indeed being generated if nothing else changes. + """ + + with open(file_path, encoding="utf-8") as file: + lines = file.readlines() + + with open(file_path, "w", encoding="utf-8") as file: + for line in lines: + # Remove random temp directory name + line = re.sub( + r"\([^\)\(]*?pyproject\.toml\)", + "(pyproject.toml)", + line, + ) + file.write(line) + + +def is_file_outdated(old_file: Path, new_file: Path) -> bool: + """Compares two lock files and returns True if there is a difference, else False""" + + header_comment = "# pip-compile" + outdated = False + + with open(old_file, encoding="utf-8") as old: + with open(new_file, encoding="utf-8") as new: + old_lines = old.readlines() + new_lines = new.readlines() + if len(old_lines) != len(new_lines): + outdated = True + if not outdated: + for old_line, new_line in zip(old_lines, new_lines): + if old_line.startswith(header_comment): + continue + if old_line != new_line: + outdated = True + break + if outdated: + cli.echo_failure(f"{str(old_file)} is out of date!") + return outdated + + +def compile_lock_file( + sources: list[Path], + output: Path, + upgrade: bool, + extras: bool, +) -> None: + """From the specified sources compile a lock file using pip-compile from pip-tools + and write it to the specified output location. + """ + + print(f"Updating '{output.name}'...") + + command = [ + "pip-compile", + "--rebuild", + "--generate-hashes", + "--annotate", + ] + + if upgrade: + command.append("--upgrade") + + if extras: + command.append("--all-extras") + + command.extend(["--output-file", str(output.absolute())]) + + command.extend([str(source.absolute()) for source in sources]) + + # constrain the production deps by what's pinned in requirements-dev.txt + if output.name == OUTPUT_LOCK_PATH.name: + command.extend(["-c", str(OUTPUT_DEV_LOCK_PATH)]) + + completed_process = subprocess.run( + args=command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + ) + if completed_process.returncode != 0: + std_out = completed_process.stdout + log = std_out.decode("utf-8") if std_out else "no log available." + raise RuntimeError(f"Failed to compile lock file:\n{log}") + + fix_temp_dir_comments(output.absolute()) + + +def ensure_lock_files_exist(): + """Make sure that the lock files exist if in check mode""" + for output in [OUTPUT_DEV_LOCK_PATH, OUTPUT_LOCK_PATH]: + if not os.path.exists(output): + cli.echo_failure(f"{output} is missing") + return + + +def main(upgrade: bool = False, check: bool = False): + """Update the dependency lock files located at 'requirements.txt' and + 'requirements-dev.txt'. + + For the 'requirements.txt' only the package with its dependencies being defined in + the 'pyproject.toml' are considered. Thereby, all recursive dependencies of the + package on itself are removed. + + For the 'requirements-dev.txt', in addition to the filtered 'pyproject.toml' the + 'requirements-dev.in' is considered. + + The `upgrade` parameter can be used to indicate that dependencies found in existing + lock files should be upgraded. Default pip-compile behavior is to leave them as is. + """ + + # if --check is used, quickly ensure that there is something to compare against + if check: + ensure_lock_files_exist() + + with open(PYPROJECT_TOML_PATH, "rb") as pyproject_toml: + pyproject = tomli.load(pyproject_toml) + + modified_pyproject = remove_self_dependencies(pyproject) + + extras = ( + "optional-dependencies" in modified_pyproject["project"] + and modified_pyproject["project"]["optional-dependencies"] + ) + with TemporaryDirectory() as temp_dir: + modified_pyproject_path = Path(temp_dir) / "pyproject.toml" + with open(modified_pyproject_path, "wb") as modified_pyproject_toml: + tomli_w.dump(modified_pyproject, modified_pyproject_toml) + + # make src dir next to TOML to satisfy build system + os.makedirs(Path(temp_dir) / "src") + + # temporary test files + check_dev_path = Path(temp_dir) / OUTPUT_DEV_LOCK_PATH.name + check_prod_path = Path(temp_dir) / OUTPUT_LOCK_PATH.name + + # compile requirements-dev.txt (includes all dependencies) + compile_lock_file( + sources=[modified_pyproject_path, DEV_DEPS_PATH], + output=check_dev_path if check else OUTPUT_DEV_LOCK_PATH, + upgrade=upgrade, + extras=extras, + ) + + if check and is_file_outdated(OUTPUT_DEV_LOCK_PATH, check_dev_path): + return + + # compile requirements.txt (only includes production-related subset of above) + compile_lock_file( + sources=[modified_pyproject_path], + output=check_prod_path if check else OUTPUT_LOCK_PATH, + upgrade=upgrade, + extras=extras, + ) + + if check and is_file_outdated(OUTPUT_LOCK_PATH, check_prod_path): + return + + if check: + cli.echo_success("Lock files are up to date.") + else: + cli.echo_success( + f"Successfully updated lock files at '{OUTPUT_LOCK_PATH}' and" + + f" '{OUTPUT_DEV_LOCK_PATH}'." + ) + + +if __name__ == "__main__": + cli.run(main) diff --git a/scripts/update_template_files.py b/scripts/update_template_files.py index 952fe2c3..d3313fd5 100755 --- a/scripts/update_template_files.py +++ b/scripts/update_template_files.py @@ -58,7 +58,7 @@ class ValidationError(RuntimeError): def get_file_list(list_name: str) -> list[str]: """Return a list of all file names specified in a given list file.""" list_path = REPO_ROOT_DIR / list_name - with open(list_path, "r", encoding="utf8") as list_file: + with open(list_path, encoding="utf8") as list_file: file_list = [ clean_line for clean_line in ( @@ -127,7 +127,7 @@ def check_file(relative_file_path: str, diff: bool = False) -> bool: print(f" - {local_file_path}: cannot check, remote is missing") return True - with open(local_file_path, "r", encoding="utf8") as file: + with open(local_file_path, encoding="utf8") as file: return diff_content(local_file_path, file.read(), template_file_content) return False @@ -153,7 +153,7 @@ def update_file(relative_file_path: str, diff: bool = False) -> bool: return True if diff and local_file_path.exists(): - with open(local_file_path, "r", encoding="utf8") as file: + with open(local_file_path, encoding="utf8") as file: if file.read() == template_file_content: return False diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index c2117de2..00000000 --- a/setup.cfg +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2021 - 2023 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln -# for the German Human Genome-Phenome Archive (GHGA) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -[metadata] -name = hexkit -version = attr: hexkit.__version__ -description = A Toolkit for Building Microservices using the Hexagonal Architecture -url = https://github.com/ghga-de/hexkit -long_description = file: README.md -long_description_content_type = text/markdown; charset=UTF-8 -author = German Human Genome Phenome Archive (GHGA) -author_email = contact@ghga.de -license = Apache 2.0 -classifiers = - Development Status :: 1 - Planning - Operating System :: POSIX :: Linux - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - License :: OSI Approved :: Apache Software License - Topic :: Internet :: WWW/HTTP :: HTTP Servers - Topic :: Software Development :: Libraries - Intended Audience :: Developers - -[options] -zip_safe = False -include_package_data = True -packages = find: -install_requires = - pydantic==1.10.6 - PyYAML==6.0 - dependency-injector==4.41.0 -python_requires = >= 3.9 - -[options.package_data] -* = *.yaml, *.json - -[options.extras_require] -akafka = - aiokafka==0.8.0 - jsonschema>=4.17.3 -s3 = - boto3==1.26.50 - botocore==1.29.50 -mongodb = - motor==3.1.1 - -test-akafka = - %(akafka)s - testcontainers[kafka]==3.4.1 -test-s3 = - %(s3)s - testcontainers==3.4.1 -test-mongodb = - %(mongodb)s - testcontainers[mongo]==3.4.1 - -test-all = - %(test-akafka)s - %(test-s3)s - %(test-mongodb)s - -all = - %(akafka)s - %(s3)s - %(mongodb)s - %(test-all)s - -[options.packages.find] -exclude = tests diff --git a/setup.py b/setup.py deleted file mode 100755 index bee89844..00000000 --- a/setup.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2021 - 2023 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln -# for the German Human Genome-Phenome Archive (GHGA) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Setup script for pip. This setup configs are specified in the `setup.cfg` file""" - -import setuptools - -if __name__ == "__main__": - setuptools.setup() diff --git a/hexkit/__init__.py b/src/hexkit/__init__.py similarity index 91% rename from hexkit/__init__.py rename to src/hexkit/__init__.py index 675cc080..a179c1d9 100644 --- a/hexkit/__init__.py +++ b/src/hexkit/__init__.py @@ -15,4 +15,6 @@ """A Toolkit for Building Microservices using the Hexagonal Architecture""" -__version__ = "0.10.2" +from importlib.metadata import version + +__version__ = version(__package__) diff --git a/hexkit/__main__.py b/src/hexkit/__main__.py similarity index 100% rename from hexkit/__main__.py rename to src/hexkit/__main__.py diff --git a/hexkit/base.py b/src/hexkit/base.py similarity index 100% rename from hexkit/base.py rename to src/hexkit/base.py diff --git a/hexkit/config.py b/src/hexkit/config.py similarity index 93% rename from hexkit/config.py rename to src/hexkit/config.py index 78f665b3..9aec5f1f 100644 --- a/hexkit/config.py +++ b/src/hexkit/config.py @@ -17,7 +17,7 @@ import os from pathlib import Path -from typing import Any, Callable, Dict, Final, Optional +from typing import Any, Callable, Final, Optional import yaml from pydantic import BaseSettings @@ -77,7 +77,7 @@ def get_default_config_yaml(prefix: str) -> Optional[Path]: def yaml_settings_factory( config_yaml: Optional[Path] = None, -) -> Callable[[BaseSettings], Dict[str, Any]]: +) -> Callable[[BaseSettings], dict[str, Any]]: """ A factory of source methods for pydantic's BaseSettings Config that load settings from a yaml file. @@ -89,12 +89,12 @@ def yaml_settings_factory( def yaml_settings( # pylint: disable=unused-argument settings: BaseSettings, - ) -> Dict[str, Any]: - """source method for loading pydantic BaseSettings from a yaml file""" + ) -> dict[str, Any]: + """Source method for loading pydantic BaseSettings from a yaml file""" if config_yaml is None: return {} - with open(config_yaml, "r", encoding="utf8") as yaml_file: + with open(config_yaml, encoding="utf8") as yaml_file: return yaml.safe_load(yaml_file) return yaml_settings @@ -136,7 +136,6 @@ def decorator(settings) -> Callable: settings (BaseSettings): A pydantic BaseSettings class to be modified. """ - # check if settings inherits from pydantic BaseSettings: if not issubclass(settings, BaseSettings): raise TypeError( @@ -153,13 +152,11 @@ def constructor_wrapper( config_yaml (str, optional): Path to a config yaml. Overwrites the default location. """ - # get default path if config_yaml not specified: if config_yaml is None: config_yaml = get_default_config_yaml(prefix) - else: - if not config_yaml.is_file(): - raise ConfigYamlDoesNotExist(path=config_yaml) + elif not config_yaml.is_file(): + raise ConfigYamlDoesNotExist(path=config_yaml) class ModSettings(settings): """Modifies the orginal Settings class provided by the user""" @@ -181,7 +178,7 @@ def customise_sources( env_settings, file_secret_settings, ): - """add custom yaml source""" + """Add custom yaml source""" return ( init_settings, env_settings, diff --git a/hexkit/custom_types.py b/src/hexkit/custom_types.py similarity index 100% rename from hexkit/custom_types.py rename to src/hexkit/custom_types.py diff --git a/hexkit/inject.py b/src/hexkit/inject.py similarity index 97% rename from hexkit/inject.py rename to src/hexkit/inject.py index 40bcb7c6..547a6af7 100644 --- a/hexkit/inject.py +++ b/src/hexkit/inject.py @@ -55,7 +55,8 @@ class NotConstructableError(TypeError): class AsyncInitShutdownError(TypeError): """Thrown when a container has sync `init_resource` or `shutdown_resource` methods - but coroutines are needed.""" + but coroutines are needed. + """ def assert_async_constructable( @@ -68,7 +69,6 @@ def assert_async_constructable( not check whether `construct` really returns an awaitable or an async context manager. """ - if not callable(getattr(constructable, "construct", None)): raise NotConstructableError( "Async(Context)Constructable class must have a callable `construct` attribute." @@ -77,7 +77,8 @@ def assert_async_constructable( class AsyncConstructor(dependency_injector.providers.Resource): """Maps an Async(Context)Constructable onto the Resource class from the - `dependency_injector` framework.""" + `dependency_injector` framework. + """ @staticmethod def constructable_to_resource( @@ -87,7 +88,6 @@ def constructable_to_resource( Converts an Async(Context)Constructable to an async generator that is compatible with the Resource definition of the `dependency_injector` framework. """ - assert_async_constructable(constructable) async def resource(*args: Any, **kwargs: Any) -> AsyncIterator[Any]: @@ -120,7 +120,6 @@ def __init__( **kwargs: dependency_injector.providers.Injection, ): """Initialize `dependency_injector`'s Resource with an AbstractAsyncContextManager.""" - if provides is None: super().__init__() else: @@ -130,8 +129,8 @@ def __init__( def get_constructor(provides: type, *args, **kwargs): """Automatically selects and applies the right constructor for the class given to - `provides`.""" - + `provides`. + """ constructor_cls: type try: @@ -148,11 +147,11 @@ def get_constructor(provides: type, *args, **kwargs): class CMDynamicContainer(dependency_injector.containers.DynamicContainer): """Adds a async context manager interface to the DynamicContainer base class from - the `dependency_injector` framework.""" + the `dependency_injector` framework. + """ async def __aenter__(self): """Init/setup resources.""" - init_future = self.init_resources() if not inspect.isawaitable(init_future): @@ -165,7 +164,6 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_value, exc_trace): """Shutdown/teardown resources""" - shutdown_future = self.shutdown_resources() if not inspect.isawaitable(shutdown_future): @@ -206,11 +204,11 @@ async def __aexit__(self, exc_type, exc_value, exc_trace): class Configurator(dependency_injector.providers.Factory, Generic[PydanticConfig]): """A configuration constructor that holds configuration parameters using a pydantic - model.""" + model. + """ def load_config(self, config: PydanticConfig): """Loading config parameters form an pydantic config instance.""" - self.override(dependency_injector.providers.Callable(lambda: config)) @@ -220,6 +218,6 @@ def get_configurator( """Initializes a configuration provider. This helper function is necessary because the __init__ of Providers used by the - dependency_injector framework need to always use the same signature.""" - + dependency_injector framework need to always use the same signature. + """ return Configurator[PydanticConfig](pydantic_cls) diff --git a/hexkit/protocols/__init__.py b/src/hexkit/protocols/__init__.py similarity index 100% rename from hexkit/protocols/__init__.py rename to src/hexkit/protocols/__init__.py diff --git a/hexkit/protocols/dao.py b/src/hexkit/protocols/dao.py similarity index 98% rename from hexkit/protocols/dao.py rename to src/hexkit/protocols/dao.py index 47ad4f8a..edf9efa1 100644 --- a/hexkit/protocols/dao.py +++ b/src/hexkit/protocols/dao.py @@ -15,7 +15,10 @@ # """Protocol for creating Data Access Objects to perform CRUD (plus find) interactions -with the database.""" +with the database. +""" + +# ruff: noqa: PLR0913 import typing from abc import ABC, abstractmethod @@ -72,7 +75,8 @@ class InvalidFindMappingError(FindError): class MultipleHitsFoundError(FindError): """Raised when a DAO find operation did result in multiple hits while only a - single hit was expected.""" + single hit was expected. + """ def __init__(self, *, mapping: Mapping[str, str]): message = ( @@ -84,7 +88,8 @@ def __init__(self, *, mapping: Mapping[str, str]): class NoHitsFoundError(FindError): """Raised when a DAO find operation did result in no hits while a - single hit was expected.""" + single hit was expected. + """ def __init__(self, *, mapping: Mapping[str, str]): message = ( @@ -259,7 +264,6 @@ async def uuid4_id_generator() -> AsyncGenerator[str, None]: This is an AsyncGenerator to be compliant with the id_generator requirements of the DaoFactoryProtocol. """ - while True: yield str(uuid4()) @@ -274,7 +278,8 @@ class IdFieldNotFoundError(ValueError): class CreationModelInvalidError(ValueError): """Raised when the DtoCreationModel was invalid in relation to the main - DTO model.""" + DTO model. + """ class IndexFieldsInvalidError(ValueError): """Raised when providing an invalid list of fields to index.""" @@ -282,8 +287,8 @@ class IndexFieldsInvalidError(ValueError): @classmethod def _validate_dto_model_id(cls, *, dto_model: type[Dto], id_field: str) -> None: """Checks whether the dto_model contains the expected id_field. - Raises IdFieldNotFoundError otherwise.""" - + Raises IdFieldNotFoundError otherwise. + """ if id_field not in dto_model.schema()["properties"]: raise cls.IdFieldNotFoundError() @@ -296,8 +301,8 @@ def _validate_dto_creation_model( id_field: str, ) -> None: """Checks that the dto_creation_model has the same fields as the dto_model - except missing the ID. Raises CreationModelInvalidError otherwise.""" - + except missing the ID. Raises CreationModelInvalidError otherwise. + """ if dto_creation_model is None: return @@ -318,8 +323,8 @@ def _validate_fields_to_index( fields_to_index: Optional[Collection[str]], ) -> None: """Checks that all provided fields are present in the dto_model. - Raises IndexFieldsInvalidError otherwise.""" - + Raises IndexFieldsInvalidError otherwise. + """ if fields_to_index is None: return @@ -403,7 +408,6 @@ async def get_dao( self.IdFieldNotFoundError: Raised when the dto_model did not contain the expected id_field. """ - self._validate_dto_model_id(dto_model=dto_model, id_field=id_field) self._validate_dto_creation_model( @@ -470,5 +474,6 @@ async def _get_dao( id_generator: AsyncGenerator[str, None], ) -> Union[DaoSurrogateId[Dto, DtoCreation], DaoNaturalId[Dto]]: """*To be implemented by the provider. Input validation is done outside of this - method.*""" + method.* + """ ... diff --git a/hexkit/protocols/eventpub.py b/src/hexkit/protocols/eventpub.py similarity index 100% rename from hexkit/protocols/eventpub.py rename to src/hexkit/protocols/eventpub.py diff --git a/hexkit/protocols/eventsub.py b/src/hexkit/protocols/eventsub.py similarity index 100% rename from hexkit/protocols/eventsub.py rename to src/hexkit/protocols/eventsub.py diff --git a/hexkit/protocols/objstorage.py b/src/hexkit/protocols/objstorage.py similarity index 98% rename from hexkit/protocols/objstorage.py rename to src/hexkit/protocols/objstorage.py index 4a2ae54e..b17710aa 100644 --- a/hexkit/protocols/objstorage.py +++ b/src/hexkit/protocols/objstorage.py @@ -16,6 +16,7 @@ """Protocol for interacting with S3-like Object Storages.""" +# ruff: noqa: PLR0913 import re from abc import ABC, abstractmethod @@ -28,16 +29,15 @@ class PresignedPostURL(NamedTuple): """Container for presigned POST URLs along with additional metadata fields that - should be attached as body data when sending the POST request.""" + should be attached as body data when sending the POST request. + """ url: str fields: dict[str, str] class ObjectStorageProtocol(ABC): - """ - Protocol for interacting with S3-like Object Storages. - """ + """Protocol for interacting with S3-like Object Storages.""" # constants for multipart uploads: # (shall not be changed by provider implementations) @@ -51,7 +51,6 @@ async def does_bucket_exist(self, bucket_id: str) -> bool: """Check whether a bucket with the specified ID (`bucket_id`) exists. Returns `True` if it exists and `False` otherwise. """ - self._validate_bucket_id(bucket_id) return await self._does_bucket_exist(bucket_id) @@ -60,7 +59,6 @@ async def create_bucket(self, bucket_id: str) -> None: Create a bucket (= a structure that can hold multiple file objects) with the specified unique ID. """ - self._validate_bucket_id(bucket_id) await self._create_bucket(bucket_id) @@ -73,14 +71,11 @@ async def delete_bucket( will be deleted, if False (the default) a BucketNotEmptyError will be raised if the bucket is not empty. """ - self._validate_bucket_id(bucket_id) await self._delete_bucket(bucket_id, delete_content=delete_content) async def list_all_object_ids(self, bucket_id: str) -> list[str]: - """ - Retrieve a list of IDs for all objects currently present in the specified bucket - """ + """Retrieve a list of IDs for all objects currently present in the specified bucket""" self._validate_bucket_id(bucket_id) return await self._list_all_object_ids(bucket_id=bucket_id) @@ -97,7 +92,6 @@ async def get_object_upload_url( You may also specify a custom expiry duration in seconds (`expires_after`) and a maximum size (bytes) for uploads (`max_upload_size`). """ - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) return await self._get_object_upload_url( @@ -114,7 +108,6 @@ async def init_multipart_upload( object_id: str, ) -> str: """Initiates a multipart upload procedure. Returns the upload ID.""" - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) return await self._init_multipart_upload( @@ -136,7 +129,6 @@ async def get_part_upload_url( Please note: the part number must be a non-zero, positive integer and parts should be uploaded in sequence. """ - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) return await self._get_part_upload_url( @@ -157,7 +149,6 @@ async def abort_multipart_upload( """Cancel a multipart upload with the specified ID. All uploaded content is deleted. """ - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) await self._abort_multipart_upload( @@ -180,7 +171,6 @@ async def complete_multipart_upload( This ensures that exactly the specified number of parts exist and that all parts (except the last one) have the specified size. """ - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) await self._complete_multipart_upload( @@ -198,7 +188,6 @@ async def get_object_download_url( the specified ID (`object_id`) from bucket with the specified id (`bucket_id`). You may also specify a custom expiry duration in seconds (`expires_after`). """ - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) return await self._get_object_download_url( @@ -213,7 +202,6 @@ async def does_object_exist( may be provided to check the objects content. Returns `True` if checks succeed and `False` otherwise. """ - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) return await self._does_object_exist( @@ -221,10 +209,7 @@ async def does_object_exist( ) async def get_object_size(self, *, bucket_id: str, object_id: str) -> int: - """ - Returns the size of an object in bytes. - """ - + """Returns the size of an object in bytes.""" self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) return await self._get_object_size(bucket_id=bucket_id, object_id=object_id) @@ -240,7 +225,6 @@ async def copy_object( """Copy an object from one bucket (`source_bucket_id` and `source_object_id`) to another bucket (`dest_bucket_id` and `dest_object_id`). """ - self._validate_bucket_id(source_bucket_id) self._validate_object_id(source_object_id) self._validate_bucket_id(dest_bucket_id) @@ -256,7 +240,6 @@ async def delete_object(self, *, bucket_id: str, object_id: str) -> None: """Delete an object with the specified id (`object_id`) in the bucket with the specified id (`bucket_id`). """ - self._validate_bucket_id(bucket_id) self._validate_object_id(object_id) await self._delete_object(bucket_id=bucket_id, object_id=object_id) @@ -601,7 +584,8 @@ class MultiPartUploadError(ObjectError): class MultiPartUploadAlreadyExistsError(MultiPartUploadError): """Thrown when trying to create a multipart upload for an object for which another - upload is already active.""" + upload is already active. + """ def __init__(self, bucket_id: str, object_id: str): message = ( diff --git a/hexkit/providers/__init__.py b/src/hexkit/providers/__init__.py similarity index 100% rename from hexkit/providers/__init__.py rename to src/hexkit/providers/__init__.py diff --git a/hexkit/providers/akafka/__init__.py b/src/hexkit/providers/akafka/__init__.py similarity index 89% rename from hexkit/providers/akafka/__init__.py rename to src/hexkit/providers/akafka/__init__.py index 27fa5d5f..216e92d9 100644 --- a/hexkit/providers/akafka/__init__.py +++ b/src/hexkit/providers/akafka/__init__.py @@ -19,7 +19,10 @@ respectively. """ -from .provider import KafkaEventPublisher # noqa: F401 -from .provider import KafkaConfig, KafkaEventSubscriber +from .provider import ( + KafkaConfig, + KafkaEventPublisher, + KafkaEventSubscriber, +) __all__ = ["KafkaEventPublisher", "KafkaEventSubscriber", "KafkaConfig"] diff --git a/hexkit/providers/akafka/provider.py b/src/hexkit/providers/akafka/provider.py similarity index 99% rename from hexkit/providers/akafka/provider.py rename to src/hexkit/providers/akafka/provider.py index a4d663c1..e14c41f3 100644 --- a/hexkit/providers/akafka/provider.py +++ b/src/hexkit/providers/akafka/provider.py @@ -107,7 +107,6 @@ def __init__( value_serializer: Function to serialize the values into bytes. """ - ... async def start(self): @@ -184,7 +183,6 @@ async def _publish_validated( key (str): The event type. ASCII characters only. topic (str): The event type. ASCII characters only. """ - event_headers = [("type", type_.encode("ascii"))] await self._producer.send_and_wait( @@ -217,7 +215,7 @@ def get_event_type(event: ConsumerEvent) -> str: class KafkaConsumerCompatible(Protocol): """A python duck type protocol describing an AIOKafkaConsumer or equivalent.""" - def __init__( + def __init__( # noqa: PLR0913 self, *topics: Ascii, bootstrap_servers: str, @@ -245,7 +243,6 @@ def __init__( value_serializer: Function to deserialize the values into strings. """ - ... async def start(self) -> None: @@ -290,7 +287,6 @@ async def construct( kafka_consumer_cls: Overwrite the used Kafka consumer class. Only intended for unit testing. """ - client_id = generate_client_id( service_name=config.service_name, instance_id=config.service_instance_id ) @@ -328,7 +324,6 @@ def __init__( type annotation) and an application-specific port (according to the triple hexagonal architecture). """ - self._consumer = consumer self._translator = translator self._types_whitelist = translator.types_of_interest @@ -376,7 +371,6 @@ async def run(self, forever: bool = True) -> None: However, you can set `forever` to `False` to make it return after handling one event. """ - if forever: async for event in self._consumer: await self._consume_event(event) diff --git a/hexkit/providers/akafka/testutils.py b/src/hexkit/providers/akafka/testutils.py similarity index 96% rename from hexkit/providers/akafka/testutils.py rename to src/hexkit/providers/akafka/testutils.py index 47367e2b..7d0a3442 100644 --- a/hexkit/providers/akafka/testutils.py +++ b/src/hexkit/providers/akafka/testutils.py @@ -19,10 +19,11 @@ Please note, only use for testing purposes. """ import json +from collections.abc import AsyncGenerator, Sequence from contextlib import asynccontextmanager from dataclasses import dataclass from functools import partial -from typing import AsyncGenerator, Optional, Sequence, Union +from typing import Optional, Union import pytest_asyncio from aiokafka import AIOKafkaConsumer, TopicPartition @@ -75,8 +76,8 @@ def __init__( details: str, ): """Initialize the error with information on the recorded and - expected_events.""" - + expected_events. + """ event_log = ( " Events recorded: " + ", ".join([str(event) for event in recorded_events]) @@ -96,8 +97,8 @@ def check_recorded_events( recorded_events: Sequence[RecordedEvent], expected_events: Sequence[ExpectedEvent] ): """Check a sequence of recorded events against a sequence of expected events. - Raises ValidationError in case of mismatches.""" - + Raises ValidationError in case of mismatches. + """ get_detailed_error = partial( ValidationError, recorded_events=recorded_events, @@ -130,18 +131,21 @@ def get_field_mismatch_error(field, index): class EventRecorder: """Instances of this class can look at at specific topic and check for expected - events occurring with a specified event key.""" + events occurring with a specified event key. + """ class NotStartedError(RuntimeError): """Raised when the recording has not been started yet but is required for the - requested action.""" + requested action. + """ def __init__(self): super().__init__("Event recording has not been started yet.") class InProgressError(RuntimeError): """Raised when the recording is still in progress but need to be stopped for - the rested action.""" + the rested action. + """ def __init__(self): super().__init__( @@ -155,7 +159,6 @@ def __init__( topic: Ascii, ): """Initialize with connection details.""" - self._kafka_servers = kafka_servers self._topic = topic @@ -164,8 +167,8 @@ def __init__( def _assert_recording_stopped(self) -> None: """Assert that the recording has been stopped. Raises an InProgressError or a - NotStartedError otherwise.""" - + NotStartedError otherwise. + """ if self._recorded_events is None: if self._starting_offsets is None: raise self.NotStartedError() @@ -174,7 +177,6 @@ def _assert_recording_stopped(self) -> None: @property def recorded_events(self) -> Sequence[RecordedEvent]: """The recorded events. Only available after the recording has been stopped.""" - self._assert_recording_stopped() return self._recorded_events # type: ignore @@ -183,8 +185,8 @@ async def _get_consumer_offsets( ) -> dict[str, int]: """Returns a dictionary where the keys are partition IDs and the values are the current offsets in the corresponding partitions for the provided consumer. - The provided consumer instance must have been started.""" - + The provided consumer instance must have been started. + """ topic_partitions = [ topic_partition for topic_partition in consumer.assignment() @@ -209,7 +211,6 @@ def _set_consumer_offsets( have been started. """ - if self._starting_offsets is None: raise self.NotStartedError() @@ -221,7 +222,6 @@ def _set_consumer_offsets( def _get_consumer(self) -> AIOKafkaConsumer: """Get an AIOKafkaConsumer.""" - return AIOKafkaConsumer( self._topic, bootstrap_servers=",".join(self._kafka_servers), @@ -239,7 +239,6 @@ async def _count_events_since_start(self, *, consumer: AIOKafkaConsumer) -> int: since the starting offset. Thereby, sum over all partitions. This does not change the offset. The provided consumer instance must have been started. """ - if self._starting_offsets is None: raise self.NotStartedError() @@ -260,16 +259,16 @@ async def _count_events_since_start(self, *, consumer: AIOKafkaConsumer) -> int: @staticmethod async def _consume_event(*, consumer: AIOKafkaConsumer) -> ConsumerEvent: """Consume a single event from a consumer instance and return it. The provided - consumer instance must have been started.""" - + consumer instance must have been started. + """ return await consumer.__anext__() async def _get_events_since_start( self, *, consumer: AIOKafkaConsumer ) -> list[RecordedEvent]: """Consume events since the starting offset. The provided consumer instance must - have been started.""" - + have been started. + """ event_count = await self._count_events_since_start(consumer=consumer) # consume all the available events (but no more, as this would lead to infinite @@ -290,7 +289,6 @@ async def _get_events_since_start( async def start_recording(self) -> None: """Start looking for the expected events from now on.""" - if self._starting_offsets is not None: raise RuntimeError( "Recording has already been started. Cannot restart. Please define a" @@ -307,7 +305,6 @@ async def start_recording(self) -> None: async def stop_recording(self) -> None: """Stop recording and collect the recorded events""" - if self._starting_offsets is None: raise self.NotStartedError() @@ -323,14 +320,13 @@ async def stop_recording(self) -> None: async def __aenter__(self) -> "EventRecorder": """Start recording when entering the context block.""" - await self.start_recording() return self async def __aexit__(self, error_type, error_val, error_tb): """Stop recording and check the recorded events agains the expectation when - exiting the context block.""" - + exiting the context block. + """ await self.stop_recording() @@ -345,7 +341,6 @@ def __init__( publisher: KafkaEventPublisher, ): """Initialize with connection details and a ready-to-use publisher.""" - self.config = config self.kafka_servers = kafka_servers self.publisher = publisher @@ -354,7 +349,6 @@ async def publish_event( self, *, payload: JsonObject, type_: Ascii, topic: Ascii, key: Ascii = "test" ) -> None: """A convenience method to publish a test event.""" - await self.publisher.publish(payload=payload, type_=type_, key=key, topic=topic) def record_events(self, *, in_topic: Ascii) -> EventRecorder: @@ -362,7 +356,6 @@ def record_events(self, *, in_topic: Ascii) -> EventRecorder: record events in the specified topic upon __aenter__ and stops the recording upon __aexit__. """ - return EventRecorder(kafka_servers=self.kafka_servers, topic=in_topic) def delete_topics(self, topics: Optional[Union[str, list[str]]] = None): @@ -370,7 +363,6 @@ def delete_topics(self, topics: Optional[Union[str, list[str]]] = None): Delete given topic(s) from Kafka broker. When no topics are specified, all existing topics will be deleted. """ - admin_client = KafkaAdminClient(bootstrap_servers=self.kafka_servers) all_topics = admin_client.list_topics() if topics is None: @@ -398,7 +390,6 @@ async def expect_events( (on __aenter__) and check that they match the specified sequence of expected events (on __aexit__). """ - async with self.record_events(in_topic=in_topic) as event_recorder: yield event_recorder @@ -412,7 +403,6 @@ async def kafka_fixture_function() -> AsyncGenerator[KafkaFixture, None]: **Do not call directly** Instead, use get_kafka_fixture() """ - with KafkaContainer(image="confluentinc/cp-kafka:5.4.9-1-deb8") as kafka: kafka_servers = [kafka.get_bootstrap_server()] config = KafkaConfig( diff --git a/hexkit/providers/mongodb/__init__.py b/src/hexkit/providers/mongodb/__init__.py similarity index 92% rename from hexkit/providers/mongodb/__init__.py rename to src/hexkit/providers/mongodb/__init__.py index 624d1b21..6fe715c0 100644 --- a/hexkit/providers/mongodb/__init__.py +++ b/src/hexkit/providers/mongodb/__init__.py @@ -18,6 +18,6 @@ and associated utilities. """ -from .provider import MongoDbConfig, MongoDbDaoFactory # noqa: F401 +from .provider import MongoDbConfig, MongoDbDaoFactory __all__ = ["MongoDbConfig", "MongoDbDaoFactory"] diff --git a/hexkit/providers/mongodb/provider.py b/src/hexkit/providers/mongodb/provider.py similarity index 96% rename from hexkit/providers/mongodb/provider.py rename to src/hexkit/providers/mongodb/provider.py index 2d2c652e..563c2259 100644 --- a/hexkit/providers/mongodb/provider.py +++ b/src/hexkit/providers/mongodb/provider.py @@ -19,17 +19,16 @@ Utilities for testing are located in `./testutils.py`. """ +# ruff: noqa: PLR0913 + import json from abc import ABC from collections.abc import AsyncGenerator, AsyncIterator, Collection, Mapping from contextlib import AbstractAsyncContextManager from typing import Any, Generic, Optional, Union, overload -from motor.motor_asyncio import ( - AsyncIOMotorClient, - AsyncIOMotorClientSession, - AsyncIOMotorCollection, -) +from motor.core import AgnosticClientSession, AgnosticCollection +from motor.motor_asyncio import AsyncIOMotorClient from pydantic import BaseSettings, Field, SecretStr from hexkit.protocols.dao import ( @@ -59,8 +58,8 @@ def __init__( *, dto_model: type[Dto], id_field: str, - collection: AsyncIOMotorCollection, - session: Optional[AsyncIOMotorClientSession] = None, + collection: AgnosticCollection, + session: Optional[AgnosticClientSession] = None, ): """Initialize the DAO. @@ -78,7 +77,6 @@ def __init__( is provided, every database operation is immediately commited. Defaults to None. """ - self._collection = collection self._session = session self._dto_model = dto_model @@ -86,15 +84,15 @@ def __init__( def _document_to_dto(self, document: dict[str, Any]) -> Dto: """Converts a document obtained from the MongoDB database into a DTO model- - compliant representation.""" - + compliant representation. + """ document[self._id_field] = document.pop("_id") return self._dto_model(**document) def _dto_to_document(self, dto: Dto) -> dict[str, Any]: """Converts a DTO into a representation that is compatible documents for a - MongoDB Database.""" - + MongoDB Database. + """ document = json.loads(dto.json()) document["_id"] = document.pop(self._id_field) @@ -112,8 +110,10 @@ async def get_by_id(self, id_: str) -> Dto: Raises: ResourceNotFoundError: when resource with the specified id_ was not found """ - - document = await self._collection.find_one({"_id": id_}, session=self._session) + document = await self._collection.find_one( # type: ignore + {"_id": id_}, + session=self._session, + ) if document is None: raise ResourceNotFoundError(id_=id_) @@ -132,7 +132,6 @@ async def update(self, dto: Dto) -> None: ResourceNotFoundError: when resource with the id specified in the dto was not found """ - document = self._dto_to_document(dto) result = await self._collection.replace_one( {"_id": document["_id"]}, document, session=self._session @@ -153,7 +152,6 @@ async def delete(self, *, id_: str) -> None: Raises: ResourceNotFoundError: when resource with the specified id_ was not found """ - result = await self._collection.delete_one({"_id": id_}, session=self._session) if result.deleted_count == 0: @@ -195,7 +193,6 @@ async def find_one(self, *, mapping: Mapping[str, Any]) -> Dto: MultipleHitsFoundError: Raised when obtaining more than one hit. """ - hits = self.find_all(mapping=mapping) try: @@ -223,7 +220,6 @@ async def find_all(self, *, mapping: Mapping[str, Any]) -> AsyncIterator[Dto]: An AsyncIterator of hits. All hits are in the form of the respective DTO model. """ - self._validate_find_mapping(mapping) if self._id_field in mapping: @@ -266,9 +262,9 @@ def __init__( dto_model: type[Dto], dto_creation_model: type[DtoCreation_contra], id_field: str, - collection: AsyncIOMotorCollection, + collection: AgnosticCollection, id_generator: AsyncGenerator[str, None], - session: Optional[AsyncIOMotorClientSession] = None, + session: Optional[AgnosticClientSession] = None, ): """Initialize the DAO. @@ -291,7 +287,6 @@ def __init__( is provided, every database operation is immediately commited. Defaults to None. """ - super().__init__( dto_model=dto_model, id_field=id_field, @@ -313,7 +308,6 @@ async def insert(self, dto: DtoCreation_contra) -> Dto: Returns: Returns a copy of the newly inserted resource including its assigned ID. """ - # complete the provided data with an autogenerated ID: data = dto.dict() data[self._id_field] = await self._id_generator.__anext__() @@ -342,7 +336,6 @@ def with_transaction(cls) -> AbstractAsyncContextManager["DaoNaturalId[Dto]"]: transaction is performed in case of an exception. Otherwise, the changes to the database are committed and flushed. """ - raise NotImplementedError() async def insert(self, dto: Dto) -> None: @@ -357,7 +350,6 @@ async def insert(self, dto: Dto) -> None: ResourceAlreadyExistsError: when a resource with the ID specified in the dto does already exist. """ - document = self._dto_to_document(dto) await self._collection.insert_one(document, session=self._session) @@ -369,7 +361,6 @@ async def upsert(self, dto: Dto) -> None: Resource content as a pydantic-based data transfer object including the resource ID. """ - document = self._dto_to_document(dto) await self._collection.replace_one( {"_id": document["_id"]}, document, session=self._session, upsert=True @@ -379,7 +370,8 @@ async def upsert(self, dto: Dto) -> None: class MongoDbConfig(BaseSettings): """Configuration parameters for connecting to a MongoDB server. - Inherit your config class from this class if your application uses MongoDB.""" + Inherit your config class from this class if your application uses MongoDB. + """ db_connection_str: SecretStr = Field( ..., @@ -410,7 +402,6 @@ def __init__( Args: config: MongoDB-specific config parameters. """ - self._config = config # get a database-specific client: @@ -419,7 +410,7 @@ def __init__( ) self._db = self._client[self._config.db_name] - def __repr__(self) -> str: + def __repr__(self) -> str: # noqa: D105 return f"{self.__class__.__qualname__}(config={repr(self._config)})" @overload @@ -468,7 +459,6 @@ async def _get_dao( from the database server. Thus for compliance with the DaoFactoryProtocol, this method is async. """ - if fields_to_index is not None: raise NotImplementedError( "Indexing on non-ID fields has not been implemented, yet." diff --git a/hexkit/providers/mongodb/testutils.py b/src/hexkit/providers/mongodb/testutils.py similarity index 97% rename from hexkit/providers/mongodb/testutils.py rename to src/hexkit/providers/mongodb/testutils.py index d5c60742..1854339e 100644 --- a/hexkit/providers/mongodb/testutils.py +++ b/src/hexkit/providers/mongodb/testutils.py @@ -18,8 +18,9 @@ Please note, only use for testing purposes. """ +from collections.abc import Generator from dataclasses import dataclass -from typing import Generator, Optional, Union +from typing import Optional, Union import pytest from pymongo import MongoClient @@ -66,7 +67,6 @@ def empty_collections( def config_from_mongodb_container(container: MongoDbContainer) -> MongoDbConfig: """Prepares a MongoDbConfig from an instance of a MongoDbContainer container.""" - db_connection_str = container.get_connection_url() return MongoDbConfig(db_connection_str=db_connection_str, db_name="test") @@ -76,7 +76,6 @@ def mongodb_fixture_function() -> Generator[MongoDbFixture, None, None]: **Do not call directly** Instead, use get_mongodb_fixture() """ - with MongoDbContainer(image="mongo:6.0.3") as mongodb: config = config_from_mongodb_container(mongodb) dao_factory = MongoDbDaoFactory(config=config) diff --git a/hexkit/providers/s3/__init__.py b/src/hexkit/providers/s3/__init__.py similarity index 98% rename from hexkit/providers/s3/__init__.py rename to src/hexkit/providers/s3/__init__.py index 137050ab..a01dc2e3 100644 --- a/hexkit/providers/s3/__init__.py +++ b/src/hexkit/providers/s3/__init__.py @@ -19,6 +19,6 @@ """ # shortcuts: -from hexkit.providers.s3.provider import S3Config, S3ObjectStorage # noqa: F401 +from hexkit.providers.s3.provider import S3Config, S3ObjectStorage __all__ = ["S3Config", "S3ObjectStorage"] diff --git a/hexkit/providers/s3/provider.py b/src/hexkit/providers/s3/provider.py similarity index 96% rename from hexkit/providers/s3/provider.py rename to src/hexkit/providers/s3/provider.py index 5b99ab93..07137770 100644 --- a/hexkit/providers/s3/provider.py +++ b/src/hexkit/providers/s3/provider.py @@ -19,6 +19,8 @@ Utilities for testing are located in `./testutils.py`. """ +# ruff: noqa: PLR0913 + import asyncio from functools import lru_cache from pathlib import Path @@ -107,7 +109,6 @@ def read_aws_config_ini(aws_config_ini: Path) -> botocore.config.Config: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#using-a-configuration-file) and returns an botocore.config.Config object. """ - config_profile = botocore.configloader.load_config(config_filename=aws_config_ini) return botocore.config.Config(**config_profile) @@ -115,9 +116,7 @@ def read_aws_config_ini(aws_config_ini: Path) -> botocore.config.Config: class S3ObjectStorage( ObjectStorageProtocol ): # pylint: disable=too-many-instance-attributes - """ - S3-based provider implementing the ObjectStorageProtocol. - """ + """S3-based provider implementing the ObjectStorageProtocol.""" def __init__( # pylint: disable=too-many-arguments self, @@ -133,7 +132,6 @@ def __init__( # pylint: disable=too-many-arguments Args: config (S3Config): Config parameters specified using the S3Config model. """ - self._config = config self.endpoint_url = config.s3_endpoint_url @@ -173,11 +171,10 @@ def __repr__(self) -> str: @staticmethod def _format_s3_error_code(error_code: str): """Format a message to describe an S3 error code.""" - return f"S3 error with code: '{error_code}'" @classmethod - def _translate_s3_client_errors( # noqa: C901 + def _translate_s3_client_errors( cls, source_exception: botocore.exceptions.ClientError, *, @@ -189,7 +186,6 @@ def _translate_s3_client_errors( # noqa: C901 Translates S3 client errors based on their error codes into exceptions from the ObjectStorageProtocol modules """ - error_code = source_exception.response["Error"]["Code"] exception: Exception @@ -208,27 +204,22 @@ def _translate_s3_client_errors( # noqa: C901 exception = cls.ObjectAlreadyExistsError( bucket_id=bucket_id, object_id=object_id ) - elif error_code == "ObjectAlreadyInActiveTierError": - exception = cls.ObjectAlreadyExistsError( - bucket_id=bucket_id, object_id=object_id - ) elif error_code == "NoSuchUpload": if upload_id is None or bucket_id is None or object_id is None: raise ValueError() exception = cls.MultiPartUploadNotFoundError( upload_id=upload_id, bucket_id=bucket_id, object_id=object_id ) + # exact match not found, match by keyword: + elif "Bucket" in error_code: + exception = cls.BucketError(cls._format_s3_error_code(error_code)) + elif "Object" in error_code or "Key" in error_code: + exception = cls.ObjectError(cls._format_s3_error_code(error_code)) else: - # exact match not found, match by keyword: - if "Bucket" in error_code: - exception = cls.BucketError(cls._format_s3_error_code(error_code)) - elif "Object" in error_code or "Key" in error_code: - exception = cls.ObjectError(cls._format_s3_error_code(error_code)) - else: - # if nothing matches, return a generic error: - exception = cls.ObjectStorageProtocolError( - cls._format_s3_error_code(error_code) - ) + # if nothing matches, return a generic error: + exception = cls.ObjectStorageProtocolError( + cls._format_s3_error_code(error_code) + ) return exception @@ -236,7 +227,6 @@ async def _does_bucket_exist(self, bucket_id: str) -> bool: """Check whether a bucket with the specified ID (`bucket_id`) exists. Return `True` if it exists and `False` otherwise. """ - try: bucket_list = await asyncio.to_thread(self._client.list_buckets) except botocore.exceptions.ClientError as error: @@ -250,7 +240,6 @@ async def _assert_bucket_exists(self, bucket_id: str) -> None: """Checks if the bucket with specified ID (`bucket_id`) exists and throws an BucketNotFoundError otherwise. """ - if not await self.does_bucket_exist(bucket_id): raise self.BucketNotFoundError(bucket_id=bucket_id) @@ -258,7 +247,6 @@ async def _assert_bucket_not_exists(self, bucket_id: str) -> None: """Checks if the bucket with specified ID (`bucket_id`) exists. If so, it throws an BucketAlreadyExistsError. """ - if await self.does_bucket_exist(bucket_id): raise self.BucketAlreadyExistsError(bucket_id=bucket_id) @@ -267,7 +255,6 @@ async def _create_bucket(self, bucket_id: str) -> None: Create a bucket (= a structure that can hold multiple file objects) with the specified unique ID. """ - await self._assert_bucket_not_exists(bucket_id) try: @@ -286,7 +273,6 @@ async def _delete_bucket( will be deleted, if False (the default) a BucketNotEmptyError will be raised if the bucket is not empty. """ - await self._assert_bucket_exists(bucket_id) try: @@ -301,9 +287,7 @@ async def _delete_bucket( ) from error async def _list_all_object_ids(self, *, bucket_id: str) -> list[str]: - """ - Retrieve a list of IDs for all objects currently present in the specified bucket - """ + """Retrieve a list of IDs for all objects currently present in the specified bucket""" await self._assert_bucket_exists(bucket_id) try: @@ -323,7 +307,6 @@ async def _does_object_exist( may be provided to check the objects content. Return `True` if checks succeed and `False` otherwise. """ - if object_md5sum is not None: raise NotImplementedError("Md5 checking is not yet implemented.") @@ -343,7 +326,6 @@ async def _assert_object_exists(self, *, bucket_id: str, object_id: str) -> None the specified ID (`bucket_id`) and throws an ObjectNotFoundError otherwise. If the bucket does not exist it throws a BucketNotFoundError. """ - # first check if bucket exists: await self._assert_bucket_exists(bucket_id) @@ -357,7 +339,6 @@ async def _assert_object_not_exists( the specified ID (`bucket_id`). If so, it throws an ObjectAlreadyExistsError. If the bucket does not exist it throws a BucketNotFoundError. """ - # first check if bucket exists: await self._assert_bucket_exists(bucket_id) @@ -379,7 +360,6 @@ async def _get_object_upload_url( You may also specify a custom expiry duration in seconds (`expires_after`) and a maximum size (bytes) for uploads (`max_upload_size`). """ - await self._assert_object_not_exists(bucket_id=bucket_id, object_id=object_id) conditions = ( @@ -416,7 +396,6 @@ async def _list_multipart_upload_for_object( (S3 allows multiple ongoing multi-part uploads.) """ - try: uploads_info = await asyncio.to_thread( self._client.list_multipart_uploads, @@ -434,7 +413,6 @@ async def _list_multipart_upload_for_object( async def _assert_no_multipart_upload(self, *, bucket_id: str, object_id: str): """Ensure that there are no active multi-part uploads for the given object.""" - upload_ids = await self._list_multipart_upload_for_object( bucket_id=bucket_id, object_id=object_id ) @@ -457,7 +435,6 @@ async def _assert_multipart_upload_exists( By default, also verifies that this upload is the only upload active for that file. Otherwise, raises MultipleActiveUploadsError. """ - upload_ids = await self._list_multipart_upload_for_object( bucket_id=bucket_id, object_id=object_id ) @@ -477,7 +454,6 @@ async def _assert_multipart_upload_exists( async def _init_multipart_upload(self, *, bucket_id: str, object_id: str) -> str: """Initiates a mulipart upload procedure. Returns the upload ID.""" - await self._assert_no_multipart_upload(bucket_id=bucket_id, object_id=object_id) try: @@ -507,7 +483,6 @@ async def _get_part_upload_url( Please note: the part number must be a non-zero, positive integer and parts should be uploaded in sequence. """ - if not 0 < part_number <= self.MAX_FILE_PART_NUMBER: raise ValueError( "The part number must be a non-zero positive integer" @@ -546,7 +521,6 @@ async def _get_parts_info( object_id: str, ) -> dict: """Get information on parts uploaded as part of the specified multi-part upload.""" - await self._assert_multipart_upload_exists( upload_id=upload_id, bucket_id=bucket_id, object_id=object_id ) @@ -585,7 +559,6 @@ async def _check_uploaded_parts( anticipated_part_size: Optional[int] = None, ) -> None: """Check size and quantity of parts""" - # check the part quantity: parts = parts_info.get("Parts") if parts is None or len(parts) == 0: @@ -666,7 +639,6 @@ async def _abort_multipart_upload( """Abort a multipart upload with the specified ID. All uploaded content is deleted. """ - await self._assert_multipart_upload_exists( upload_id=upload_id, bucket_id=bucket_id, @@ -729,7 +701,6 @@ async def _complete_multipart_upload( This ensures that exactly the specified number of parts exist and that all parts (except the last one) have the specified size. """ - parts_info = await self._get_parts_info( upload_id=upload_id, bucket_id=bucket_id, @@ -775,7 +746,6 @@ async def _get_object_download_url( the specified ID (`object_id`) from bucket with the specified id (`bucket_id`). You may also specify a custom expiry duration in seconds (`expires_after`). """ - await self._assert_object_exists(bucket_id=bucket_id, object_id=object_id) try: @@ -793,10 +763,7 @@ async def _get_object_download_url( return presigned_url async def _get_object_size(self, *, bucket_id: str, object_id: str) -> int: - """ - Returns the size of an object in bytes. - """ - + """Returns the size of an object in bytes.""" await self._assert_object_exists(bucket_id=bucket_id, object_id=object_id) object_metadata = await self._get_object_metadata( @@ -814,9 +781,7 @@ async def _get_object_size(self, *, bucket_id: str, object_id: str) -> int: async def _get_object_metadata( self, *, bucket_id: str, object_id: str ) -> dict[str, Any]: - """ - Returns object metadata without downloading the actual object. - """ + """Returns object metadata without downloading the actual object.""" try: metadata = await asyncio.to_thread( self._client.head_object, @@ -871,7 +836,6 @@ async def _delete_object(self, *, bucket_id: str, object_id: str) -> None: """Delete an object with the specified id (`object_id`) in the bucket with the specified id (`bucket_id`). """ - await self._assert_object_exists(bucket_id=bucket_id, object_id=object_id) try: diff --git a/hexkit/providers/s3/test_files/__init__.py b/src/hexkit/providers/s3/test_files/__init__.py similarity index 100% rename from hexkit/providers/s3/test_files/__init__.py rename to src/hexkit/providers/s3/test_files/__init__.py diff --git a/hexkit/providers/s3/test_files/test_file1.yaml b/src/hexkit/providers/s3/test_files/test_file1.yaml similarity index 100% rename from hexkit/providers/s3/test_files/test_file1.yaml rename to src/hexkit/providers/s3/test_files/test_file1.yaml diff --git a/hexkit/providers/s3/test_files/test_file2.yaml b/src/hexkit/providers/s3/test_files/test_file2.yaml similarity index 100% rename from hexkit/providers/s3/test_files/test_file2.yaml rename to src/hexkit/providers/s3/test_files/test_file2.yaml diff --git a/hexkit/providers/s3/test_files/test_file3.yaml b/src/hexkit/providers/s3/test_files/test_file3.yaml similarity index 100% rename from hexkit/providers/s3/test_files/test_file3.yaml rename to src/hexkit/providers/s3/test_files/test_file3.yaml diff --git a/hexkit/providers/s3/test_files/test_file4.yaml b/src/hexkit/providers/s3/test_files/test_file4.yaml similarity index 100% rename from hexkit/providers/s3/test_files/test_file4.yaml rename to src/hexkit/providers/s3/test_files/test_file4.yaml diff --git a/hexkit/providers/s3/testutils.py b/src/hexkit/providers/s3/testutils.py similarity index 94% rename from hexkit/providers/s3/testutils.py rename to src/hexkit/providers/s3/testutils.py index ff5f3b26..eb931b6e 100644 --- a/hexkit/providers/s3/testutils.py +++ b/src/hexkit/providers/s3/testutils.py @@ -18,16 +18,20 @@ Please note, only use for testing purposes. """ + +# ruff: noqa: PLR0913 + import hashlib import os +from collections.abc import Generator from contextlib import contextmanager from pathlib import Path from tempfile import NamedTemporaryFile -from typing import Generator, List, Optional +from typing import Optional import pytest import requests -from pydantic import BaseModel, validator +from pydantic import BaseModel, SecretStr, validator from testcontainers.localstack import LocalStackContainer from hexkit.custom_types import PytestScope @@ -47,11 +51,8 @@ def calc_md5(content: bytes) -> str: - """ - Calc the md5 checksum for the specified bytes. - """ - - return hashlib.md5(content).hexdigest() # nosec + """Calc the md5 checksum for the specified bytes.""" + return hashlib.md5(content, usedforsecurity=False).hexdigest() # nosec class FileObject(BaseModel): @@ -65,17 +66,15 @@ class FileObject(BaseModel): # pylint: disable=no-self-argument @validator("content", always=True) - def read_content(cls, _, values): + def read_content(cls, _, values): # noqa: N805 """Read in the file content.""" - with open(values["file_path"], "rb") as file: return file.read() # pylint: disable=no-self-argument @validator("md5", always=True) - def calc_md5_from_content(cls, _, values): + def calc_md5_from_content(cls, _, values): # noqa: N805 """Calculate md5 based on the content.""" - return calc_md5(values["content"]) @@ -106,7 +105,6 @@ async def empty_buckets(self, buckets_to_exclude: Optional[list[str]] = None): async def populate_buckets(self, buckets: list[str]): """Populate the storage with buckets.""" - await populate_storage( self.storage, bucket_fixtures=buckets, object_fixtures=[] ) @@ -115,7 +113,6 @@ async def populate_buckets(self, buckets: list[str]): async def populate_file_objects(self, file_objects: list[FileObject]): """Populate the storage with file objects.""" - await populate_storage( self.storage, bucket_fixtures=[], object_fixtures=file_objects ) @@ -126,7 +123,6 @@ def s3_fixture_function() -> Generator[S3Fixture, None, None]: **Do not call directly** Instead, use get_s3_fixture() """ - with LocalStackContainer(image="localstack/localstack:0.14.5").with_services( "s3" ) as localstack: @@ -151,7 +147,6 @@ def temp_file_object( size: int = 5 * MEBIBYTE, ) -> Generator[FileObject, None, None]: """Generates a file object with the specified size in bytes.""" - chunk_size = 1024 chunk = b"\0" * chunk_size current_size = 0 @@ -172,14 +167,12 @@ def temp_file_object( @pytest.fixture def file_fixture(): """A fixture that provides a temporary file.""" - with temp_file_object() as temp_file: yield temp_file def upload_file(presigned_url: PresignedPostURL, file_path: Path, file_md5: str): """Uploads the test file to the specified URL""" - with open(file_path, "rb") as test_file: files = {"file": (str(file_path), test_file)} headers = {"ContentMD5": file_md5} @@ -195,8 +188,8 @@ def upload_file(presigned_url: PresignedPostURL, file_path: Path, file_md5: str) def check_part_size(file_path: Path, anticipated_size: int) -> None: """Check if the anticipated part size can be used to upload the specified file - using the maximum number of file parts. Raises an exception otherwise.""" - + using the maximum number of file parts. Raises an exception otherwise. + """ file_size = os.path.getsize(file_path) if file_size / anticipated_size > ObjectStorageProtocol.MAX_FILE_PART_NUMBER: raise RuntimeError( @@ -217,7 +210,6 @@ async def upload_part( part_number: int = 1, ): """Upload the specified content as part to an initialized multipart upload.""" - upload_url = await storage_dao.get_part_upload_url( upload_id=upload_id, bucket_id=bucket_id, @@ -241,7 +233,6 @@ async def upload_part_of_size( Generate a bytes object of the specified size and uploads the part to an initialized multipart upload. """ - content = b"\0" * size await upload_part( storage_dao=storage_dao, @@ -255,7 +246,6 @@ async def upload_part_of_size( def upload_part_via_url(*, url: str, size: int): """Upload a file part of given size using the given URL.""" - content = b"\0" * size response = requests.put(url, data=content, timeout=TIMEOUT) response.raise_for_status() @@ -269,7 +259,6 @@ async def multipart_upload_file( part_size: int = ObjectStorageProtocol.DEFAULT_PART_SIZE, ) -> None: """Uploads the test file to the specified URL""" - check_part_size(file_path=file_path, anticipated_size=part_size) print(f" - initiate multipart upload for test object {object_id}") @@ -306,24 +295,22 @@ async def multipart_upload_file( def download_and_check_test_file(presigned_url: str, expected_md5: str): """Download the test file from the specified URL and check its integrity (md5).""" - response = requests.get(presigned_url, timeout=TIMEOUT) response.raise_for_status() observed_md5 = calc_md5(response.content) - assert ( # nosec + assert ( # noqa: S101 observed_md5 == expected_md5 ), "downloaded file has unexpected md5 checksum" async def populate_storage( storage: ObjectStorageProtocol, - bucket_fixtures: List[str], - object_fixtures: List[FileObject], + bucket_fixtures: list[str], + object_fixtures: list[FileObject], ): """Populate Storage with object and bucket fixtures""" - for bucket_fixture in bucket_fixtures: await storage.create_bucket(bucket_fixture) @@ -344,18 +331,16 @@ async def populate_storage( def config_from_localstack_container(container: LocalStackContainer) -> S3Config: """Prepares a S3Config from an instance of a localstack test container.""" - s3_endpoint_url = container.get_url() - return S3Config( # nosec + return S3Config( # type: ignore[call-arg] s3_endpoint_url=s3_endpoint_url, s3_access_key_id="test", - s3_secret_access_key="test", + s3_secret_access_key=SecretStr("test"), ) async def get_initialized_upload(s3_fixture_: S3Fixture): """Initialize a new empty multipart upload.""" - bucket_id = "mybucketwithupload001" object_id = "myobjecttobeuploaded001" @@ -370,7 +355,6 @@ async def get_initialized_upload(s3_fixture_: S3Fixture): async def prepare_non_completed_upload(s3_fixture_: S3Fixture): """Prepare an upload that has not been marked as completed, yet.""" - upload_id, bucket_id, object_id = await get_initialized_upload(s3_fixture_) with temp_file_object() as file: @@ -399,17 +383,14 @@ async def typical_workflow( use_multipart_upload: bool = True, part_size: int = ObjectStorageProtocol.DEFAULT_PART_SIZE, ): - """ - Run a typical workflow of basic object operations using a S3 service. - """ - + """Run a typical workflow of basic object operations using a S3 service.""" print("Run a workflow for testing basic object operations using a S3 service:") print(f" - create new bucket {bucket1_id}") await storage_client.create_bucket(bucket1_id) print(" - confirm bucket creation") - assert await storage_client.does_bucket_exist(bucket1_id) # nosec + assert await storage_client.does_bucket_exist(bucket1_id) # noqa: S101 if use_multipart_upload: await multipart_upload_file( @@ -429,7 +410,7 @@ async def typical_workflow( ) print(" - confirm object upload") - assert await storage_client.does_object_exist( # nosec + assert await storage_client.does_object_exist( # noqa: S101 bucket_id=bucket1_id, object_id=object_id ) @@ -452,10 +433,10 @@ async def typical_workflow( await storage_client.delete_object(bucket_id=bucket1_id, object_id=object_id) print(" - confirm move") - assert not await storage_client.does_object_exist( # nosec + assert not await storage_client.does_object_exist( # noqa: S101 bucket_id=bucket1_id, object_id=object_id ) - assert await storage_client.does_object_exist( # nosec + assert await storage_client.does_object_exist( # noqa: S101 bucket_id=bucket2_id, object_id=object_id ) @@ -463,7 +444,7 @@ async def typical_workflow( await storage_client.delete_bucket(bucket1_id) print(" - confirm bucket deletion") - assert not await storage_client.does_bucket_exist(bucket1_id) # nosec + assert not await storage_client.does_bucket_exist(bucket1_id) # noqa: S101 print(f" - download object from bucket {bucket2_id}") download_url2 = await storage_client.get_object_download_url( diff --git a/hexkit/providers/testing/__init__.py b/src/hexkit/providers/testing/__init__.py similarity index 100% rename from hexkit/providers/testing/__init__.py rename to src/hexkit/providers/testing/__init__.py diff --git a/hexkit/providers/testing/eventpub.py b/src/hexkit/providers/testing/eventpub.py similarity index 96% rename from hexkit/providers/testing/eventpub.py rename to src/hexkit/providers/testing/eventpub.py index cc37efdd..4d505956 100644 --- a/hexkit/providers/testing/eventpub.py +++ b/src/hexkit/providers/testing/eventpub.py @@ -53,12 +53,10 @@ def __init__(self): def post(self, topic: str, event: Event) -> None: """Queue a new event to a topic.""" - self.topics[topic].append(event) def get(self, topic) -> Event: """Get the next element in the queue corresponding to the specified topic.""" - try: return self.topics[topic].popleft() except IndexError as error: @@ -73,9 +71,7 @@ class InMemEventPublisher(EventPublisherProtocol): """ def __init__(self, event_store: Optional[InMemEventStore] = None): - """ - Initialize with existing event_store or let it create a new one. - """ + """Initialize with existing event_store or let it create a new one.""" self.event_store = event_store if event_store else InMemEventStore() async def _publish_validated( diff --git a/hexkit/providers/testing/utils.py b/src/hexkit/providers/testing/utils.py similarity index 100% rename from hexkit/providers/testing/utils.py rename to src/hexkit/providers/testing/utils.py diff --git a/hexkit/utils.py b/src/hexkit/utils.py similarity index 97% rename from hexkit/utils.py rename to src/hexkit/utils.py index a1f5e099..75d3385c 100644 --- a/hexkit/utils.py +++ b/src/hexkit/utils.py @@ -62,7 +62,6 @@ def calc_part_size(*, file_size: int, preferred_part_size: Optional[int] = None) Raises: ValueError if file size exceeds the maximum of 5 TiB """ - if preferred_part_size is None: preferred_part_size = 8 * 1024**2 @@ -91,7 +90,8 @@ def calc_part_size(*, file_size: int, preferred_part_size: Optional[int] = None) def check_ascii(*str_values: str): """Checks that the provided `str_values` are ASCII compatible, - raises an exception otherwise.""" + raises an exception otherwise. + """ for str_value in str_values: if not str_value.isascii(): raise NonAsciiStrError(str_value=str_value) @@ -103,8 +103,8 @@ def validate_fields_in_model( fields: Collection[str], ) -> None: """Checks that all provided fields are present in the dto_model. - Raises IndexFieldsInvalidError otherwise.""" - + Raises IndexFieldsInvalidError otherwise. + """ fields_set = set(fields) existing_fields_set = set(model.schema()["properties"]) diff --git a/tests/fixtures/config.py b/tests/fixtures/config.py index 8ffb2e6c..3f6f237d 100644 --- a/tests/fixtures/config.py +++ b/tests/fixtures/config.py @@ -20,7 +20,7 @@ import re from dataclasses import dataclass from pathlib import Path -from typing import Any, Dict +from typing import Any from pydantic import BaseModel, BaseSettings @@ -37,7 +37,7 @@ class ConfigYamlFixture(BaseModel): """Container for config yaml fixtures""" path: Path - content: Dict[str, Any] + content: dict[str, Any] def read_config_yaml(name: str): @@ -61,14 +61,16 @@ class EnvVarFixture: """Container for env var set. This class can be used as context manager so that the env vars are available within a with block but, after leaving the with block, - the original enviroment is restored.""" + the original enviroment is restored. + """ - env_vars: Dict[str, str] + env_vars: dict[str, str] prefix: str = DEFAULT_CONFIG_PREFIX def __enter__(self): - """makes a backup of the environment and set the - env_vars""" + """Makes a backup of the environment and set the + env_vars + """ # pylint: disable=attribute-defined-outside-init self.env_backup = copy.deepcopy(os.environ) @@ -76,11 +78,12 @@ def __enter__(self): os.environ[f"{self.prefix}_{name}"] = value def __exit__(self, exc_type, exc_val, exc_tb): - """restores the original environment""" - os.environ = self.env_backup + """Restores the original environment""" + os.environ.clear() + os.environ = self.env_backup # noqa: B003 -def read_env_var_sets() -> Dict[str, EnvVarFixture]: +def read_env_var_sets() -> dict[str, EnvVarFixture]: """Read env vars sets and return a list of EnvVarFixtures.""" env_var_dict = utils.read_yaml(BASE_DIR / "config_env_var_sets.yaml") diff --git a/tests/fixtures/dummy_joint.py b/tests/fixtures/dummy_joint.py index 0a97b183..b3186da1 100644 --- a/tests/fixtures/dummy_joint.py +++ b/tests/fixtures/dummy_joint.py @@ -15,8 +15,8 @@ # """Simple joint fixture for testing the event loop fixture override's impact""" +from collections.abc import AsyncGenerator from dataclasses import dataclass -from typing import AsyncGenerator import pytest_asyncio @@ -35,4 +35,5 @@ class JointFixture: @pytest_asyncio.fixture(scope="module") async def joint_fixture(s3_fixture) -> AsyncGenerator[JointFixture, None]: + """Simple joint fixture only wrapping S3 fixture""" yield JointFixture(s3_fixture=s3_fixture) diff --git a/tests/fixtures/inject.py b/tests/fixtures/inject.py index 94482eed..25df86bf 100644 --- a/tests/fixtures/inject.py +++ b/tests/fixtures/inject.py @@ -24,9 +24,7 @@ class ValidResource(dependency_injector.resources.AsyncResource): - """ - An example of an AsyncResource as defined by the `dependency_injector` framework. - """ + """An example of an AsyncResource as defined by the `dependency_injector` framework.""" class Resource: """Returned upon executing the `init` method.""" @@ -41,13 +39,12 @@ async def init(self, foo: str = "foo") -> Resource: return self.Resource(foo=foo) async def shutdown(self, resource: Resource) -> None: # type: ignore + """Cleanup async resource""" resource.in_context = False class ValidSyncResource(dependency_injector.resources.Resource): - """ - An example of an ordinary Resource as defined by the `dependency_injector` framework. - """ + """An example of an ordinary Resource as defined by the `dependency_injector` framework.""" class Resource: """Returned upon executing the `init` method.""" @@ -62,6 +59,7 @@ def init(self, foo: str = "foo") -> Resource: return self.Resource(foo=foo) def shutdown(self, resource: Resource) -> None: # type: ignore + """Cleanup async resource""" resource.in_context = False diff --git a/tests/fixtures/utils.py b/tests/fixtures/utils.py index 2fc9b802..3e84e3f5 100644 --- a/tests/fixtures/utils.py +++ b/tests/fixtures/utils.py @@ -31,5 +31,5 @@ def read_yaml(path: Path) -> dict: """Read yaml file and return content as dict.""" - with open(path, "r", encoding="UTF-8") as file: + with open(path, encoding="UTF-8") as file: return yaml.safe_load(file) diff --git a/tests/integration/test_akafka_testutils.py b/tests/integration/test_akafka_testutils.py index c7daaf16..b504205d 100644 --- a/tests/integration/test_akafka_testutils.py +++ b/tests/integration/test_akafka_testutils.py @@ -15,18 +15,19 @@ # """Testing of the Kafka testutils.""" -from typing import Sequence +from collections.abc import Sequence import pytest from kafka import KafkaAdminClient +from kafka.admin.new_topic import NewTopic from hexkit.providers.akafka import KafkaConfig, KafkaEventPublisher -from hexkit.providers.akafka.testutils import kafka_fixture # noqa: F401 from hexkit.providers.akafka.testutils import ( ExpectedEvent, KafkaFixture, RecordedEvent, ValidationError, + kafka_fixture, # noqa: F401 ) @@ -35,18 +36,23 @@ def test_delete_topics_specific(kafka_fixture: KafkaFixture): # noqa: F811 admin_client = KafkaAdminClient( bootstrap_servers=kafka_fixture.config.kafka_servers ) + new_topics = [ + NewTopic(name="test", num_partitions=1, replication_factor=1), + NewTopic(name="test2", num_partitions=1, replication_factor=1), + ] + admin_client.create_topics(new_topics) - # get the topics created by default (confluent.support.metrics) initial_topics = admin_client.list_topics() - initial_length = len(initial_topics) - assert initial_length > 0 + assert "test" in initial_topics # delete that topic - kafka_fixture.delete_topics(initial_topics) + kafka_fixture.delete_topics("test") # make sure it got deleted - assert len(admin_client.list_topics()) + 1 == initial_length + final_topics = admin_client.list_topics() + assert "test" not in final_topics + assert "test2" in final_topics def test_delete_topics_all(kafka_fixture: KafkaFixture): # noqa: F811 @@ -55,12 +61,21 @@ def test_delete_topics_all(kafka_fixture: KafkaFixture): # noqa: F811 bootstrap_servers=kafka_fixture.config.kafka_servers ) - assert len(admin_client.list_topics()) > 0 + new_topics = [ + NewTopic(name="test", num_partitions=1, replication_factor=1), + NewTopic(name="test2", num_partitions=1, replication_factor=1), + ] + admin_client.create_topics(new_topics) + initial_topics = admin_client.list_topics() + assert "test" in initial_topics + assert "test2" in initial_topics # delete all topics by not specifying any kafka_fixture.delete_topics() - assert len(admin_client.list_topics()) == 0 + final_topics = admin_client.list_topics() + assert "test" not in final_topics + assert "test2" not in final_topics @pytest.mark.asyncio @@ -83,7 +98,6 @@ async def test_event_recorder( kafka_fixture: KafkaFixture, # noqa: F811 ): """Test event recording using the EventRecorder class.""" - topic = "test_topic" config = KafkaConfig( @@ -112,7 +126,6 @@ async def test_expect_events_happy( """Test successful validation of recorded events with the expect_events method of the KafkaFixture. """ - expected_events = [ ExpectedEvent( payload={"test_content": "Hello"}, type_="test_hello", key="test_key" @@ -219,7 +232,6 @@ async def test_expect_events_mismatch( the methods `start_recording` and `stop_and_check` so that we can nicely locate where the ValidationError is thrown. """ - expected_events = [ ExpectedEvent( payload={"test_content": "Hello"}, type_="test_hello", key="test_key" diff --git a/tests/integration/test_inject.py b/tests/integration/test_inject.py index cd7746c2..d9dd19b0 100644 --- a/tests/integration/test_inject.py +++ b/tests/integration/test_inject.py @@ -44,7 +44,6 @@ async def test_context_constructor_with_decl_container(): Test the context constructor together with the `DeclarativeContainer` from the `dependency_injector` framework. """ - foo = "bar" class Container(dependency_injector.containers.DeclarativeContainer): @@ -75,10 +74,7 @@ class Container(dependency_injector.containers.DeclarativeContainer): ], ) async def test_container_base(provides, constructor, has_context: bool): - """ - Test the ContainerBase and its contextual setup and teardown functionality. - """ - + """Test the ContainerBase and its contextual setup and teardown functionality.""" foo = "bar" class Container(ContainerBase): @@ -99,7 +95,8 @@ class Container(ContainerBase): @pytest.mark.asyncio async def test_container_base_sync_resouce(): """Make sure that using a non async Resource with the ContainerBase results in an - exception.""" + exception. + """ class Container(ContainerBase): test = dependency_injector.providers.Resource(ValidSyncResource, "bar") @@ -138,24 +135,26 @@ class ExampleConfig(BaseSettings): class SyncConfigConsumer: """A class that consumes an entire ExampleConfig instance (and not just - individual parameters).""" + individual parameters). + """ def __init__(self, *, config: ExampleConfig): """Takes an ExampleConfig instance and checks their values against the - expectation.""" - + expectation. + """ self.config = config class AsyncConfigConsumer(SyncConfigConsumer): """A class that consumes an entire ExampleConfig instance (and not just - individual parameters). Is constucted using an async context manager.""" + individual parameters). Is constucted using an async context manager. + """ @classmethod @asynccontextmanager async def construct(cls, *, config: ExampleConfig): """A constructor with setup and teardown logic. - Just there so that we can use the container as an async context manager.""" - + Just there so that we can use the container as an async context manager. + """ yield cls(config=config) class Container(ContainerBase): diff --git a/tests/integration/test_mongodb.py b/tests/integration/test_mongodb.py index d5e51bf7..fc4ca583 100644 --- a/tests/integration/test_mongodb.py +++ b/tests/integration/test_mongodb.py @@ -44,6 +44,8 @@ class ExampleCreationDto(BaseModel): field_c: bool class Config: + """Additional config options for model""" + frozen = True @@ -56,7 +58,6 @@ class ExampleDto(ExampleCreationDto): @pytest.mark.asyncio async def test_dao_find_all_with_id(mongodb_fixture: MongoDbFixture): # noqa: F811 """Test using the id field as part of the mapping in find_all()""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -110,7 +111,6 @@ async def test_dao_find_all_without_collection( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Test calling find_all() when there is no collection.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="does-not-exist-at-all", dto_model=ExampleDto, @@ -140,8 +140,8 @@ async def test_empty_collections(mongodb_fixture: MongoDbFixture): # noqa: F811 @pytest.mark.asyncio async def test_dao_happy(mongodb_fixture: MongoDbFixture): # noqa: F811 """Test the happy path of performing basic CRUD database interactions using - the MongoDbDaoFactory in a surrograte ID setting.""" - + the MongoDbDaoFactory in a surrograte ID setting. + """ dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -205,7 +205,6 @@ async def test_dao_insert_natural_id_happy( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests the happy path of inserting a new resource in a natural ID setting.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -225,8 +224,8 @@ async def test_dao_upsert_natural_id_happy( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests the happy path of upserting new and existing resources in a natural ID - setting.""" - + setting. + """ dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -254,7 +253,6 @@ async def test_dao_get_not_found( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests getting a non existing resource via its ID.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -270,7 +268,6 @@ async def test_dao_update_not_found( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests updating a non existing resource.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -290,7 +287,6 @@ async def test_dao_delete_not_found( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests deleting a non existing resource via its ID.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -306,7 +302,6 @@ async def test_dao_find_invalid_mapping( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests find_one and find_all methods with an invalid mapping.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -326,7 +321,6 @@ async def test_dao_find_no_hits( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests find_one and find_all methods with a mapping that results in no hits.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -346,7 +340,6 @@ async def test_dao_find_one_with_multiple_hits( mongodb_fixture: MongoDbFixture, # noqa: F811 ): """Tests find_one with a mapping that results in multiple hits.""" - dao = await mongodb_fixture.dao_factory.get_dao( name="example", dto_model=ExampleDto, @@ -375,8 +368,8 @@ async def prefixed_count_id_generator( prefix: str, count_offset: int = 1 ) -> AsyncGenerator[str, None]: """A generator that yields IDs by counting upwards und prefixing that counts - with a predefined string.""" - + with a predefined string. + """ for count in itertools.count(start=count_offset): yield f"{prefix}-{count}" diff --git a/tests/integration/test_s3.py b/tests/integration/test_s3.py index dc9a48d8..47ea64c9 100644 --- a/tests/integration/test_s3.py +++ b/tests/integration/test_s3.py @@ -13,11 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -""" -Test S3 storage DAO -""" +"""Test S3 storage DAO""" -from typing import ContextManager, Optional +from contextlib import AbstractContextManager +from typing import Optional import pytest from black import nullcontext @@ -48,7 +47,6 @@ async def test_empty_buckets( s3_fixture: S3Fixture, file_fixture: FileObject # noqa: F811 ): """Make sure the empty_buckets function works""" - # bucket should not exist in the beginning: bucket_id = file_fixture.bucket_id assert not await s3_fixture.storage.does_bucket_exist(bucket_id=bucket_id) @@ -78,10 +76,7 @@ async def test_typical_workflow( use_multipart_upload: bool, s3_fixture: S3Fixture, # noqa: F811 ): - """ - Tests all methods of the ObjectStorageS3 DAO implementation in one long workflow. - """ - + """Tests all methods of the ObjectStorageS3 DAO implementation in one long workflow.""" with temp_file_object(size=20 * MEBIBYTE) as file: await typical_workflow( storage_client=s3_fixture.storage, @@ -100,7 +95,6 @@ async def test_object_existence_checks( file_fixture: FileObject, # noqa: F811 ): """Test if the checks for existence of objects work correctly.""" - # object should not exist in the beginning: assert not await s3_fixture.storage.does_object_exist( bucket_id=file_fixture.bucket_id, object_id=file_fixture.object_id @@ -121,7 +115,6 @@ async def test_get_object_size( file_fixture: FileObject, # noqa: F811 ): """Test if the get_object_size method returns the correct size.""" - expected_size = len(file_fixture.content) await s3_fixture.populate_file_objects([file_fixture]) @@ -138,7 +131,6 @@ async def test_list_all_object_ids( file_fixture: FileObject, # noqa: F811 ): """Test if listing all object IDs for a bucket works correctly.""" - file_fixture2 = file_fixture.copy(deep=True) file_fixture2.object_id = "mydefaulttestobject002" @@ -155,7 +147,6 @@ async def test_list_all_object_ids( @pytest.mark.asyncio async def test_bucket_existence_checks(s3_fixture: S3Fixture): # noqa: F811 """Test if the checks for existence of buckets work correctly.""" - bucket_id = EXAMPLE_BUCKETS[0] # bucket should not exist in the beginning: @@ -176,7 +167,6 @@ async def test_object_and_bucket_collisions( Tests whether overwriting (re-creation, re-upload, or copy to existing object) fails with the expected error. """ - await s3_fixture.populate_file_objects([file_fixture]) with pytest.raises(ObjectStorageProtocol.BucketAlreadyExistsError): @@ -207,7 +197,6 @@ async def test_handling_non_existing_file_and_bucket( Tests whether interacting with a non-existing bucket/file object fails with the expected result. """ - non_existing_bucket_id = "mynonexistingbucket001" non_existing_object_id = "mynonexistingobject001" existing_bucket_id = file_fixture.bucket_id @@ -278,10 +267,9 @@ async def test_delete_non_empty_bucket( file_fixture: FileObject, # noqa: F811 ): """Test deleting an non-empty bucket.""" - await s3_fixture.populate_file_objects([file_fixture]) - with nullcontext() if delete_content else pytest.raises( # type: ignore + with nullcontext() if delete_content else pytest.raises( ObjectStorageProtocol.BucketNotEmptyError ): await s3_fixture.storage.delete_bucket( @@ -309,7 +297,6 @@ async def test_using_non_existing_upload( Makes sure that using a non existing upload_id-bucket_id-object_id combination throws the right error. """ - # prepare a non-completed upload: real_upload_id, real_bucket_id, real_object_id = await prepare_non_completed_upload( s3_fixture @@ -319,8 +306,8 @@ async def test_using_non_existing_upload( bucket_id = real_bucket_id if bucket_id_correct else "wrong-bucket" object_id = real_object_id if object_id_correct else "wrong-object" - def get_exception_context() -> ContextManager: - return pytest.raises(exception) if exception else nullcontext() # type: ignore + def get_exception_context() -> AbstractContextManager: + return pytest.raises(exception) if exception else nullcontext() # call relevant methods from the provider: with pytest.raises(exception): @@ -350,7 +337,6 @@ async def test_invalid_part_number( s3_fixture: S3Fixture, # noqa: F811 ): """Check that invalid part numbers are cached correctly.""" - upload_id, bucket_id, object_id = await prepare_non_completed_upload(s3_fixture) with pytest.raises(exception) if exception else nullcontext(): # type: ignore @@ -413,10 +399,7 @@ async def test_complete_multipart_upload( exception: Optional[Exception], s3_fixture: S3Fixture, # noqa: F811 ): - """ - Test the complete_multipart_upload method. - """ - + """Test the complete_multipart_upload method.""" upload_id, bucket_id, object_id = await get_initialized_upload(s3_fixture) for part_idx, part_size in enumerate(part_sizes): await upload_part_of_size( @@ -444,10 +427,7 @@ async def test_abort_multipart_upload( empty_upload: bool, s3_fixture: S3Fixture, # noqa: F811 ): - """ - Test the abort_multipart_upload method. - """ - + """Test the abort_multipart_upload method.""" upload_id, bucket_id, object_id = await get_initialized_upload(s3_fixture) async def upload_part_shortcut(part_number): @@ -482,10 +462,7 @@ async def upload_part_shortcut(part_number): @pytest.mark.asyncio async def test_multiple_active_uploads(s3_fixture: S3Fixture): # noqa: F811 - """ - Test that multiple active uploads for the same object are not possible. - """ - + """Test that multiple active uploads for the same object are not possible.""" # initialize an upload: _, bucket_id, object_id = await get_initialized_upload(s3_fixture) @@ -504,7 +481,6 @@ async def test_handling_multiple_coexisting_uploads( Test that the invalid state of multiple uploads coexisting for the same object is correctly handled. """ - # initialize an upload: upload1_id, bucket_id, object_id = await get_initialized_upload(s3_fixture) @@ -560,7 +536,6 @@ async def test_handling_multiple_subsequent_uploads( 2. initiate an upload, upload some parts, complete it, then start another upload, uploads some parts, complete it (`abort_first` set to False) """ - # perform first upload: upload1_id, bucket_id, object_id = await get_initialized_upload(s3_fixture) diff --git a/tests/unit/test_akafka.py b/tests/unit/test_akafka.py index 4ebeec50..354cdff5 100644 --- a/tests/unit/test_akafka.py +++ b/tests/unit/test_akafka.py @@ -177,7 +177,7 @@ async def test_kafka_event_subscriber( assert callable(cc_kwargs["value_deserializer"]) # consume one event: - with pytest.raises(exception) if exception else nullcontext(): # type: ignore + with pytest.raises(exception) if exception else nullcontext(): await event_subscriber.run(forever=False) # check if producer was correctly started and stopped: diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 394c3430..f5a63e3a 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -26,8 +26,8 @@ def test_config_from_yaml(): """Test that config yaml correctly overwrites - default parameters""" - + default parameters + """ config_yaml = config_yamls["basic"] # update config class with content of config yaml @@ -41,7 +41,8 @@ def test_config_from_yaml(): def test_config_from_env(): """Test that env vars correctly overwrites - default parameters""" + default parameters + """ env_var_fixture = env_var_sets["basic_complete"] with env_var_fixture: # update config class with content of config yaml and @@ -50,14 +51,14 @@ def test_config_from_env(): config = config_constructor() # compare to expected content: - expected = BasicConfig(**env_var_fixture.env_vars) + expected = BasicConfig(**env_var_fixture.env_vars) # type: ignore assert config.dict() == expected def test_config_from_yaml_and_env(): """Test that config yaml and env vars correctly overwrites - default parameters""" - + default parameters + """ config_yaml = config_yamls["basic"] env_var_fixture = env_var_sets["basic_partly"] @@ -76,7 +77,6 @@ def test_config_from_yaml_and_env(): @pytest.mark.parametrize("cwd", [True, False]) def test_config_from_default_yaml(cwd: bool): """Test that default config yaml from home is correctly read""" - base_dir = Path(os.getcwd()) if cwd else Path.home() prefix = "test_prefix" @@ -99,8 +99,8 @@ def test_config_from_default_yaml(cwd: bool): def test_config_from_default_yaml_via_env(): """Test that default config yaml specified via an environment variable is correctly - read""" - + read + """ prefix = "test_prefix" # set env var: diff --git a/tests/unit/test_dao.py b/tests/unit/test_dao.py index 0d5525ee..79c2994d 100644 --- a/tests/unit/test_dao.py +++ b/tests/unit/test_dao.py @@ -71,12 +71,8 @@ async def _get_dao( id_generator: AsyncGenerator[str, None], ) -> Union[DaoSurrogateId[Dto, DtoCreation], DaoNaturalId[Dto]]: """*To be implemented by the provider. Input validation is done outside of this - method.*""" - ... - - """*To be implemented by the provider. Input validation is done outside of this - method.*""" - + method.* + """ raise NotImplementedError() @@ -89,7 +85,8 @@ class ExampleCreationDto(BaseModel): class ExampleInvalidCreationDto(ExampleCreationDto): """Example for a DTO creation model that is invalid because it contains a - parameter that the main DTO model is missing.""" + parameter that the main DTO model is missing. + """ unexpected_param: str @@ -103,7 +100,6 @@ class ExampleDto(ExampleCreationDto): @pytest.mark.asyncio async def test_get_dto_valid(): """Use the get_dao method of the DaoFactory with valid parameters.""" - dao_factory = FakeDaoFactory() with pytest.raises(NotImplementedError): @@ -119,8 +115,8 @@ async def test_get_dto_valid(): @pytest.mark.asyncio async def test_get_dto_invalid_id(): """Use the get_dao method of the DaoFactory with an invalid ID that is not found in - the provided DTO model.""" - + the provided DTO model. + """ dao_factory = FakeDaoFactory() with pytest.raises(DaoFactoryProtocol.IdFieldNotFoundError): @@ -136,7 +132,6 @@ async def test_get_dto_invalid_id(): ) async def test_get_dto_invalid_creation_model(dto_creation_model: type[BaseModel]): """Use the get_dao method of the DaoFactory with an invalid creation model.""" - dao_factory = FakeDaoFactory() with pytest.raises(DaoFactoryProtocol.CreationModelInvalidError): @@ -151,7 +146,6 @@ async def test_get_dto_invalid_creation_model(dto_creation_model: type[BaseModel @pytest.mark.asyncio async def test_get_dto_invalid_fields_to_index(): """Use the get_dao method of the DaoFactory with an invalid list of fields to index.""" - dao_factory = FakeDaoFactory() with pytest.raises(DaoFactoryProtocol.IndexFieldsInvalidError): diff --git a/tests/unit/test_eventpub.py b/tests/unit/test_eventpub.py index 09ce5067..1da5654a 100644 --- a/tests/unit/test_eventpub.py +++ b/tests/unit/test_eventpub.py @@ -67,7 +67,7 @@ async def test_ascii_val(type_, key, topic, exception): event_publisher = FakePublisher() # publish event using the provider: - with pytest.raises(exception) if exception else nullcontext(): # type: ignore + with pytest.raises(exception) if exception else nullcontext(): await event_publisher.publish( payload=payload, type_=type_, diff --git a/tests/unit/test_eventsub.py b/tests/unit/test_eventsub.py index fbd19dbb..0e2204ce 100644 --- a/tests/unit/test_eventsub.py +++ b/tests/unit/test_eventsub.py @@ -59,7 +59,7 @@ async def test_ascii_val(type_, topic, exception): event_submitter = FakeSubscriber() # publish event using the provider: - with pytest.raises(exception) if exception else nullcontext(): # type: ignore + with pytest.raises(exception) if exception else nullcontext(): await event_submitter.consume( payload=payload, type_=type_, diff --git a/tests/unit/test_inject.py b/tests/unit/test_inject.py index 845b5408..f6dc977e 100644 --- a/tests/unit/test_inject.py +++ b/tests/unit/test_inject.py @@ -56,11 +56,8 @@ async def test_assert_constructable( constructable: type[AsyncContextConstructable], does_raises: bool ): - """ - Test that assert_constructable can distinguish between - """ - - with pytest.raises(NotConstructableError) if does_raises else nullcontext(): # type: ignore + """Test that assert_constructable can distinguish between""" + with pytest.raises(NotConstructableError) if does_raises else nullcontext(): assert_async_constructable(constructable) @@ -85,8 +82,7 @@ async def test_context_constructor_init( Test the initialization of a context constructor with valid and invalid constructables. """ - - with pytest.raises(exception) if exception else nullcontext(): # type: ignore + with pytest.raises(exception) if exception else nullcontext(): test = AsyncConstructor(constructable) if not exception: @@ -108,12 +104,11 @@ async def test_context_constructor_setup_teardown( has_context: bool, ): """Test whether init and shutdown correctly works with a context constructor.""" - foo = "bar" test = AsyncConstructor(constructable, foo) - with pytest.raises(exception) if exception else nullcontext(): # type: ignore + with pytest.raises(exception) if exception else nullcontext(): resource = await test.async_() assert isinstance(resource, constructable) test_instance = await test.init() # type: ignore @@ -139,8 +134,8 @@ async def test_context_constructor_setup_teardown( ) def test_get_constructor(provides: type, args, kwargs, constructor_cls: type): """Tests whether the `get_constructor` function chooses the correct constructor - classes for the given `provides` classes.""" - + classes for the given `provides` classes. + """ constructor = get_constructor(provides, *args, **kwargs) assert isinstance(constructor, constructor_cls) diff --git a/tests/unit/test_testing_eventpub.py b/tests/unit/test_testing_eventpub.py index 05f43046..661a574b 100644 --- a/tests/unit/test_testing_eventpub.py +++ b/tests/unit/test_testing_eventpub.py @@ -28,7 +28,6 @@ @pytest.mark.asyncio async def test_in_mem_publisher(): """Test the InMemEventPublisher testing utilities.""" - type_ = "test_type" key = "test_key" topic = "test_topic" diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index b23b6303..a746caf2 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -52,7 +52,7 @@ def test_calc_part_size( preferred_part_size: int, file_size: int, expected_part_size: int ): """Test code to dynamically adapt part size""" - with pytest.raises(ValueError) if file_size > 5 * TiB else nullcontext(): # type: ignore + with pytest.raises(ValueError) if file_size > 5 * TiB else nullcontext(): adapted_part_size = calc_part_size( preferred_part_size=preferred_part_size, file_size=file_size ) @@ -88,13 +88,11 @@ class ExampleModel(BaseModel): @pytest.mark.parametrize("fields", ({"param_a"}, {"param_a", "param_b"})) def test_validate_fields_in_model_happy(fields: Collection[str]): """Test validate_fields_in_model with valid parameters.""" - validate_fields_in_model(model=ExampleModel, fields=fields) @pytest.mark.parametrize("fields", ({"param_c"}, {"param_a", "param_c"})) def test_validate_fields_in_model_error(fields: Collection[str]): """Test validate_fields_in_model with invalid parameters.""" - with pytest.raises(FieldNotInModelError): validate_fields_in_model(model=ExampleModel, fields=fields)