diff --git a/.github/workflows/iroha2-dev-pr-static.yml b/.github/workflows/iroha2-dev-pr-static.yml index bc44c92101e..59439db45f9 100644 --- a/.github/workflows/iroha2-dev-pr-static.yml +++ b/.github/workflows/iroha2-dev-pr-static.yml @@ -8,6 +8,7 @@ on: - '**.json' - '**.toml' - '.github/workflows/**.yml' + - 'client_cli/pytests/**/*.py' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -54,3 +55,31 @@ jobs: - name: Documentation if: always() run: cargo doc --no-deps --quiet + python_static_analysis: + runs-on: ubuntu-latest + container: + image: hyperledger/iroha2-ci:nightly-2024-01-12 + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies using Poetry + working-directory: client_cli/pytests + run: | + poetry install + + - name: Check code formatting with Black + working-directory: client_cli/pytests + run: | + poetry run black --check . + + - name: Run mypy (Type Checker) + working-directory: client_cli/pytests + run: | + poetry run mypy --explicit-package-bases . + + - name: Run flake8 (Linter) + working-directory: client_cli/pytests + run: | + poetry run flake8 . --max-line-length=110 --ignore=F401,W503,E203 + + diff --git a/.github/workflows/iroha2-dev-pr.yml b/.github/workflows/iroha2-dev-pr.yml index 7846ef54715..9bb38c02a2b 100644 --- a/.github/workflows/iroha2-dev-pr.yml +++ b/.github/workflows/iroha2-dev-pr.yml @@ -49,8 +49,8 @@ jobs: - name: Run tests, with coverage run: | mold --run cargo llvm-cov clean --workspace - mold --run cargo llvm-cov --doc --no-report --all-features --workspace --no-fail-fast - mold --run cargo llvm-cov --no-report --ignore-filename-regex main.rs --all-features --workspace --no-fail-fast + mold --run cargo llvm-cov --doc --no-report --all-features --workspace --no-fail-fast --ignore-filename-regex='(^client_cli/|main\.rs)' + mold --run cargo llvm-cov --no-report --ignore-filename-regex='(^client_cli/|main\.rs)' --all-features --workspace --no-fail-fast - name: Generate lcov report run: | # generate report without tests diff --git a/client/tests/integration/add_account.rs b/client/tests/integration/add_account.rs index d7de69d5041..49d15f1ebd9 100644 --- a/client/tests/integration/add_account.rs +++ b/client/tests/integration/add_account.rs @@ -6,6 +6,8 @@ use iroha_config::parameters::actual::Root as Config; use test_network::*; #[test] +// This test suite is also covered at the UI level in the iroha_client_cli tests +// in test_register_accounts.py fn client_add_account_with_name_length_more_than_limit_should_not_commit_transaction() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_505).start_with_runtime(); diff --git a/client/tests/integration/add_domain.rs b/client/tests/integration/add_domain.rs index d4cfe89c3b3..443978885d8 100644 --- a/client/tests/integration/add_domain.rs +++ b/client/tests/integration/add_domain.rs @@ -6,6 +6,8 @@ use iroha_config::parameters::actual::Root as Config; use test_network::*; #[test] +// This test suite is also covered at the UI level in the iroha_client_cli tests +// in test_register_domains.py fn client_add_domain_with_name_length_more_than_limit_should_not_commit_transaction() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_500).start_with_runtime(); diff --git a/client/tests/integration/asset.rs b/client/tests/integration/asset.rs index 7838742fd71..3c3d719d694 100644 --- a/client/tests/integration/asset.rs +++ b/client/tests/integration/asset.rs @@ -12,6 +12,8 @@ use serde_json::json; use test_network::*; #[test] +// This test is also covered at the UI level in the iroha_client_cli tests +// in test_register_asset_definitions.py fn client_register_asset_should_add_asset_once_but_not_twice() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_620).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); @@ -89,6 +91,8 @@ fn unregister_asset_should_remove_asset_from_account() -> Result<()> { } #[test] +// This test is also covered at the UI level in the iroha_client_cli tests +// in test_mint_assets.py fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_000).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); @@ -202,6 +206,8 @@ fn client_add_asset_with_decimal_should_increase_asset_amount() -> Result<()> { } #[test] +// This test is also covered at the UI level in the iroha_client_cli tests +// in test_register_asset_definitions.py fn client_add_asset_with_name_length_more_than_limit_should_not_commit_transaction() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_520).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); diff --git a/client/tests/integration/asset_propagation.rs b/client/tests/integration/asset_propagation.rs index 99a834017db..29122fc9560 100644 --- a/client/tests/integration/asset_propagation.rs +++ b/client/tests/integration/asset_propagation.rs @@ -13,6 +13,8 @@ use iroha_config::parameters::actual::Root as Config; use test_network::*; #[test] +// This test is also covered at the UI level in the iroha_client_cli tests +// in test_mint_asset.py fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount_on_another_peer( ) -> Result<()> { // Given diff --git a/client/tests/integration/config.rs b/client/tests/integration/config.rs deleted file mode 100644 index 1c71aba683d..00000000000 --- a/client/tests/integration/config.rs +++ /dev/null @@ -1,41 +0,0 @@ -use iroha_client::data_model::Level; -use test_network::*; - -#[test] -fn config_endpoints() { - const NEW_LOG_LEVEL: Level = Level::ERROR; - - let (rt, peer, test_client) = ::new().with_port(10_685).start_with_runtime(); - wait_for_genesis_committed(&vec![test_client.clone()], 0); - - let init_log_level = rt.block_on(async move { - peer.iroha - .as_ref() - .unwrap() - .kiso - .get_dto() - .await - .unwrap() - .logger - .level - }); - - // Just to be sure this test suite is not useless - assert_ne!(init_log_level, NEW_LOG_LEVEL); - - // Retrieving through API - let mut dto = test_client.get_config().expect("Client can always get it"); - assert_eq!(dto.logger.level, init_log_level); - - // Updating the log level - dto.logger.level = NEW_LOG_LEVEL; - test_client.set_config(dto).expect("New config is valid"); - - // Checking the updated value - dto = test_client.get_config().unwrap(); - assert_eq!(dto.logger.level, NEW_LOG_LEVEL); - - // Restoring value - dto.logger.level = init_log_level; - test_client.set_config(dto).expect("Also valid DTO"); -} diff --git a/client/tests/integration/domain_owner.rs b/client/tests/integration/domain_owner_permissions.rs similarity index 100% rename from client/tests/integration/domain_owner.rs rename to client/tests/integration/domain_owner_permissions.rs diff --git a/client/tests/integration/connected_peers.rs b/client/tests/integration/extra_functional/connected_peers.rs similarity index 100% rename from client/tests/integration/connected_peers.rs rename to client/tests/integration/extra_functional/connected_peers.rs diff --git a/client/tests/integration/extra_functional/mod.rs b/client/tests/integration/extra_functional/mod.rs new file mode 100644 index 00000000000..ad4db9c11d0 --- /dev/null +++ b/client/tests/integration/extra_functional/mod.rs @@ -0,0 +1,6 @@ +mod connected_peers; +mod multiple_blocks_created; +mod offline_peers; +mod restart_peer; +mod unregister_peer; +mod unstable_network; diff --git a/client/tests/integration/multiple_blocks_created.rs b/client/tests/integration/extra_functional/multiple_blocks_created.rs similarity index 100% rename from client/tests/integration/multiple_blocks_created.rs rename to client/tests/integration/extra_functional/multiple_blocks_created.rs diff --git a/client/tests/integration/offline_peers.rs b/client/tests/integration/extra_functional/offline_peers.rs similarity index 100% rename from client/tests/integration/offline_peers.rs rename to client/tests/integration/extra_functional/offline_peers.rs diff --git a/client/tests/integration/restart_peer.rs b/client/tests/integration/extra_functional/restart_peer.rs similarity index 100% rename from client/tests/integration/restart_peer.rs rename to client/tests/integration/extra_functional/restart_peer.rs diff --git a/client/tests/integration/unregister_peer.rs b/client/tests/integration/extra_functional/unregister_peer.rs similarity index 100% rename from client/tests/integration/unregister_peer.rs rename to client/tests/integration/extra_functional/unregister_peer.rs diff --git a/client/tests/integration/unstable_network.rs b/client/tests/integration/extra_functional/unstable_network.rs similarity index 100% rename from client/tests/integration/unstable_network.rs rename to client/tests/integration/extra_functional/unstable_network.rs diff --git a/client/tests/integration/mod.rs b/client/tests/integration/mod.rs index 8c8008ce649..4340f7de2bd 100644 --- a/client/tests/integration/mod.rs +++ b/client/tests/integration/mod.rs @@ -3,20 +3,15 @@ mod add_domain; mod asset; mod asset_propagation; mod burn_public_keys; -mod config; -mod connected_peers; -mod domain_owner; +mod domain_owner_permissions; mod events; -mod multiple_blocks_created; +mod extra_functional; mod multisignature_account; mod multisignature_transaction; mod non_mintable; -mod offline_peers; mod pagination; mod permissions; mod queries; -mod query_errors; -mod restart_peer; mod roles; mod set_parameter; mod sorting; @@ -24,6 +19,4 @@ mod transfer_asset; mod triggers; mod tx_history; mod tx_rollback; -mod unregister_peer; -mod unstable_network; mod upgrade; diff --git a/client/tests/integration/queries/mod.rs b/client/tests/integration/queries/mod.rs index d654c8fc83b..d4ed9a45cd6 100644 --- a/client/tests/integration/queries/mod.rs +++ b/client/tests/integration/queries/mod.rs @@ -12,6 +12,7 @@ use test_network::*; mod account; mod asset; +mod query_errors; mod role; #[test] diff --git a/client/tests/integration/query_errors.rs b/client/tests/integration/queries/query_errors.rs similarity index 100% rename from client/tests/integration/query_errors.rs rename to client/tests/integration/queries/query_errors.rs diff --git a/client/tests/integration/transfer_asset.rs b/client/tests/integration/transfer_asset.rs index 0de27aad05a..6b3ee6540dc 100644 --- a/client/tests/integration/transfer_asset.rs +++ b/client/tests/integration/transfer_asset.rs @@ -15,6 +15,8 @@ use iroha_primitives::fixed::Fixed; use test_network::*; #[test] +// This test suite is also covered at the UI level in the iroha_client_cli tests +// in test_tranfer_assets.py fn simulate_transfer_quantity() { simulate_transfer( 200_u32, @@ -53,6 +55,8 @@ fn simulate_transfer_fixed() { #[test] #[ignore = "long"] #[should_panic(expected = "insufficient funds")] +// This test is also covered at the UI level in the iroha_client_cli tests +// in test_tranfer_assets.py fn simulate_insufficient_funds() { simulate_transfer( Fixed::try_from(20_f64).unwrap(), diff --git a/client_cli/pytests/common/__init__.py b/client_cli/pytests/common/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/client_cli/pytests/common/consts.py b/client_cli/pytests/common/consts.py index 6987c4b9d41..98d7c447bd1 100644 --- a/client_cli/pytests/common/consts.py +++ b/client_cli/pytests/common/consts.py @@ -14,21 +14,26 @@ class Stderr(Enum): """ Enum for standard error messages. """ - CANNOT_BE_EMPTY = 'cannot be empty\n\nFor more information, try \'--help\'.\n' - REPETITION = 'Repetition' - TOO_LONG = 'Name length violation' - FAILED_TO_FIND_DOMAIN = 'Failed to find domain' - INVALID_CHARACTER = 'Invalid character' - INVALID_VALUE_TYPE = 'Matching variant not found' - RESERVED_CHARACTER = 'The `@` character is reserved for `account@domain` constructs,' \ - ' `#` — for `asset#domain` and `$` — for `trigger$domain`.' + + CANNOT_BE_EMPTY = "cannot be empty\n\nFor more information, try '--help'.\n" + REPETITION = "Repetition" + TOO_LONG = "Name length violation" + FAILED_TO_FIND_DOMAIN = "Failed to find domain" + INVALID_CHARACTER = "Invalid character" + INVALID_VALUE_TYPE = "Matching variant not found" + RESERVED_CHARACTER = ( + "The `@` character is reserved for `account@domain` constructs," + " `#` — for `asset#domain` and `$` — for `trigger$domain`." + ) WHITESPACES = "White space not allowed" + INSUFFICIENT_FUNDS = "Not enough quantity to transfer/burn" class ReservedChars(Enum): """ Enum for reserved characters in names. """ + SPECIAL = "@#$" WHITESPACES = string.whitespace ALL = SPECIAL + WHITESPACES @@ -38,8 +43,9 @@ class ValueTypes(Enum): """ Enum for value types used in the application. """ - QUANTITY = 'Quantity' # unsigned 32-bit integer - STORE = 'Store' #storing key-values in object's metadata - # BIG_QUANTITY = 'BigQuantity' unsigned 128-bit integer + + QUANTITY = "Quantity" # unsigned 32-bit integer + STORE = "Store" # storing key-values in object's metadata + BIG_QUANTITY = "BigQuantity" # unsigned 128-bit integer # FIXED = 'Fixed' 64-bit fixed-precision number with # nine significant digits after the decimal point diff --git a/client_cli/pytests/common/helpers.py b/client_cli/pytests/common/helpers.py index f63b11dfc86..6145ea35557 100644 --- a/client_cli/pytests/common/helpers.py +++ b/client_cli/pytests/common/helpers.py @@ -3,14 +3,68 @@ """ import binascii +import json +import os import random +import re import string from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey + from common.consts import ReservedChars, fake +def extract_hash(stdout): + """ + Extracts a SHA-256 hash from the given string. + + :param stdout: The string from which to extract the hash. + :return: The extracted hash if found, otherwise None. + """ + if not isinstance(stdout, str) or not stdout.strip(): + return None + pattern = r'"([A-Fa-f0-9]{64})"' + match = re.search(pattern, stdout) + return match.group(1) if match else None + + +def get_peers_config_files(path_to_configs): + """ + Returns a list of config file paths from the given directory. + """ + config_files = [] + for entry in os.listdir(path_to_configs): + if entry.endswith(".json") and "config_to_peer" in entry: + config_files.append(os.path.join(path_to_configs, entry)) + return config_files + + +def read_isi_from_json(file_path): + """ + Reads ISI instruction from a JSON file. + + :param file_path: Path to the JSON file containing ISI instruction. + :return: Dictionary with ISI instruction. + """ + with open(file_path, "r", encoding="utf-8") as file: + isi_data = json.load(file) + return isi_data + + +def write_isi_to_json(isi_data, file_path): + """ + Writes ISI instruction to a JSON file. + + :param isi_data: Dictionary with ISI instruction. + :param file_path: Path to save the JSON file. + """ + if not isinstance(isi_data, list): + isi_data = [isi_data] + with open(file_path, "w", encoding="utf-8") as file: + json.dump(isi_data, file, indent=4) + + def generate_random_string_with_reserved_char(): """ Generate a random string with a reserved character. @@ -38,9 +92,12 @@ def generate_public_key(): Generate a public key using Ed25519PrivateKey. """ public_key = binascii.hexlify( - Ed25519PrivateKey.generate().public_key().public_bytes( - encoding=serialization.Encoding.Raw, - format=serialization.PublicFormat.Raw)).decode() + Ed25519PrivateKey.generate() + .public_key() + .public_bytes( + encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw + ) + ).decode() return public_key @@ -48,14 +105,18 @@ def generate_random_string(length, allowed_chars): """ Generate a random string with the specified length and characters. """ - return ''.join(random.choice(allowed_chars) for _ in range(length)) + return "".join(random.choice(allowed_chars) for _ in range(length)) def generate_random_string_without_reserved_chars(length): """ Generate a random string with the specified length, excluding reserved characters. """ - allowed_chars = [c for c in [*string.ascii_letters, *string.digits] if c not in ReservedChars.ALL.value] + allowed_chars = [ + c + for c in [*string.ascii_letters, *string.digits] + if c not in ReservedChars.ALL.value + ] return generate_random_string(length, allowed_chars) @@ -78,7 +139,7 @@ def not_existing_name(): """ Generate a non-existing name. """ - return 'not_existing_name' + return "not_existing_name" def key_with_invalid_character_in_key(public_key, random_character): @@ -93,5 +154,9 @@ def name_with_uppercase_letter(name): Change one random letter in a name to uppercase. """ random_position = random.randint(0, len(name) - 1) - name = name[:random_position] + name[random_position].upper() + name[random_position + 1:] + name = ( + name[:random_position] + + name[random_position].upper() + + name[random_position + 1 :] + ) return name diff --git a/client_cli/pytests/common/json_isi_examples/unregister_asset.json b/client_cli/pytests/common/json_isi_examples/unregister_asset.json new file mode 100644 index 00000000000..ac3d7f314f9 --- /dev/null +++ b/client_cli/pytests/common/json_isi_examples/unregister_asset.json @@ -0,0 +1,7 @@ +[{ +"Unregister": { + "Asset": { + "object_id": "rose#alice@wonderland" + } +} +}] \ No newline at end of file diff --git a/client_cli/pytests/common/settings.py b/client_cli/pytests/common/settings.py index d5de68f753f..3593f91d0b7 100644 --- a/client_cli/pytests/common/settings.py +++ b/client_cli/pytests/common/settings.py @@ -4,18 +4,18 @@ """ import os + from dotenv import load_dotenv load_dotenv() -BASE_DIR = os.path.dirname \ - (os.path.dirname - (os.path.dirname - (os.path.abspath(__file__)))) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +ROOT_DIR = os.environ.get("CLIENT_CLI_DIR", BASE_DIR) PATH_CONFIG_CLIENT_CLI = os.environ["CLIENT_CLI_CONFIG"] CLIENT_CLI_PATH = os.environ["CLIENT_CLI_BINARY"] +PEERS_CONFIGS_PATH = os.path.join(ROOT_DIR, "peers_configs") -PORT_MIN = int(os.getenv('TORII_API_PORT_MIN', '8080')) -PORT_MAX = int(os.getenv('TORII_API_PORT_MAX', '8083')) +PORT_MIN = int(os.getenv("TORII_API_PORT_MIN", "8080")) +PORT_MAX = int(os.getenv("TORII_API_PORT_MAX", "8083")) diff --git a/client_cli/pytests/models/account.py b/client_cli/pytests/models/account.py index 0dd65575948..7f3acb01d67 100644 --- a/client_cli/pytests/models/account.py +++ b/client_cli/pytests/models/account.py @@ -1,8 +1,10 @@ """ This module provides an Account class for working with Iroha network accounts. """ + from dataclasses import dataclass + @dataclass class Account: """ @@ -15,9 +17,10 @@ class Account: :param public_key: The public key of the account. :type public_key: str """ + name: str domain: str - public_key: str + public_key: str = "" def __repr__(self): return f"{self.name}@{self.domain}" diff --git a/client_cli/pytests/models/asset.py b/client_cli/pytests/models/asset.py index 2ca532c4a6d..c3decd39da4 100644 --- a/client_cli/pytests/models/asset.py +++ b/client_cli/pytests/models/asset.py @@ -4,6 +4,7 @@ from dataclasses import dataclass + @dataclass class AssetDefinition: """ @@ -16,6 +17,7 @@ class AssetDefinition: :param value_type: The value type of the asset definition. :type value_type: str """ + name: str domain: str value_type: str @@ -32,6 +34,7 @@ def get_id(self): """ return f"{self.name}#{self.domain}" + @dataclass class Asset: """ @@ -39,10 +42,14 @@ class Asset: :param definition: The asset definition of the asset. :type definition: AssetDefinition + :param account: The account of the asset. + :type definition: str :param value: The value of the asset. :type value: str """ + definition: AssetDefinition + account: str value: str def __repr__(self): diff --git a/client_cli/pytests/models/domain.py b/client_cli/pytests/models/domain.py index cf3528ba338..9b936c745a1 100644 --- a/client_cli/pytests/models/domain.py +++ b/client_cli/pytests/models/domain.py @@ -1,8 +1,10 @@ """ This module contains the Domain class. """ + from dataclasses import dataclass + @dataclass class Domain: """ @@ -11,8 +13,8 @@ class Domain: :param name: The name of the domain. :type name: str """ - name: str + name: str def get_name(self): """ diff --git a/client_cli/pytests/poetry.lock b/client_cli/pytests/poetry.lock index 53341202632..f46e771b829 100644 --- a/client_cli/pytests/poetry.lock +++ b/client_cli/pytests/poetry.lock @@ -32,13 +32,13 @@ pluggy = ">=0.4.0" [[package]] name = "astroid" -version = "2.15.5" +version = "2.15.8" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"}, - {file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"}, + {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, + {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, ] [package.dependencies] @@ -47,98 +47,145 @@ wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""} [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "black" +version = "24.2.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, + {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, + {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, + {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, + {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, + {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, + {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, + {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, + {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, + {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, + {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, + {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, + {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, + {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, + {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, + {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, + {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, + {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, + {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, + {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, + {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, + {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cffi" -version = "1.15.1" +version = "1.16.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] [package.dependencies] pycparser = "*" +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -152,87 +199,117 @@ files = [ [[package]] name = "cryptography" -version = "41.0.1" +version = "42.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699"}, - {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3"}, - {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db"}, - {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31"}, - {file = "cryptography-41.0.1-cp37-abi3-win32.whl", hash = "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5"}, - {file = "cryptography-41.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5"}, - {file = "cryptography-41.0.1.tar.gz", hash = "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006"}, + {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:de5086cd475d67113ccb6f9fae6d8fe3ac54a4f9238fd08bfdb07b03d791ff0a"}, + {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:935cca25d35dda9e7bd46a24831dfd255307c55a07ff38fd1a92119cffc34857"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20100c22b298c9eaebe4f0b9032ea97186ac2555f426c3e70670f2517989543b"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eb6368d5327d6455f20327fb6159b97538820355ec00f8cc9464d617caecead"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39d5c93e95bcbc4c06313fc6a500cee414ee39b616b55320c1904760ad686938"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d96ea47ce6d0055d5b97e761d37b4e84195485cb5a38401be341fabf23bc32a"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d1998e545081da0ab276bcb4b33cce85f775adb86a516e8f55b3dac87f469548"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93fbee08c48e63d5d1b39ab56fd3fdd02e6c2431c3da0f4edaf54954744c718f"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:90147dad8c22d64b2ff7331f8d4cddfdc3ee93e4879796f837bdbb2a0b141e0c"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4dcab7c25e48fc09a73c3e463d09ac902a932a0f8d0c568238b3696d06bf377b"}, + {file = "cryptography-42.0.3-cp37-abi3-win32.whl", hash = "sha256:1e935c2900fb53d31f491c0de04f41110351377be19d83d908c1fd502ae8daa5"}, + {file = "cryptography-42.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:762f3771ae40e111d78d77cbe9c1035e886ac04a234d3ee0856bf4ecb3749d54"}, + {file = "cryptography-42.0.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3ec384058b642f7fb7e7bff9664030011ed1af8f852540c76a1317a9dd0d20"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35772a6cffd1f59b85cb670f12faba05513446f80352fe811689b4e439b5d89e"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04859aa7f12c2b5f7e22d25198ddd537391f1695df7057c8700f71f26f47a129"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3d1f5a1d403a8e640fa0887e9f7087331abb3f33b0f2207d2cc7f213e4a864c"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:df34312149b495d9d03492ce97471234fd9037aa5ba217c2a6ea890e9166f151"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:de4ae486041878dc46e571a4c70ba337ed5233a1344c14a0790c4c4be4bbb8b4"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0fab2a5c479b360e5e0ea9f654bcebb535e3aa1e493a715b13244f4e07ea8eec"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25b09b73db78facdfd7dd0fa77a3f19e94896197c86e9f6dc16bce7b37a96504"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d5cf11bc7f0b71fb71af26af396c83dfd3f6eed56d4b6ef95d57867bf1e4ba65"}, + {file = "cryptography-42.0.3-cp39-abi3-win32.whl", hash = "sha256:0fea01527d4fb22ffe38cd98951c9044400f6eff4788cf52ae116e27d30a1ba3"}, + {file = "cryptography-42.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:2619487f37da18d6826e27854a7f9d4d013c51eafb066c80d09c63cf24505306"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ead69ba488f806fe1b1b4050febafdbf206b81fa476126f3e16110c818bac396"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:20180da1b508f4aefc101cebc14c57043a02b355d1a652b6e8e537967f1e1b46"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5fbf0f3f0fac7c089308bd771d2c6c7b7d53ae909dce1db52d8e921f6c19bb3a"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c23f03cfd7d9826cdcbad7850de67e18b4654179e01fe9bc623d37c2638eb4ef"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db0480ffbfb1193ac4e1e88239f31314fe4c6cdcf9c0b8712b55414afbf80db4"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:6c25e1e9c2ce682d01fc5e2dde6598f7313027343bd14f4049b82ad0402e52cd"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9541c69c62d7446539f2c1c06d7046aef822940d248fa4b8962ff0302862cc1f"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b797099d221df7cce5ff2a1d272761d1554ddf9a987d3e11f6459b38cd300fd"}, + {file = "cryptography-42.0.3.tar.gz", hash = "sha256:069d2ce9be5526a44093a0991c450fe9906cdf069e0e7cd67d9dee49a62b9ebe"}, ] [package.dependencies] -cffi = ">=1.12" +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] nox = ["nox"] -pep8test = ["black", "check-sdist", "mypy", "ruff"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] name = "dill" -version = "0.3.6" -description = "serialize all of python" +version = "0.3.8" +description = "serialize all of Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, - {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "execnet" -version = "1.9.0" +version = "2.0.2" description = "execnet: rapid multi-Python deployment" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" files = [ - {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, - {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, + {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, + {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, ] [package.extras] -testing = ["pre-commit"] +testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "faker" -version = "18.11.2" +version = "23.2.0" description = "Faker is a Python package that generates fake data for you." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Faker-18.11.2-py3-none-any.whl", hash = "sha256:21c2c29638e98502f3bba9ad6a4f07a4b09c5e2150bb491ff02411a5888f6955"}, - {file = "Faker-18.11.2.tar.gz", hash = "sha256:ec6e2824bb1d3546b36c156324b9df6bca5a3d6d03adf991e6a5586756dcab9d"}, + {file = "Faker-23.2.0-py3-none-any.whl", hash = "sha256:2db4b60ef93d247a8fe5518d01ebafa8df3a5dffd40cbb9577b25c45b04a9952"}, + {file = "Faker-23.2.0.tar.gz", hash = "sha256:8631f712e85ae9208352442f74537927a92c439b83125f83e4798e5716764cc9"}, ] [package.dependencies] python-dateutil = ">=2.4" +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + [[package]] name = "iniconfig" version = "2.0.0" @@ -246,64 +323,62 @@ files = [ [[package]] name = "isort" -version = "5.12.0" +version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +colors = ["colorama (>=0.4.6)"] [[package]] name = "lazy-object-proxy" -version = "1.9.0" +version = "1.10.0" description = "A fast and thorough lazy object proxy." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, ] [[package]] @@ -319,37 +394,38 @@ files = [ [[package]] name = "mypy" -version = "1.4.1" +version = "1.8.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, - {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, - {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, - {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, - {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, - {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, - {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, - {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, - {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, - {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, - {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, - {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, - {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, - {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, - {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, - {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, - {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, - {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, - {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, - {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, + {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, + {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, + {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, + {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, + {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, + {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, + {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, + {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, + {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, + {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, + {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, + {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, + {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, + {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, + {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, + {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, + {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, ] [package.dependencies] @@ -359,7 +435,7 @@ typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] -python2 = ["typed-ast (>=1.4.0,<2)"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] @@ -375,45 +451,67 @@ files = [ [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "platformdirs" -version = "3.8.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-3.8.0-py3-none-any.whl", hash = "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e"}, - {file = "platformdirs-3.8.0.tar.gz", hash = "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.2.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + [[package]] name = "pycparser" version = "2.21" @@ -425,19 +523,30 @@ files = [ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + [[package]] name = "pylint" -version = "2.17.4" +version = "2.17.7" description = "python code static checker" optional = false python-versions = ">=3.7.2" files = [ - {file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"}, - {file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"}, + {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"}, + {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"}, ] [package.dependencies] -astroid = ">=2.15.4,<=2.17.0-dev0" +astroid = ">=2.15.8,<=2.17.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""} isort = ">=4.2.5,<6" @@ -451,33 +560,33 @@ testutils = ["gitpython (>3)"] [[package]] name = "pytest" -version = "7.4.0" +version = "8.0.0" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-8.0.0-py3-none-any.whl", hash = "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6"}, + {file = "pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" +pluggy = ">=1.3.0,<2.0" [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-xdist" -version = "3.3.1" +version = "3.5.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, - {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, + {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"}, + {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"}, ] [package.dependencies] @@ -505,13 +614,13 @@ six = ">=1.5" [[package]] name = "python-dotenv" -version = "1.0.0" +version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [package.extras] @@ -541,100 +650,95 @@ files = [ [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] name = "wrapt" -version = "1.15.0" +version = "1.16.0" description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, ] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "101321a5a8443974ff254d60b75c4a59c0da6a8b2e2387a8a3f666692a58b834" +content-hash = "04f8d471669f9b089470787b9c56ec3b618986aa78efffd4c46892da9056945b" diff --git a/client_cli/pytests/pyproject.toml b/client_cli/pytests/pyproject.toml index 707d8df5d4b..780c0f4ee37 100644 --- a/client_cli/pytests/pyproject.toml +++ b/client_cli/pytests/pyproject.toml @@ -18,11 +18,12 @@ pytest = "*" allure-pytest = "*" pytest-xdist = "*" mypy = "*" -isort = "*" +black = "*" +flake8 = "*" [tool.poetry.group.dev.dependencies] pylint = "^2.17.1" -isort = "^5.12.0" +isort = "^5.13.2" [build-system] requires = ["poetry-core"] diff --git a/client_cli/pytests/src/client_cli/__init__.py b/client_cli/pytests/src/client_cli/__init__.py index f907e8b5009..dd9bea09b2a 100644 --- a/client_cli/pytests/src/client_cli/__init__.py +++ b/client_cli/pytests/src/client_cli/__init__.py @@ -2,7 +2,7 @@ This module initializes the Iroha client and configuration using environment variables. """ -from common.settings import PATH_CONFIG_CLIENT_CLI, PORT_MIN, PORT_MAX +from common.settings import PATH_CONFIG_CLIENT_CLI, PORT_MAX, PORT_MIN from src.client_cli.client_cli import ClientCli from src.client_cli.configuration import Config from src.client_cli.iroha import Iroha diff --git a/client_cli/pytests/src/client_cli/client_cli.py b/client_cli/pytests/src/client_cli/client_cli.py index dfdc8629ef8..6f939018035 100644 --- a/client_cli/pytests/src/client_cli/client_cli.py +++ b/client_cli/pytests/src/client_cli/client_cli.py @@ -2,14 +2,17 @@ This module contains the ClientCli class, which is responsible for building and executing commands for interacting with Iroha blockchain using the Iroha command-line client. """ -import json + +import shlex import subprocess -from time import sleep, monotonic +from pathlib import Path +from time import monotonic, sleep from typing import Callable -import allure +import allure # type: ignore -from common.settings import PATH_CONFIG_CLIENT_CLI, CLIENT_CLI_PATH +from common.helpers import extract_hash, read_isi_from_json, write_isi_to_json +from common.settings import BASE_DIR, CLIENT_CLI_PATH, PATH_CONFIG_CLIENT_CLI, ROOT_DIR from src.client_cli.configuration import Config @@ -17,11 +20,12 @@ class ClientCli: """ A class to represent the Iroha client command line interface. """ + BASE_PATH = CLIENT_CLI_PATH # --skip-mst-check flag is used because # MST isn't used in the tests # and don't using this flag results in tests being broken by interactive prompt - BASE_FLAGS = ['--config=' + PATH_CONFIG_CLIENT_CLI, '--skip-mst-check'] + BASE_FLAGS = ["--config=" + PATH_CONFIG_CLIENT_CLI, "--skip-mst-check"] def __init__(self, config: Config): """ @@ -32,7 +36,8 @@ def __init__(self, config: Config): self.command = [self.BASE_PATH] + self.BASE_FLAGS self.stdout = None self.stderr = None - self._timeout = 5 + self.transaction_hash = None + self._timeout = 20 def __enter__(self): """ @@ -64,8 +69,10 @@ def wait_for(self, condition: Callable[[], bool], timeout=None): start_time = monotonic() while not condition(): if monotonic() - start_time > timeout: - raise TimeoutError(f"Expected condition to be satisfied after waiting for '{timeout}' seconds.") - sleep(0.5) + raise TimeoutError( + f"Expected condition to be satisfied after waiting for '{timeout}' seconds." + ) + sleep(0.25) def reset(self): """ @@ -82,7 +89,7 @@ def register(self): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.append('register') + self.command.append("register") return self def mint(self): @@ -92,7 +99,7 @@ def mint(self): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.append('mint') + self.command.append("mint") return self def list_all(self): @@ -102,20 +109,18 @@ def list_all(self): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.append('list') - self.command.append('all') + self.command.append("list") + self.command.append("all") return self - def list_filter(self, filter): + def list_filter(self, filter_criteria): """ - Appends the 'list all' command to the command list. - - :return: The current ClientCli object. - :rtype: ClientCli + Appends the 'list filter' command to the command list. + :param filter_criteria: Criteria to filter the list. """ - self.command.append('list'), - self.command.append('filter') - self.command.append(filter) + self.command.append("list") + self.command.append("filter") + self.command.append(str(filter_criteria)) return self def domain(self, domain: str): @@ -127,8 +132,8 @@ def domain(self, domain: str): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.insert(3, 'domain') - self.command.append('--id=' + domain) + self.command.insert(3, "domain") + self.command.append("--id=" + domain) self.execute() return self @@ -145,9 +150,9 @@ def account(self, account: str, domain: str, key: str): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.insert(3, 'account') - self.command.append('--id=' + account + '@' + domain) - self.command.append('--key=ed0120' + key) + self.command.insert(3, "account") + self.command.append("--id=" + account + "@" + domain) + self.command.append("--key=ed0120" + key) self.execute() return self @@ -156,21 +161,29 @@ def asset(self, asset_definition=None, account=None, value_of_value_type=None): Executes the 'asset' command with the given asset definition, account, and value. :param asset_definition: The asset definition to be queried, defaults to None. - :type asset_definition: AssetDefinition, optional + :type asset_definition: AssetDefinition :param account: The account to be queried, defaults to None. - :type account: Account, optional + :type account: Account :param value_of_value_type: The value of the value type, defaults to None. :type value_of_value_type: str, optional :return: The current ClientCli object. :rtype: ClientCli """ - self.command.insert(3, 'asset') + self.command.insert(3, "asset") if asset_definition and account and value_of_value_type: self.command.append( - '--asset-id=' - + asset_definition.name + '#' + account.domain + '#' + account.name + '@' + asset_definition.domain) + "--asset-id=" + + asset_definition.name + + "#" + + account.domain + + "#" + + account.name + + "@" + + asset_definition.domain + ) self.command.append( - '--' + asset_definition.value_type.lower() + '=' + value_of_value_type) + "--" + asset_definition.value_type.lower() + "=" + value_of_value_type + ) self.execute() return self @@ -189,12 +202,20 @@ def transfer(self, asset, source_account, target_account, quantity: str): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.append('asset') - self.command.append('transfer') - self.command.append('--to=' + repr(target_account)) + self.command.append("asset") + self.command.append("transfer") + self.command.append("--to=" + repr(target_account)) self.command.append( - '--asset-id=' + asset.name + '#' + source_account.domain + '#' + source_account.name + '@' + asset.domain) - self.command.append('--quantity=' + quantity) + "--asset-id=" + + asset.name + + "#" + + source_account.domain + + "#" + + source_account.name + + "@" + + asset.domain + ) + self.command.append("--quantity=" + quantity) self.execute() return self @@ -210,11 +231,19 @@ def burn(self, account, asset, quantity: str): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.append('asset') - self.command.append('burn') + self.command.append("asset") + self.command.append("burn") self.command.append( - '--asset-id=' + asset.name + '#' + account.domain + '#' + account.name + '@' + asset.domain) - self.command.append('--quantity=' + quantity) + "--asset-id=" + + asset.name + + "#" + + account.domain + + "#" + + account.name + + "@" + + asset.domain + ) + self.command.append("--quantity=" + quantity) self.execute() return self @@ -231,11 +260,67 @@ def definition(self, asset: str, domain: str, value_type: str): :return: The current ClientCli object. :rtype: ClientCli """ - self.command.append('--definition-id=' + asset + '#' + domain) - self.command.append('--value-type=' + value_type) + self.command.append("--definition-id=" + asset + "#" + domain) + self.command.append("--value-type=" + value_type) self.execute() return self + def register_trigger(self, account): + """ + Creates a JSON file for the register trigger and executes it using the Iroha CLI. + + :param account: The account to be used in the register_trigger. + :type account: str + """ + + json_template_path = ( + Path(BASE_DIR) + / "pytests" + / "common" + / "json_isi_examples" + / "register_trigger.json" + ) + trigger_data = read_isi_from_json(str(json_template_path)) + trigger_data[0]["Register"]["Trigger"]["action"]["authority"] = str(account) + + json_temp_file_path = Path(ROOT_DIR) / "isi_register_trigger.json" + write_isi_to_json(trigger_data, str(json_temp_file_path)) + + self._execute_pipe( + ["cat", str(json_temp_file_path)], + [self.BASE_PATH] + self.BASE_FLAGS + ["json"], + ) + + return self + + def unregister_asset(self, asset_id): + """ + Creates a JSON file for the unregister asset and executes it using the Iroha CLI. + + :param asset_id: The object ID to be used in the unregister_asset. + :type asset_id: str + """ + + json_template_path = ( + Path(BASE_DIR) + / "pytests" + / "common" + / "json_isi_examples" + / "unregister_asset.json" + ) + asset_data = read_isi_from_json(str(json_template_path)) + asset_data[0]["Unregister"]["Asset"]["object_id"] = str(asset_id) + + json_temp_file_path = Path(ROOT_DIR) / "isi_unregister_asset.json" + write_isi_to_json(asset_data, str(json_temp_file_path)) + + self._execute_pipe( + ["cat", str(json_temp_file_path)], + [self.BASE_PATH] + self.BASE_FLAGS + ["json"], + ) + + return self + def should(self, _expected): """ Placeholder method for implementing assertions. @@ -258,35 +343,62 @@ def execute(self, command=None): if command is None: command = self.command else: - command = [self.BASE_PATH] + self.BASE_FLAGS + command.split() - allure_command = ' '.join(map(str, command[3:])) - print(allure_command) - with allure.step(f'{allure_command} on the {str(self.config.torii_url)} peer'): - try: - with subprocess.Popen( - command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - env=self.config.env - ) as process: - self.stdout, self.stderr = process.communicate() - allure.attach( - self.stdout, - name='stdout', - attachment_type=allure.attachment_type.TEXT) - allure.attach( - self.stderr, - name='stderr', - attachment_type=allure.attachment_type.TEXT) - except Exception as exception: - raise RuntimeError( - f"Error executing command: {command}. " - f"Error: {exception}" - ) from exception - finally: - self.command = [self.BASE_PATH] + self.BASE_FLAGS - return self + command = [self.BASE_PATH] + self.BASE_FLAGS + shlex.split(command) + + if "|" in command: + pipe_index = command.index("|") + self._execute_pipe(command[:pipe_index], command[pipe_index + 1 :]) + else: + self._execute_single(command) + + self.command = [self.BASE_PATH] + self.BASE_FLAGS + return self + + def _execute_pipe(self, cmd1, cmd2): + """ + Executes two commands connected by a pipe. + """ + with ( + subprocess.Popen( + cmd1, stdout=subprocess.PIPE, env=self.config.env + ) as proc1, + subprocess.Popen( + cmd2, + stdin=proc1.stdout, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=self.config.env, + ) as proc2, + ): + self.stdout, self.stderr = proc2.communicate() + self.transaction_hash = extract_hash(self.stdout) + self._attach_allure_reports() + + def _execute_single(self, command): + """ + Executes a single command. + """ + with subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + env=self.config.env, + ) as process: + self.stdout, self.stderr = process.communicate() + self.transaction_hash = extract_hash(self.stdout) + self._attach_allure_reports() + + def _attach_allure_reports(self): + """ + Attaches stdout and stderr to Allure reports. + """ + allure.attach( + self.stdout, name="stdout", attachment_type=allure.attachment_type.TEXT + ) + allure.attach( + self.stderr, name="stderr", attachment_type=allure.attachment_type.TEXT + ) @property def config(self) -> Config: diff --git a/client_cli/pytests/src/client_cli/configuration.py b/client_cli/pytests/src/client_cli/configuration.py index a80b0e50202..86a06a66b4f 100644 --- a/client_cli/pytests/src/client_cli/configuration.py +++ b/client_cli/pytests/src/client_cli/configuration.py @@ -3,6 +3,8 @@ """ import tomlkit +import glob +import json import os import random from urllib.parse import urlparse @@ -19,6 +21,7 @@ class Config: :param port_max: The maximum port number for the TORII_API_URL. :type port_max: int """ + def __init__(self, port_min, port_max): self._config = None self.file = None @@ -36,10 +39,49 @@ def load(self, path_config_client_cli): """ if not os.path.exists(path_config_client_cli): raise IOError(f"No config file found at {path_config_client_cli}") - with open(path_config_client_cli, 'r', encoding='utf-8') as config_file: + with open(path_config_client_cli, "r", encoding="utf-8") as config_file: self._config = tomlkit.load(config_file) self.file = path_config_client_cli + def generate_by_peers(self, peers_configs_dir): + """ + Generate configuration files for each port in the range from port_min to port_max. + """ + if self._config is None: + raise ValueError( + "No configuration loaded. Use load() method to load the configuration." + ) + + if self.port_min >= self.port_max: + raise ValueError("port_min must be less than port_max.") + + os.makedirs(peers_configs_dir, exist_ok=True) + + for port in range(self.port_min, self.port_max + 1): + config_copy = self._config.copy() + config_copy["TORII_API_URL"] = f"http://localhost:{port}" + file_name = f"config_to_peer_{port}.json" + file_path = os.path.join(peers_configs_dir, file_name) + with open(file_path, "w", encoding="utf-8") as config_file: + json.dump(config_copy, config_file, indent=4) + + def select_random_peer_config(self): + """ + Select and load a random configuration file generated by the generate_by_peers method. + This updates the current configuration to the one chosen. + + :return: None + """ + peers_configs = glob.glob("path/to/peers/configs/*.json") + if not peers_configs: + raise ValueError( + "Peer configuration files not found. First generate them using generate_by_peers." + ) + + chosen_config_file = random.choice(peers_configs) + + self.load(chosen_config_file) + def randomise_torii_url(self): """ Update Torii URL. @@ -50,7 +92,9 @@ def randomise_torii_url(self): """ parsed_url = urlparse(self._config["torii_url"]) random_port = random.randint(self.port_min, self.port_max) - self._envs["TORII_URL"] = parsed_url._replace(netloc=f"{parsed_url.hostname}:{random_port}").geturl() + self._envs["TORII_URL"] = parsed_url._replace( + netloc=f"{parsed_url.hostname}:{random_port}" + ).geturl() @property def torii_url(self): @@ -70,6 +114,8 @@ def env(self): :return: Dictionary with env vars (mixed with existing OS vars) :rtype: dict """ + # self.select_random_peer_config() + # return self._config['TORII_API_URL' return {**os.environ, **self._envs} @property @@ -80,7 +126,7 @@ def account_id(self): :return: The ACCOUNT_ID. :rtype: str """ - return self._config['account']["id"] + return self._config["account"]["id"] @property def account_name(self): @@ -90,7 +136,7 @@ def account_name(self): :return: The account name. :rtype: str """ - return self.account_id.split('@')[0] + return self.account_id.split("@")[0] @property def account_domain(self): @@ -100,7 +146,7 @@ def account_domain(self): :return: The account domain. :rtype: str """ - return self.account_id.split('@')[1] + return self.account_id.split("@")[1] @property def public_key(self): @@ -110,4 +156,4 @@ def public_key(self): :return: The public key. :rtype: str """ - return self._config["account"]['public_key'].split('ed0120')[1] + return self._config["account"]["public_key"].split("ed0120")[1] diff --git a/client_cli/pytests/src/client_cli/have.py b/client_cli/pytests/src/client_cli/have.py index 3bb51878891..db660fba956 100644 --- a/client_cli/pytests/src/client_cli/have.py +++ b/client_cli/pytests/src/client_cli/have.py @@ -3,20 +3,28 @@ """ import json -import allure + +import allure # type: ignore from src.client_cli import client_cli, iroha, match def expected_in_actual(expected, actual) -> bool: + """ + Check if the expected result is present in the actual result. + + :param expected: The expected result. + :param actual: The actual result. + :return: True if expected is in actual, False otherwise. + """ allure.attach( - json.dumps(actual), - name='actual', - attachment_type=allure.attachment_type.JSON) + json.dumps(actual), name="actual", attachment_type=allure.attachment_type.JSON + ) allure.attach( json.dumps(expected), - name='expected', - attachment_type=allure.attachment_type.JSON) + name="expected", + attachment_type=allure.attachment_type.JSON, + ) return expected in actual @@ -28,16 +36,18 @@ def domain(expected, owned_by=None): :param expected: The expected domain object. :param owned_by: The owner of the domain, default is None. - :return: True if the domain is present (and owned by the specified owner if provided), False otherwise. + :return: True if the domain is present and owned by the specified owner if provided. """ def domain_in_domains() -> bool: - domains = iroha.list_filter(f'{{"Identifiable": {{"Is": "{expected}"}}}}').domains() + domains = iroha.list_filter( + f'{{"Identifiable": {{"Is": "{expected}"}}}}' + ).domains() if not expected_in_actual(expected, domains): return False if owned_by: domain_info = domains.get(expected) - if not domain_info or domain_info.get('owned_by') != str(owned_by): + if not domain_info or domain_info.get("owned_by") != str(owned_by): return False return True @@ -54,7 +64,9 @@ def account(expected): """ def account_in_accounts() -> bool: - accounts = iroha.list_filter(f'{{"Identifiable": {{"Is": "{expected}"}}}}').accounts() + accounts = iroha.list_filter( + f'{{"Identifiable": {{"Is": "{expected}"}}}}' + ).accounts() return expected_in_actual(expected, accounts) return client_cli.wait_for(account_in_accounts) @@ -67,10 +79,12 @@ def asset_definition(expected): :param expected: The expected asset definition object. :return: True if the asset definition is present, False otherwise. """ - expected_domain = expected.split('#')[1] + expected_domain = expected.split("#")[1] def asset_definition_in_asset_definitions() -> bool: - asset_definitions = iroha.list_filter(f'{{"Identifiable": {{"Is": "{expected_domain}"}}}}').asset_definitions() + asset_definitions = iroha.list_filter( + f'{{"Identifiable": {{"Is": "{expected_domain}"}}}}' + ).asset_definitions() return expected_in_actual(expected, asset_definitions) return client_cli.wait_for(asset_definition_in_asset_definitions) @@ -85,11 +99,14 @@ def asset(expected): """ def asset_in_assets() -> bool: - assets = iroha.list_filter(f'{{"Identifiable": {{"Is": "{expected}"}}}}').assets() + assets = iroha.list_filter( + f'{{"Identifiable": {{"Is": "{expected}"}}}}' + ).assets() return expected_in_actual(expected, assets) return client_cli.wait_for(asset_in_assets) + def asset_has_quantity(expected_asset_id, expected_quantity): """ Check if the expected asset quantity is present in the list of assets. @@ -100,29 +117,33 @@ def asset_has_quantity(expected_asset_id, expected_quantity): """ def check_quantity() -> bool: - assets = iroha.list_filter(f'{{"Identifiable": {{"Is": "{expected_asset_id}"}}}}').assets() + assets = iroha.list_filter( + f'{{"Identifiable": {{"Is": "{expected_asset_id}"}}}}' + ).assets() actual_quantity = None - for asset in assets: - if asset == expected_asset_id: + for asset_item in assets: + if asset_item == expected_asset_id: actual_quantity = assets.get(expected_asset_id, {})["value"]["Quantity"] break - if actual_quantity is None: - raise Exception(f"Asset with ID {expected_asset_id} not found.") + raise ValueError(f"Asset with ID {expected_asset_id} not found.") allure.attach( json.dumps(actual_quantity), - name='actual_quantity', - attachment_type=allure.attachment_type.JSON) + name="actual_quantity", + attachment_type=allure.attachment_type.JSON, + ) allure.attach( json.dumps(expected_quantity), - name='expected_quantity', - attachment_type=allure.attachment_type.JSON) + name="expected_quantity", + attachment_type=allure.attachment_type.JSON, + ) return expected_quantity == str(actual_quantity) return client_cli.wait_for(check_quantity) + def error(expected): """ Check if the expected error is present in the client_cli stderr. @@ -130,6 +151,4 @@ def error(expected): :param expected: The expected error message. :return: True if the error is present, False otherwise. """ - return match.client_cli_have_error( - expected=expected, - actual=client_cli.stderr) + return match.client_cli_have_error(expected=expected, actual=client_cli.stderr) diff --git a/client_cli/pytests/src/client_cli/iroha.py b/client_cli/pytests/src/client_cli/iroha.py index e62e694dc6f..602bbe8a838 100644 --- a/client_cli/pytests/src/client_cli/iroha.py +++ b/client_cli/pytests/src/client_cli/iroha.py @@ -4,7 +4,8 @@ import json from typing import Dict, List -from src.client_cli.client_cli import ClientCli, Config + +from src.client_cli.client_cli import ClientCli class Iroha(ClientCli): @@ -13,15 +14,6 @@ class Iroha(ClientCli): for interacting with the Iroha network. """ - def __init__(self, config: Config): - """ - :param config: A configuration object containing the details for the client. - :type config: Config - :param path: The path where the client executable is located. - :type path: str - """ - super().__init__(config) - def _execute_command(self, command_name: str): """ Execute a command by inserting the command_name into the command list and then executing it. @@ -32,17 +24,29 @@ def _execute_command(self, command_name: str): self.command.insert(3, command_name) self.execute() - def should(self, _expected): + def should(self, *args, **kwargs): """ Placeholder method for implementing assertions. - :param expected: The expected value. - :type expected: str + :param kwargs: :return: The current Iroha object. :rtype: Iroha """ return self + def should_not(self, func): + """ + Decorator that inverts the result of the check function. + + :param func: The function to be inverted. + :return: Inverted result of the function. + """ + + def wrapper(*args, **kwargs): + return not func(*args, **kwargs) + + return wrapper + def domains(self) -> Dict[str, Dict]: """ Retrieve domains from the Iroha network and return then as list of ids. @@ -50,15 +54,18 @@ def domains(self) -> Dict[str, Dict]: :return: List of domains ids. :rtype: List[str] """ - self._execute_command('domain') - try: - domains = json.loads(self.stdout) - except json.decoder.JSONDecodeError as e: - print(f"JSON decode error occurred with this input:", self.stdout) - print(f"STDERR:", self.stderr) - raise - domains_dict = { domain["id"]: domain for domain in domains } - return domains_dict + self._execute_command("domain") + if self.stdout is not None: + try: + domains = json.loads(self.stdout) + except json.decoder.JSONDecodeError: + print("JSON decode error occurred with this input:", self.stdout) + print("STDERR:", self.stderr) + raise + domains_dict = {domain["id"]: domain for domain in domains} + return domains_dict + else: + return {} def accounts(self) -> List[str]: """ @@ -67,10 +74,13 @@ def accounts(self) -> List[str]: :return: List of accounts ids. :rtype: List[str] """ - self._execute_command('account') - accounts = json.loads(self.stdout) - accounts = [account["id"] for account in accounts] - return accounts + self._execute_command("account") + if self.stdout is not None: + accounts = json.loads(self.stdout) + accounts = [account["id"] for account in accounts] + return accounts + else: + return [] def assets(self) -> Dict[str, str]: """ @@ -80,10 +90,13 @@ def assets(self) -> Dict[str, str]: :return: Dictionary of assets. :rtype: Dict[str, Any] """ - self._execute_command('asset') - assets = json.loads(self.stdout) - asset_dict = {asset["id"]: asset for asset in assets} - return asset_dict + self._execute_command("asset") + if self.stdout is not None: + assets = json.loads(self.stdout) + asset_dict = {asset["id"]: asset for asset in assets} + return asset_dict + else: + return {} def asset_definitions(self) -> Dict[str, str]: """ @@ -93,13 +106,16 @@ def asset_definitions(self) -> Dict[str, str]: :return: Dict of asset definitions ids with there value type. :rtype: Dict[str, str] """ - self._execute_command('domain') - domains = json.loads(self.stdout) - asset_definitions = {} - for domain in domains: - asset_defs = domain.get('asset_definitions') - for asset_def in asset_defs.values(): - value_type = asset_def.get('value_type') - if value_type: - asset_definitions[asset_def['id']] = value_type - return asset_definitions + self._execute_command("domain") + if self.stdout is not None: + domains = json.loads(self.stdout) + asset_definitions = {} + for domain in domains: + asset_defs = domain.get("asset_definitions") + for asset_def in asset_defs.values(): + value_type = asset_def.get("value_type") + if value_type: + asset_definitions[asset_def["id"]] = value_type + return asset_definitions + else: + return {} diff --git a/client_cli/pytests/src/client_cli/match.py b/client_cli/pytests/src/client_cli/match.py index 4aa9b65cadf..9f597495c5d 100644 --- a/client_cli/pytests/src/client_cli/match.py +++ b/client_cli/pytests/src/client_cli/match.py @@ -2,7 +2,8 @@ This module provides helper functions for matching expected and actual values in Iroha objects. """ -import allure +import allure # type: ignore + def client_cli_have_error(expected: str, actual: str): """ @@ -14,6 +15,10 @@ def client_cli_have_error(expected: str, actual: str): try: assert expected in actual except AssertionError as error: - allure.attach(actual, name='actual', attachment_type=allure.attachment_type.TEXT) - allure.attach(expected, name='expected', attachment_type=allure.attachment_type.TEXT) + allure.attach( + actual, name="actual", attachment_type=allure.attachment_type.TEXT + ) + allure.attach( + expected, name="expected", attachment_type=allure.attachment_type.TEXT + ) raise error diff --git a/client_cli/pytests/test/__init__.py b/client_cli/pytests/test/__init__.py index 1eebe6afb9f..64b1c8440b3 100644 --- a/client_cli/pytests/test/__init__.py +++ b/client_cli/pytests/test/__init__.py @@ -1,25 +1,33 @@ """ This module provides access to fixtures for testing. """ + from .conftest import ( GIVEN_127_lenght_name, GIVEN_128_lenght_name, GIVEN_129_lenght_name, - GIVEN_new_one_existing_account, - GIVEN_existing_asset_definition_with_quantity_value_type, - GIVEN_existing_asset_definition_with_store_value_type, - GIVEN_existing_domain_with_uppercase_letter, - GIVEN_new_one_existing_domain, + GIVEN_big_quantity_value_type, + GIVEN_currently_account_quantity_with_two_quantity_of_asset, + GIVEN_currently_authorized_account, GIVEN_fake_asset_name, GIVEN_fake_name, GIVEN_key_with_invalid_character_in_key, + GIVEN_minted_asset_quantity, GIVEN_not_existing_name, GIVEN_public_key, + GIVEN_quantity_asset_for_account, GIVEN_quantity_value, + GIVEN_quantity_value_type, GIVEN_random_character, - before_each, + GIVEN_registered_account, + GIVEN_registered_asset_definition_with_big_quantity_value_type, + GIVEN_registered_asset_definition_with_quantity_value_type, + GIVEN_registered_asset_definition_with_store_value_type, + GIVEN_registered_domain, + GIVEN_registered_domain_with_uppercase_letter, + GIVEN_store_value_type, GIVEN_string_with_reserved_character, GIVEN_string_with_whitespaces, - GIVEN_quantity_value_type, - GIVEN_currently_authorized_account, - GIVEN_currently_account_quantity_with_two_quantity_of_asset) + before_all, + before_each, +) diff --git a/client_cli/pytests/test/accounts/conftest.py b/client_cli/pytests/test/accounts/conftest.py index b925774b068..063ec22ef3f 100644 --- a/client_cli/pytests/test/accounts/conftest.py +++ b/client_cli/pytests/test/accounts/conftest.py @@ -1,19 +1,22 @@ -import pytest -import allure - from test import ( GIVEN_127_lenght_name, GIVEN_129_lenght_name, - GIVEN_new_one_existing_account, - GIVEN_new_one_existing_domain, GIVEN_fake_name, GIVEN_key_with_invalid_character_in_key, GIVEN_not_existing_name, GIVEN_public_key, GIVEN_random_character, - before_each) + GIVEN_registered_account, + GIVEN_registered_domain, + before_all, + before_each, +) + +import allure # type: ignore +import pytest + @pytest.fixture(scope="function", autouse=True) def account_test_setup(): - allure.dynamic.feature('Accounts') - allure.dynamic.label('permission', 'no_permission_required') + allure.dynamic.feature("Accounts") + allure.dynamic.label("permission", "no_permission_required") diff --git a/client_cli/pytests/test/accounts/test_accounts_query_filters.py b/client_cli/pytests/test/accounts/test_accounts_query_filters.py index cd76a8a18da..bb13afe52bf 100644 --- a/client_cli/pytests/test/accounts/test_accounts_query_filters.py +++ b/client_cli/pytests/test/accounts/test_accounts_query_filters.py @@ -1,51 +1,66 @@ import json -import allure -from src.client_cli import iroha, client_cli +import allure # type: ignore + +from src.client_cli import client_cli, iroha + # using existing account to have at least one account in response -def test_filter_by_domain(GIVEN_new_one_existing_account): +def test_filter_by_domain(GIVEN_registered_account): def condition(): - domain = GIVEN_new_one_existing_account.domain - with allure.step( - f'WHEN client_cli query accounts ' - f'in the "{domain}" domain'): - accounts = iroha.list_filter(f'{{"Identifiable": {{"EndsWith": "@{domain}"}}}}').accounts() + domain = GIVEN_registered_account.domain with allure.step( - f'THEN Iroha should return only accounts with this domain'): + f"WHEN client_cli query accounts " f'in the "{domain}" domain' + ): + accounts = iroha.list_filter( + {"Identifiable": {"EndsWith": f"@{domain}"}} + ).accounts() + with allure.step("THEN Iroha should return only accounts with this domain"): allure.attach( json.dumps(accounts), - name='accounts', - attachment_type=allure.attachment_type.JSON) + name="accounts", + attachment_type=allure.attachment_type.JSON, + ) return accounts and all(account.endswith(domain) for account in accounts) + client_cli.wait_for(condition) -def test_filter_by_account_name(GIVEN_new_one_existing_account): + +def test_filter_by_account_name(GIVEN_registered_account): def condition(): - name = GIVEN_new_one_existing_account.name - with allure.step( - f'WHEN client_cli query accounts with name "{name}"'): - accounts = iroha.list_filter(f'{{"Identifiable": {{"StartsWith": "{name}@"}}}}').accounts() - with allure.step( - f'THEN Iroha should return only accounts with this name'): + name = GIVEN_registered_account.name + with allure.step(f'WHEN client_cli query accounts with name "{name}"'): + accounts = iroha.list_filter( + {"Identifiable": {"StartsWith": f"{name}@"}} + ).accounts() + with allure.step("THEN Iroha should return only accounts with this name"): allure.attach( json.dumps(accounts), - name='accounts', - attachment_type=allure.attachment_type.JSON) + name="accounts", + attachment_type=allure.attachment_type.JSON, + ) return accounts and all(account.startswith(name) for account in accounts) + client_cli.wait_for(condition) -def test_filter_by_account_id(GIVEN_new_one_existing_account): + +def test_filter_by_account_id(GIVEN_registered_account): def condition(): - account_id = GIVEN_new_one_existing_account.name + "@" + GIVEN_new_one_existing_account.domain + account_id = ( + GIVEN_registered_account.name + "@" + GIVEN_registered_account.domain + ) with allure.step( - f'WHEN client_cli query accounts with account id "{account_id}"'): - accounts = iroha.list_filter(f'{{"Identifiable": {{"Is": "{account_id}"}}}}').accounts() - with allure.step( - f'THEN Iroha should return only accounts with this id'): + f'WHEN client_cli query accounts with account id "{account_id}"' + ): + accounts = iroha.list_filter( + {"Identifiable": {"Is": account_id}} + ).accounts() + with allure.step("THEN Iroha should return only accounts with this id"): allure.attach( json.dumps(accounts), - name='accounts', - attachment_type=allure.attachment_type.JSON) + name="accounts", + attachment_type=allure.attachment_type.JSON, + ) return accounts and all(account == account_id for account in accounts) + client_cli.wait_for(condition) diff --git a/client_cli/pytests/test/accounts/test_register_accounts.py b/client_cli/pytests/test/accounts/test_register_accounts.py index c2954337bee..d328a22cb30 100644 --- a/client_cli/pytests/test/accounts/test_register_accounts.py +++ b/client_cli/pytests/test/accounts/test_register_accounts.py @@ -1,141 +1,153 @@ -import allure +import allure # type: ignore import pytest from common.consts import Stderr from src.client_cli import client_cli, have, iroha + @pytest.fixture(scope="function", autouse=True) def story_account_register_account(): - allure.dynamic.story('Account registers an account') - allure.dynamic.label('permission', 'no_permission_required') - -@allure.label('sdk_test_id', 'register_account') -def test_register_account( - GIVEN_fake_name, - GIVEN_new_one_existing_domain, - GIVEN_public_key): + allure.dynamic.story("Account registers an account") + allure.dynamic.label("permission", "no_permission_required") + + +@allure.label("sdk_test_id", "register_account") +def test_register_account(GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key): with allure.step( - f'WHEN client_cli registers the account "{GIVEN_fake_name}" ' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): + f'WHEN client_cli registers the account "{GIVEN_fake_name}" ' + f'in the "{GIVEN_registered_domain.name}" domain' + ): client_cli.register().account( account=GIVEN_fake_name, - domain=GIVEN_new_one_existing_domain.name, - key=GIVEN_public_key) - registered = GIVEN_fake_name + '@' + GIVEN_new_one_existing_domain.name - with allure.step( - f'THEN Iroha should have the "{registered}" account'): + domain=GIVEN_registered_domain.name, + key=GIVEN_public_key, + ) + registered = GIVEN_fake_name + "@" + GIVEN_registered_domain.name + with allure.step(f'THEN Iroha should have the "{registered}" account'): iroha.should(have.account(registered)) -@allure.label('sdk_test_id', 'register_account_with_two_public_keys') +@allure.label("sdk_test_id", "register_account_with_two_public_keys") @pytest.mark.xfail(reason="TO DO") def test_register_account_with_two_public_keys( - GIVEN_fake_name, - GIVEN_new_one_existing_domain, - GIVEN_public_key): + GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key +): assert 0 -@allure.label('sdk_test_id', 'register_account_with_empty_name') -def test_register_account_with_empty_name( - GIVEN_new_one_existing_domain, - GIVEN_public_key): +@allure.label("sdk_test_id", "register_account_with_empty_name") +def test_register_account_with_empty_name(GIVEN_registered_domain, GIVEN_public_key): with allure.step( - f'WHEN client_cli tries to register an account with an empty name ' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): - client_cli.register().account(account='', domain=GIVEN_new_one_existing_domain.name, key=GIVEN_public_key) + f"WHEN client_cli tries to register an account with an empty name " + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().account( + account="", domain=GIVEN_registered_domain.name, key=GIVEN_public_key + ) with allure.step( - f'THEN сlient_cli should have the account error: "{Stderr.CANNOT_BE_EMPTY}"'): + f'THEN сlient_cli should have the account error: "{Stderr.CANNOT_BE_EMPTY}"' + ): client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) - -@allure.label('sdk_test_id', 'register_account_with_existing_name') +@allure.label("sdk_test_id", "register_account_with_existing_name") def test_register_account_with_existing_name( - GIVEN_new_one_existing_domain, - GIVEN_public_key, - GIVEN_new_one_existing_account): + GIVEN_registered_domain, GIVEN_public_key, GIVEN_registered_account +): with allure.step( - f'WHEN client_cli tries to register an account ' - f'with the same name "{GIVEN_new_one_existing_domain.name}" ' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): - client_cli.register().account(account=GIVEN_new_one_existing_account.name, domain=GIVEN_new_one_existing_account.domain, - key=GIVEN_new_one_existing_account.public_key) + f"WHEN client_cli tries to register an account " + f'with the same name "{GIVEN_registered_domain.name}" ' + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().account( + account=GIVEN_registered_account.name, + domain=GIVEN_registered_account.domain, + key=GIVEN_registered_account.public_key, + ) with allure.step( - f'THEN client_cli should have the account error: "{GIVEN_new_one_existing_domain.name}"'): + f'THEN client_cli should have the account error: "{GIVEN_registered_domain.name}"' + ): client_cli.should(have.error(Stderr.REPETITION.value)) - -@allure.label('sdk_test_id', 'register_account_with_invalid_domain') +@allure.label("sdk_test_id", "register_account_with_invalid_domain") def test_register_account_with_invalid_domain( - GIVEN_fake_name, - GIVEN_not_existing_name, - GIVEN_public_key, ): - with allure.step( - 'WHEN client_cli tries to register an account with an invalid domain'): - client_cli.register().account(account=GIVEN_fake_name, domain=GIVEN_not_existing_name, key=GIVEN_public_key) + GIVEN_fake_name, + GIVEN_not_existing_name, + GIVEN_public_key, +): with allure.step( - 'THEN client_cli should have the error'): + "WHEN client_cli tries to register an account with an invalid domain" + ): + client_cli.register().account( + account=GIVEN_fake_name, + domain=GIVEN_not_existing_name, + key=GIVEN_public_key, + ) + with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.FAILED_TO_FIND_DOMAIN.value)) - -@allure.label('sdk_test_id', 'register_account_with_invalid_character_in_key') +@allure.label("sdk_test_id", "register_account_with_invalid_character_in_key") def test_register_account_with_invalid_character_in_key( - GIVEN_fake_name, - GIVEN_new_one_existing_domain, - GIVEN_key_with_invalid_character_in_key): - with allure.step( - 'WHEN client_cli tries to register an account with invalid character in the key'): - client_cli.register().account(account=GIVEN_fake_name, domain=GIVEN_new_one_existing_domain.name, - key=GIVEN_key_with_invalid_character_in_key) + GIVEN_fake_name, GIVEN_registered_domain, GIVEN_key_with_invalid_character_in_key +): with allure.step( - 'THEN client_cli should have the error'): + "WHEN client_cli tries to register an account with invalid character in the key" + ): + client_cli.register().account( + account=GIVEN_fake_name, + domain=GIVEN_registered_domain.name, + key=GIVEN_key_with_invalid_character_in_key, + ) + with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.INVALID_CHARACTER.value)) -@allure.label('sdk_test_id', 'register_account_with_max_name') +@allure.label("sdk_test_id", "register_account_with_max_name") def test_register_account_with_max_name( - GIVEN_127_lenght_name, - GIVEN_new_one_existing_domain, - GIVEN_public_key): - with allure.step( - 'WHEN client_cli register an account with the 127 lenght name'): - client_cli.register().account(account=GIVEN_127_lenght_name, domain=GIVEN_new_one_existing_domain.name, - key=GIVEN_public_key) - registered = GIVEN_127_lenght_name + '@' + GIVEN_new_one_existing_domain.name - with allure.step( - f'THEN Iroha should have the "{registered}" account'): + GIVEN_127_lenght_name, GIVEN_registered_domain, GIVEN_public_key +): + with allure.step("WHEN client_cli register an account with the 127 lenght name"): + client_cli.register().account( + account=GIVEN_127_lenght_name, + domain=GIVEN_registered_domain.name, + key=GIVEN_public_key, + ) + registered = GIVEN_127_lenght_name + "@" + GIVEN_registered_domain.name + with allure.step(f'THEN Iroha should have the "{registered}" account'): iroha.should(have.account(registered)) - -@allure.label('sdk_test_id', 'register_account_with_special_characters') +@allure.label("sdk_test_id", "register_account_with_special_characters") @pytest.mark.xfail(reason="TO DO") def test_register_account_with_special_characters( - GIVEN_new_one_existing_domain, - GIVEN_public_key): + GIVEN_registered_domain, GIVEN_public_key +): assert 0 -@allure.label('sdk_test_id', 'register_account_with_long_account_name') +@allure.label("sdk_test_id", "register_account_with_long_account_name") def test_register_account_with_long_account_name( - GIVEN_new_one_existing_domain, - GIVEN_129_lenght_name, - GIVEN_public_key): + GIVEN_registered_domain, GIVEN_129_lenght_name, GIVEN_public_key +): with allure.step( - 'WHEN client_cli tries to register an account with a name with 129 characters'): - client_cli.register().account(account=GIVEN_129_lenght_name, domain=GIVEN_new_one_existing_domain.name, - key=GIVEN_public_key) + "WHEN client_cli tries to register an account with a name with 129 characters" + ): + client_cli.register().account( + account=GIVEN_129_lenght_name, + domain=GIVEN_registered_domain.name, + key=GIVEN_public_key, + ) with allure.step( - f'THEN client_cli should have the name error: "{Stderr.TOO_LONG}"'): + f'THEN client_cli should have the name error: "{Stderr.TOO_LONG}"' + ): client_cli.should(have.error(Stderr.TOO_LONG.value)) -@allure.label('sdk_test_id', 'register_account_with_metadata') + +@allure.label("sdk_test_id", "register_account_with_metadata") @pytest.mark.xfail(reason="TO DO") def test_register_account_with_metadata( - GIVEN_fake_name, - GIVEN_new_one_existing_domain, - GIVEN_public_key): + GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key +): assert 0 diff --git a/client_cli/pytests/test/accounts/test_set_key_value_pair.py b/client_cli/pytests/test/accounts/test_set_key_value_pair.py index 406bf9726b9..86a4d9b9b90 100644 --- a/client_cli/pytests/test/accounts/test_set_key_value_pair.py +++ b/client_cli/pytests/test/accounts/test_set_key_value_pair.py @@ -1,22 +1,27 @@ -import allure +import allure # type: ignore import pytest + @pytest.fixture(scope="function", autouse=True) def story_client_change_account_metadata(): allure.dynamic.story("Account set key value pair") -@allure.label('sdk_test_id', 'set_key_value_in_foreign_asset_after_granting_role') + +@allure.label("sdk_test_id", "set_key_value_in_foreign_asset_after_granting_role") @pytest.mark.xfail(reason="TO DO") def test_set_key_value_in_foreign_asset_after_granting_role( - GIVEN_currently_authorized_account, - GIVEN_new_one_existing_account, - GIVEN_existing_asset_definition_with_store_value_type): + GIVEN_currently_authorized_account, + GIVEN_registered_account, + GIVEN_registered_asset_definition_with_store_value_type, +): assert 0 -@allure.label('sdk_test_id', 'set_key_value_pair_for_another_account_asset_definition') + +@allure.label("sdk_test_id", "set_key_value_pair_for_another_account_asset_definition") @pytest.mark.xfail(reason="TO DO") def test_set_key_value_pair_for_another_account_asset_definition( - GIVEN_currently_authorized_account, - GIVEN_new_one_existing_account, - GIVEN_existing_asset_definition_with_store_value_type): + GIVEN_currently_authorized_account, + GIVEN_registered_account, + GIVEN_registered_asset_definition_with_store_value_type, +): assert 0 diff --git a/client_cli/pytests/test/assets/conftest.py b/client_cli/pytests/test/assets/conftest.py index 1e71664f7c4..a1611d3ba51 100644 --- a/client_cli/pytests/test/assets/conftest.py +++ b/client_cli/pytests/test/assets/conftest.py @@ -1,21 +1,31 @@ -import pytest -import allure - from test import ( - GIVEN_new_one_existing_account, - GIVEN_existing_asset_definition_with_quantity_value_type, - GIVEN_existing_asset_definition_with_store_value_type, - GIVEN_new_one_existing_domain, + GIVEN_129_lenght_name, + GIVEN_big_quantity_value_type, + GIVEN_currently_account_quantity_with_two_quantity_of_asset, + GIVEN_currently_authorized_account, GIVEN_fake_asset_name, GIVEN_fake_name, + GIVEN_minted_asset_quantity, + GIVEN_not_existing_name, GIVEN_public_key, + GIVEN_quantity_asset_for_account, GIVEN_quantity_value, GIVEN_quantity_value_type, - GIVEN_currently_authorized_account, - GIVEN_currently_account_quantity_with_two_quantity_of_asset, - before_each) + GIVEN_registered_account, + GIVEN_registered_asset_definition_with_big_quantity_value_type, + GIVEN_registered_asset_definition_with_quantity_value_type, + GIVEN_registered_asset_definition_with_store_value_type, + GIVEN_registered_domain, + GIVEN_store_value_type, + before_all, + before_each, +) + +import allure # type: ignore +import pytest + @pytest.fixture(scope="function", autouse=True) def asset_test_setup(): - allure.dynamic.feature('Assets') - allure.dynamic.label('permission', 'no_permission_required') + allure.dynamic.feature("Assets") + allure.dynamic.label("permission", "no_permission_required") diff --git a/client_cli/pytests/test/assets/test_assets_query_filters.py b/client_cli/pytests/test/assets/test_assets_query_filters.py index 789f27d2f1f..1a44e6e8683 100644 --- a/client_cli/pytests/test/assets/test_assets_query_filters.py +++ b/client_cli/pytests/test/assets/test_assets_query_filters.py @@ -1,54 +1,86 @@ import json -import allure -from src.client_cli import iroha, client_cli +import allure # type: ignore + +from src.client_cli import client_cli, iroha + # using existing account with asset to have at least one in response def test_filter_by_domain(GIVEN_currently_account_quantity_with_two_quantity_of_asset): def condition(): - domain = GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.domain - with allure.step( - f'WHEN client_cli query assets' - f'in the "{domain}" domain'): + domain = ( + GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.domain + ) + with allure.step(f"WHEN client_cli query assets" f'in the "{domain}" domain'): assets = iroha.list_filter( - f'{{"Or": [{{"Identifiable": {{"Contains": "#{domain}#"}}}}, {{"And": [{{"Identifiable": {{"Contains": "##"}}}}, {{"Identifiable": {{"EndsWith": "@{domain}"}}}}]}}]}}' + { + "Or": [ + {"Identifiable": {"Contains": f"#{domain}#"}}, + { + "And": [ + {"Identifiable": {"Contains": "##"}}, + {"Identifiable": {"EndsWith": f"@{domain}"}}, + ] + }, + ] + } ).assets() - with allure.step( - f'THEN Iroha should return only assets with this domain'): + with allure.step("THEN Iroha should return only assets with this domain"): allure.attach( json.dumps(assets), - name='assets', - attachment_type=allure.attachment_type.JSON) - return assets and all(f'#{domain}#' in asset or ('##' in asset and f"@{domain}" in asset) for asset in assets) + name="assets", + attachment_type=allure.attachment_type.JSON, + ) + return assets and all( + f"#{domain}#" in asset or ("##" in asset and f"@{domain}" in asset) + for asset in assets + ) + client_cli.wait_for(condition) - -def test_filter_by_asset_name(GIVEN_currently_account_quantity_with_two_quantity_of_asset): + +def test_filter_by_asset_name( + GIVEN_currently_account_quantity_with_two_quantity_of_asset, +): def condition(): - name = GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name - with allure.step( - f'WHEN client_cli query assets with name "{name}"'): - assets = iroha.list_filter(f'{{"Identifiable": {{"StartsWith": "{name}#"}}}}').assets() - with allure.step( - f'THEN Iroha should return only assets with this name'): + name = ( + GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name + ) + with allure.step(f'WHEN client_cli query assets with name "{name}"'): + assets = iroha.list_filter( + {"Identifiable": {"StartsWith": f"{name}#"}} + ).assets() + with allure.step("THEN Iroha should return only assets with this name"): allure.attach( json.dumps(assets), - name='assets', - attachment_type=allure.attachment_type.JSON) + name="assets", + attachment_type=allure.attachment_type.JSON, + ) return assets and all(asset.startswith(name) for asset in assets) + client_cli.wait_for(condition) -def test_filter_by_asset_id(GIVEN_currently_authorized_account, GIVEN_currently_account_quantity_with_two_quantity_of_asset): + +def test_filter_by_asset_id( + GIVEN_currently_authorized_account, + GIVEN_currently_account_quantity_with_two_quantity_of_asset, +): def condition(): - asset_id = GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name + "##" + GIVEN_currently_authorized_account.name + "@" + GIVEN_currently_authorized_account.domain - with allure.step( - f'WHEN client_cli query assets with asset id "{asset_id}"'): - assets = iroha.list_filter(f'{{"Identifiable": {{"Is": "{asset_id}"}}}}').assets() - with allure.step( - f'THEN Iroha should return only assets with this id'): + asset_id = ( + GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name + + "##" + + GIVEN_currently_authorized_account.name + + "@" + + GIVEN_currently_authorized_account.domain + ) + with allure.step(f'WHEN client_cli query assets with asset id "{asset_id}"'): + assets = iroha.list_filter({"Identifiable": {"Is": asset_id}}).assets() + with allure.step("THEN Iroha should return only assets with this id"): allure.attach( json.dumps(assets), - name='assets', - attachment_type=allure.attachment_type.JSON) + name="assets", + attachment_type=allure.attachment_type.JSON, + ) return assets and all(asset == asset_id for asset in assets) + client_cli.wait_for(condition) diff --git a/client_cli/pytests/test/assets/test_burn_assets.py b/client_cli/pytests/test/assets/test_burn_assets.py index 4c53db92ac6..6be80183ca5 100644 --- a/client_cli/pytests/test/assets/test_burn_assets.py +++ b/client_cli/pytests/test/assets/test_burn_assets.py @@ -1,4 +1,4 @@ -import allure +import allure # type: ignore import pytest from src.client_cli import client_cli, have, iroha @@ -6,50 +6,61 @@ @pytest.fixture(scope="function", autouse=True) def story_client_burn_asset(): - allure.dynamic.story('Account burn an asset') + allure.dynamic.story("Account burn an asset") -@allure.label('sdk_test_id', 'burn_asset_for_account_in_same_domain') -@allure.label('permission', 'no_permission_required') +@allure.label("sdk_test_id", "burn_asset_for_account_in_same_domain") +@allure.label("permission", "no_permission_required") def test_burn_asset_for_account_in_same_domain( - GIVEN_currently_authorized_account, - GIVEN_currently_account_quantity_with_two_quantity_of_asset): - with allure.step(f'WHEN {GIVEN_currently_authorized_account.name} burns 1 Quantity' - f'of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}'): + GIVEN_currently_authorized_account, + GIVEN_currently_account_quantity_with_two_quantity_of_asset, +): + with allure.step( + f"WHEN {GIVEN_currently_authorized_account.name} burns 1 Quantity" + f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + ): client_cli.burn( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, account=GIVEN_currently_authorized_account, - quantity="1") - with allure.step(f'THEN {GIVEN_currently_authorized_account.name} has 1 Quantity of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}'): - iroha.should(have.asset_has_quantity( - f'{GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}##{GIVEN_currently_authorized_account}', - '1')) + quantity="1", + ) + with allure.step( + f"THEN {GIVEN_currently_authorized_account.name} " + f"has 1 Quantity of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + ): + iroha.should( + have.asset_has_quantity( + f"{GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}##" + f"{GIVEN_currently_authorized_account}", + "1", + ) + ) -@allure.label('sdk_test_id', 'burn_other_user_asset') -@allure.label('permission', 'can_burn_assets_with_definition') +@allure.label("sdk_test_id", "burn_other_user_asset") +@allure.label("permission", "can_burn_assets_with_definition") @pytest.mark.xfail(reason="TO DO") def test_burn_other_user_asset( - GIVEN_existing_asset_definition_with_quantity_value_type, - GIVEN_new_one_existing_account, - GIVEN_quantity_value): + GIVEN_registered_asset_definition_with_quantity_value_type, + GIVEN_registered_account, + GIVEN_quantity_value, +): assert 0 -@allure.label('sdk_test_id', 'burn_asset_if_condition') + +@allure.label("sdk_test_id", "burn_asset_if_condition") @pytest.mark.xfail(reason="TO DO") -def test_burn_asset_if_condition( - GIVEN_currently_authorized_account): +def test_burn_asset_if_condition(GIVEN_currently_authorized_account): assert 0 -@allure.label('sdk_test_id', 'not_burn_asset_if_condition_not_met') + +@allure.label("sdk_test_id", "not_burn_asset_if_condition_not_met") @pytest.mark.xfail(reason="TO DO") -def test_not_burn_asset_if_condition_not_met( - GIVEN_currently_authorized_account): +def test_not_burn_asset_if_condition_not_met(GIVEN_currently_authorized_account): assert 0 @allure.label("sdk_test_id", "burn_fixed_asset") @pytest.mark.xfail(reason="TO DO") -def test_burn_fixed_asset( - GIVEN_new_one_existing_account): +def test_burn_fixed_asset(GIVEN_registered_account): assert 0 diff --git a/client_cli/pytests/test/assets/test_mint_assets.py b/client_cli/pytests/test/assets/test_mint_assets.py index 198ddac3ca2..8f95077f271 100644 --- a/client_cli/pytests/test/assets/test_mint_assets.py +++ b/client_cli/pytests/test/assets/test_mint_assets.py @@ -1,4 +1,4 @@ -import allure +import allure # type: ignore import pytest from src.client_cli import client_cli, have, iroha @@ -6,31 +6,85 @@ @pytest.fixture(scope="function", autouse=True) def story_account_mint_asset(): - allure.dynamic.story('Account mints an asset') + allure.dynamic.story("Account mints an asset") -@allure.label('sdk_test_id', 'mint_asset_for_account_in_same_domain') + +@allure.label("sdk_test_id", "mint_asset_for_account_in_same_domain") def test_mint_asset_for_account_in_same_domain( - GIVEN_existing_asset_definition_with_quantity_value_type, - GIVEN_new_one_existing_account, - GIVEN_quantity_value): + GIVEN_registered_asset_definition_with_quantity_value_type, + GIVEN_registered_account, + GIVEN_quantity_value, +): with allure.step( - f'WHEN client_cli mint the asset "{GIVEN_existing_asset_definition_with_quantity_value_type.name}" ' - f'for the "{GIVEN_new_one_existing_account.name}" ' - f'in the "{GIVEN_existing_asset_definition_with_quantity_value_type.domain}" domain' + f'WHEN client_cli mint the asset "{GIVEN_registered_asset_definition_with_quantity_value_type.name}" ' + f'for the "{GIVEN_registered_account.name}" ' + f'in the "{GIVEN_registered_asset_definition_with_quantity_value_type.domain}" domain' ): client_cli.mint().asset( - account=GIVEN_new_one_existing_account, - asset_definition=GIVEN_existing_asset_definition_with_quantity_value_type, - value_of_value_type=GIVEN_quantity_value + account=GIVEN_registered_account, + asset_definition=GIVEN_registered_asset_definition_with_quantity_value_type, + value_of_value_type=GIVEN_quantity_value, ) with allure.step( - f'THEN "{GIVEN_new_one_existing_account}" account ' - f'should have the "{GIVEN_existing_asset_definition_with_quantity_value_type}" asset definition'): - iroha.should(have.asset(f'{GIVEN_existing_asset_definition_with_quantity_value_type.name}##{GIVEN_new_one_existing_account}')) + f'THEN "{GIVEN_registered_account}" account ' + f'should have the "{GIVEN_registered_asset_definition_with_quantity_value_type}" asset definition' + ): + iroha.should( + have.asset( + f"{GIVEN_registered_asset_definition_with_quantity_value_type.name}##" + f"{GIVEN_registered_account}" + ) + ) + iroha.should( + have.asset_has_quantity( + f"{GIVEN_registered_asset_definition_with_quantity_value_type.name}##" + f"{GIVEN_registered_account}", + GIVEN_quantity_value, + ) + ) + + +@allure.label("sdk_test_id", "mint_asset_quantity_after_minting") +def test_mint_asset_quantity_after_minting(GIVEN_minted_asset_quantity): + with allure.step( + f'WHEN client_cli mint additional asset "{GIVEN_minted_asset_quantity.definition}" ' + f'for the "{GIVEN_minted_asset_quantity.account}" ' + f'with "{GIVEN_minted_asset_quantity.value}" quantity' + ): + client_cli.mint().asset( + account=GIVEN_minted_asset_quantity.account, + asset_definition=GIVEN_minted_asset_quantity.definition, + value_of_value_type="1", + ) + expected_quantity = int(GIVEN_minted_asset_quantity.value) + 1 + with allure.step( + f'THEN "{GIVEN_minted_asset_quantity.account}" account ' + f'should have the "{expected_quantity}" asset definition ' + f"with updated quantity" + ): + iroha.should( + have.asset_has_quantity( + f"{GIVEN_minted_asset_quantity.definition.name}##{GIVEN_minted_asset_quantity.account}", + str(expected_quantity), + ) + ) + + +@allure.label("sdk_test_id", "mint_big_quantity_asset") +@pytest.mark.xfail(reason="https://github.com/hyperledger/iroha/issues/4035") +def test_mint_big_quantity_asset( + GIVEN_registered_asset_definition_with_big_quantity_value_type, +): + assert 0 @allure.label("sdk_test_id", "mint_fixed_asset") -@pytest.mark.xfail(reason="TO DO") -def test_mint_fixed_asset( - GIVEN_new_one_existing_account): +@pytest.mark.xfail(reason="https://github.com/hyperledger/iroha/issues/4035") +def test_mint_fixed_asset(GIVEN_registered_account): + assert 0 + + +@allure.label("sdk_test_id", "mint_store_asset") +@pytest.mark.xfail(reason="https://github.com/hyperledger/iroha/issues/4035") +def test_mint_store_asset(GIVEN_registered_account): assert 0 diff --git a/client_cli/pytests/test/assets/test_register_asset_definitions.py b/client_cli/pytests/test/assets/test_register_asset_definitions.py index fcd5510750f..6452b59fc9b 100644 --- a/client_cli/pytests/test/assets/test_register_asset_definitions.py +++ b/client_cli/pytests/test/assets/test_register_asset_definitions.py @@ -1,117 +1,179 @@ -import allure +import allure # type: ignore import pytest from common.consts import Stderr from src.client_cli import client_cli, have, iroha + @pytest.fixture(scope="function", autouse=True) def story_account_registers_asset_definitions(): - allure.dynamic.story('Account registers an asset definition') - allure.dynamic.label('permission', 'no_permission_required') + allure.dynamic.story("Account registers an asset definition") + allure.dynamic.label("permission", "no_permission_required") -@allure.label('sdk_test_id', 'register_asset_definition_with_quantity_value_type') +@allure.label("sdk_test_id", "register_asset_definition_with_quantity_value_type") def test_register_asset_definition_with_quantity_value_type( - GIVEN_fake_asset_name, - GIVEN_new_one_existing_domain, - GIVEN_quantity_value_type): + GIVEN_fake_asset_name, GIVEN_registered_domain, GIVEN_quantity_value_type +): with allure.step( - f'WHEN client_cli registers the asset_definition "{GIVEN_fake_asset_name}" ' - f'with "{GIVEN_quantity_value_type}" value type' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): + f'WHEN client_cli registers the asset_definition "{GIVEN_fake_asset_name}" ' + f'with "{GIVEN_quantity_value_type}" value type' + f'in the "{GIVEN_registered_domain.name}" domain' + ): client_cli.register().asset().definition( asset=GIVEN_fake_asset_name, - domain=GIVEN_new_one_existing_domain.name, - value_type=GIVEN_quantity_value_type) + domain=GIVEN_registered_domain.name, + value_type=GIVEN_quantity_value_type, + ) + with allure.step(f'THEN Iroha should have the asset "{GIVEN_fake_asset_name}"'): + iroha.should( + have.asset_definition( + GIVEN_fake_asset_name + "#" + GIVEN_registered_domain.name + ) + ) + + +@allure.label("sdk_test_id", "register_asset_definition_with_too_long_name") +def test_register_asset_definition_with_too_long_name( + GIVEN_129_lenght_name, GIVEN_registered_domain, GIVEN_quantity_value_type +): with allure.step( - f'THEN Iroha should have the asset "{GIVEN_fake_asset_name}"'): - iroha.should(have.asset_definition(GIVEN_fake_asset_name + '#' + GIVEN_new_one_existing_domain.name)) + f'WHEN client_cli registers the asset_definition "{GIVEN_129_lenght_name}" ' + f'with "{GIVEN_quantity_value_type}" value type' + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().asset().definition( + asset=GIVEN_129_lenght_name, + domain=GIVEN_registered_domain.name, + value_type=GIVEN_quantity_value_type, + ) + with allure.step(f'THEN Iroha should have the asset "{GIVEN_129_lenght_name}"'): + client_cli.should(have.error(Stderr.TOO_LONG.value)) + -@allure.label('sdk_test_id', 'register_asset_definition_with_store_value_type') +@allure.label("sdk_test_id", "register_asset_definition_with_store_value_type") def test_register_asset_definition_with_store_value_type( - GIVEN_fake_asset_name, - GIVEN_new_one_existing_domain, - GIVEN_store_value_type): + GIVEN_fake_asset_name, GIVEN_registered_domain, GIVEN_store_value_type +): with allure.step( - f'WHEN client_cli registers the asset_definition "{GIVEN_fake_asset_name}" ' - f'with "{GIVEN_store_value_type}" value type' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): + f'WHEN client_cli registers the asset_definition "{GIVEN_fake_asset_name}" ' + f'with "{GIVEN_store_value_type}" value type' + f'in the "{GIVEN_registered_domain.name}" domain' + ): client_cli.register().asset().definition( asset=GIVEN_fake_asset_name, - domain=GIVEN_new_one_existing_domain.name, - value_type=GIVEN_store_value_type) + domain=GIVEN_registered_domain.name, + value_type=GIVEN_store_value_type, + ) + with allure.step(f'THEN Iroha should have the asset "{GIVEN_fake_asset_name}"'): + iroha.should( + have.asset_definition( + GIVEN_fake_asset_name + "#" + GIVEN_registered_domain.name + ) + ) + + +@allure.label("sdk_test_id", "register_asset_definition_with_big_quantity_type") +def test_register_asset_definition_with_big_quantity_type( + GIVEN_fake_asset_name, GIVEN_registered_domain, GIVEN_big_quantity_value_type +): with allure.step( - f'THEN Iroha should have the asset "{GIVEN_fake_asset_name}"'): - iroha.should(have.asset_definition(GIVEN_fake_asset_name + '#' + GIVEN_new_one_existing_domain.name)) + f'WHEN client_cli registers the asset_definition "{GIVEN_fake_asset_name}" ' + f'with "{GIVEN_big_quantity_value_type}" value type' + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().asset().definition( + asset=GIVEN_fake_asset_name, + domain=GIVEN_registered_domain.name, + value_type=GIVEN_big_quantity_value_type, + ) + with allure.step(f'THEN Iroha should have the asset "{GIVEN_fake_asset_name}"'): + iroha.should( + have.asset_definition( + GIVEN_fake_asset_name + "#" + GIVEN_registered_domain.name + ) + ) + @allure.label("sdk_test_id", "register_asset_definition_with_metadata") @pytest.mark.xfail(reason="TO DO") def test_register_asset_definition_with_metadata( - GIVEN_fake_asset_name, - GIVEN_new_one_existing_domain): + GIVEN_fake_asset_name, GIVEN_registered_domain +): assert 0 @allure.label("sdk_test_id", "register_fixed_asset_definition") @pytest.mark.xfail(reason="TO DO") def test_register_fixed_asset_definition( - GIVEN_fake_asset_name, - GIVEN_new_one_existing_domain): + GIVEN_fake_asset_name, GIVEN_registered_domain +): assert 0 -@allure.label('sdk_test_id', 'register_asset_with_existing_name') +@allure.label("sdk_test_id", "register_asset_with_existing_name") def test_register_asset_with_existing_name( - GIVEN_existing_asset_definition_with_quantity_value_type): + GIVEN_registered_asset_definition_with_quantity_value_type, +): with allure.step( - f'WHEN client_cli tries to register an asset definition with the same name "{GIVEN_existing_asset_definition_with_quantity_value_type.name}"' - f'in the "{GIVEN_existing_asset_definition_with_quantity_value_type.domain}" domain'): - client_cli.register().asset().definition(asset=GIVEN_existing_asset_definition_with_quantity_value_type.name, - domain=GIVEN_existing_asset_definition_with_quantity_value_type.domain, - value_type=GIVEN_existing_asset_definition_with_quantity_value_type.value_type) + f"WHEN account tries to register an asset definition " + f'with the same name "{GIVEN_registered_asset_definition_with_quantity_value_type.name}"' + f'in the "{GIVEN_registered_asset_definition_with_quantity_value_type.domain}" domain' + ): + client_cli.register().asset().definition( + asset=GIVEN_registered_asset_definition_with_quantity_value_type.name, + domain=GIVEN_registered_asset_definition_with_quantity_value_type.domain, + value_type=GIVEN_registered_asset_definition_with_quantity_value_type.value_type, + ) with allure.step( - f'THEN client_cli should have the asset definition error: "{GIVEN_existing_asset_definition_with_quantity_value_type.__repr__()}"'): + f'THEN client_cli should have the asset definition error: "' + f'{GIVEN_registered_asset_definition_with_quantity_value_type.__repr__()}"' + ): client_cli.should(have.error(Stderr.REPETITION.value)) - -@allure.label('sdk_test_id', 'register_asset_with_empty_name') -def test_register_asset_with_empty_name( - GIVEN_new_one_existing_domain): +@allure.label("sdk_test_id", "register_asset_with_empty_name") +def test_register_asset_with_empty_name(GIVEN_registered_domain): with allure.step( - f'WHEN client_cli tries to register an asset definition with an empty name' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): - client_cli.register().asset().definition(asset='', domain=GIVEN_new_one_existing_domain.name, value_type='Quantity') + "WHEN client_cli tries to register an asset definition with an empty name" + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().asset().definition( + asset="", domain=GIVEN_registered_domain.name, value_type="Quantity" + ) with allure.step( - f'THEN сlient_cli should have the asset error: "{Stderr.CANNOT_BE_EMPTY}"'): + f'THEN сlient_cli should have the asset error: "{Stderr.CANNOT_BE_EMPTY}"' + ): client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) - -@allure.label('sdk_test_id', 'register_asset_with_not_existing_domain') +@allure.label("sdk_test_id", "register_asset_with_not_existing_domain") def test_register_asset_with_not_existing_domain( - GIVEN_not_existing_name, - GIVEN_quantity_value_type, - GIVEN_fake_asset_name): - with allure.step( - f'WHEN client_cli tries to register an asset definition with not existing domain'): - client_cli.register().asset().definition(asset=GIVEN_fake_asset_name, domain=GIVEN_not_existing_name, - value_type=GIVEN_quantity_value_type) + GIVEN_not_existing_name, GIVEN_quantity_value_type, GIVEN_fake_asset_name +): with allure.step( - f'THEN client_cli should have the error'): + "WHEN client_cli tries to register an asset definition with not existing domain" + ): + client_cli.register().asset().definition( + asset=GIVEN_fake_asset_name, + domain=GIVEN_not_existing_name, + value_type=GIVEN_quantity_value_type, + ) + with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.FAILED_TO_FIND_DOMAIN.value)) - -@allure.label('sdk_test_id', 'register_asset_with_too_long_value_type') +@allure.label("sdk_test_id", "register_asset_with_too_long_value_type") def test_register_asset_with_too_long_value_type( - GIVEN_fake_asset_name, - GIVEN_new_one_existing_domain): - with allure.step( - 'WHEN client_cli tries to register an asset definition with too long value type'): - client_cli.register().asset().definition(asset=GIVEN_fake_asset_name, - domain=GIVEN_new_one_existing_domain.name, - value_type='coin') + GIVEN_fake_asset_name, GIVEN_registered_domain +): with allure.step( - 'THEN client_cli should have the error'): + "WHEN client_cli tries to register an asset definition with too long value type" + ): + client_cli.register().asset().definition( + asset=GIVEN_fake_asset_name, + domain=GIVEN_registered_domain.name, + value_type="coin", + ) + with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.INVALID_VALUE_TYPE.value)) diff --git a/client_cli/pytests/test/assets/test_transfer_assets.py b/client_cli/pytests/test/assets/test_transfer_assets.py index 31601b5bbff..f78a2da2daa 100644 --- a/client_cli/pytests/test/assets/test_transfer_assets.py +++ b/client_cli/pytests/test/assets/test_transfer_assets.py @@ -1,39 +1,109 @@ -import allure +import allure # type: ignore import pytest +from common.consts import Stderr from src.client_cli import client_cli, have, iroha @pytest.fixture(scope="function", autouse=True) def story_account_transfer_asset(): - allure.dynamic.story('Account transfers assets') + allure.dynamic.story("Account transfers assets") -@allure.label('sdk_test_id', 'transfer_asset') -@allure.label('permission', 'no_permission_required') + +@allure.label("sdk_test_id", "transfer_asset") +@allure.label("permission", "no_permission_required") def test_transfer_asset( - GIVEN_new_one_existing_account, - GIVEN_currently_authorized_account, - GIVEN_currently_account_quantity_with_two_quantity_of_asset): - with allure.step(f'WHEN {GIVEN_currently_authorized_account.name} transfers 1 Quantity' - f'of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}' - f'to {GIVEN_new_one_existing_account.name}'): + GIVEN_registered_account, + GIVEN_currently_authorized_account, + GIVEN_currently_account_quantity_with_two_quantity_of_asset, +): + with allure.step( + f"WHEN {GIVEN_currently_authorized_account.name} transfers 1 Quantity" + f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + f"to {GIVEN_registered_account.name}" + ): + client_cli.transfer( + asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, + source_account=GIVEN_currently_authorized_account, + target_account=GIVEN_registered_account, + quantity="1", + ) + + with allure.step( + f"THEN {GIVEN_currently_authorized_account.name} has 1 Quantity " + f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + f"AND {GIVEN_registered_account} has 1 more Quantity" + ): + iroha.should( + have.asset( + f"{GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}#" + f"{GIVEN_currently_authorized_account.domain}#" + f"{GIVEN_registered_account}" + ) + ) + + +@allure.label("sdk_test_id", "transfer_with_insufficient_funds") +@allure.label("permission", "no_permission_required") +def test_transfer_with_insufficient_funds( + GIVEN_registered_account, + GIVEN_currently_authorized_account, + GIVEN_currently_account_quantity_with_two_quantity_of_asset, +): + with allure.step( + f"WHEN {GIVEN_currently_authorized_account.name} attempts to transfer more than available Quantity" + f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + f"to {GIVEN_registered_account.name}" + ): client_cli.transfer( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, source_account=GIVEN_currently_authorized_account, - target_account=GIVEN_new_one_existing_account, - quantity="1") + target_account=GIVEN_registered_account, + quantity=str( + int(GIVEN_currently_account_quantity_with_two_quantity_of_asset.value) + + 1 + ), + ) + with allure.step( + f"THEN {GIVEN_currently_authorized_account.name} still has the original Quantity " + f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + f"AND {GIVEN_registered_account.name} does not receive any additional Quantity" + ): + client_cli.should(have.error(Stderr.INSUFFICIENT_FUNDS.value)) + - with allure.step(f'THEN {GIVEN_currently_authorized_account.name} has 1 Quantity ' - f'of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}' - f'AND {GIVEN_new_one_existing_account} has 1 more Quantity'): - iroha.should(have.asset( - f'{GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}#{GIVEN_currently_authorized_account.domain}#{GIVEN_new_one_existing_account}')) +@allure.label("sdk_test_id", "exchange_asset_intermediary") +@allure.label("permission", "intermediary_permission_required") +@pytest.mark.xfail(reason="TO DO") +def test_exchange_asset_through_intermediary( + GIVEN_registered_account, + GIVEN_intermediary_with_transfer_permission, + GIVEN_seller_account_with_btc, + GIVEN_buyer_account_with_eth, +): + # with allure.step(f'WHEN {GIVEN_intermediary_with_transfer_permission.name}' + # f'exchanges BTC from {GIVEN_seller_account_with_btc.name}' + # f'with ETH from {GIVEN_buyer_account_with_eth.name}'): + # client_cli.exchange_assets( + # intermediary_account=GIVEN_intermediary_with_transfer_permission, + # seller_account=GIVEN_seller_account_with_btc, + # buyer_account=GIVEN_buyer_account_with_eth, + # btc_quantity="1", + # eth_quantity="10") + # + # with allure.step(f'THEN {GIVEN_seller_account_with_btc.name} receives ETH ' + # f'AND {GIVEN_buyer_account_with_eth.name} receives BTC'): + # iroha.should(have.asset( + # f'eth#{GIVEN_seller_account_with_btc.domain}', quantity="10")) + # iroha.should(have.asset( + # f'btc#{GIVEN_buyer_account_with_eth.domain}', quantity="1")) + assert 0 -@allure.label('sdk_test_id', 'transfer_user_asset') -@allure.label('permission', 'can_transfer_user_asset') +@allure.label("sdk_test_id", "transfer_user_asset") +@allure.label("permission", "can_transfer_user_asset") @pytest.mark.xfail(reason="TO DO") def test_transfer_user_asset( - GIVEN_new_one_existing_account, - GIVEN_currently_authorized_account): + GIVEN_registered_account, GIVEN_currently_authorized_account +): assert 0 diff --git a/client_cli/pytests/test/assets/test_unregister_asset.py b/client_cli/pytests/test/assets/test_unregister_asset.py new file mode 100644 index 00000000000..20b3b6006dc --- /dev/null +++ b/client_cli/pytests/test/assets/test_unregister_asset.py @@ -0,0 +1,38 @@ +import allure # type: ignore +import pytest + +from src.client_cli import client_cli, have, iroha + + +@pytest.fixture(scope="function", autouse=True) +def story_account_unregisters_asset(): + allure.dynamic.story("Account unregisters an asset") + allure.dynamic.label("permission", "no_permission_required") + + +@allure.label("sdk_test_id", "unregister_asset") +@pytest.mark.parametrize( + "GIVEN_quantity_asset_for_account", ["alice@wonderland"], indirect=True +) +@pytest.mark.xfail(reason="wait for #4039") +def test_unregister_asset( + GIVEN_quantity_asset_for_account, +): + with allure.step( + f'WHEN client_cli unregisters the asset "{GIVEN_quantity_asset_for_account.definition.name}"' + ): + client_cli.unregister_asset( + asset_id=f"{GIVEN_quantity_asset_for_account.definition.name}#" + f"{GIVEN_quantity_asset_for_account.account}@" + f"{GIVEN_quantity_asset_for_account.definition.domain}" + ) + with allure.step( + f'THEN Iroha should not have the asset "{GIVEN_quantity_asset_for_account.definition.name}"' + ): + iroha.should( + have.asset( + f"{GIVEN_quantity_asset_for_account.definition.name}##" + f"{GIVEN_quantity_asset_for_account.account}@" + f"{GIVEN_quantity_asset_for_account.definition.domain}" + ) + ) diff --git a/client_cli/pytests/test/atomicity/conftest.py b/client_cli/pytests/test/atomicity/conftest.py index 5d06e5ef90e..dfd54995483 100644 --- a/client_cli/pytests/test/atomicity/conftest.py +++ b/client_cli/pytests/test/atomicity/conftest.py @@ -1,10 +1,10 @@ +from test import before_all, before_each + +import allure # type: ignore import pytest -import allure -from test import ( - before_each) @pytest.fixture(scope="function", autouse=True) def atomicity_test_setup(): - allure.dynamic.feature('Atomicity') - allure.dynamic.label('permission', 'no_permission_required') + allure.dynamic.feature("Atomicity") + allure.dynamic.label("permission", "no_permission_required") diff --git a/client_cli/pytests/test/atomicity/test_multiple_instructions_within_transaction.py b/client_cli/pytests/test/atomicity/test_multiple_instructions_within_transaction.py index c05a70430ab..28afe162a1b 100644 --- a/client_cli/pytests/test/atomicity/test_multiple_instructions_within_transaction.py +++ b/client_cli/pytests/test/atomicity/test_multiple_instructions_within_transaction.py @@ -1,12 +1,13 @@ -import allure +import allure # type: ignore import pytest + @pytest.fixture(scope="function", autouse=True) def story_client_sends_multiple_instructions_within_transaction(): - allure.dynamic.story('Client sends a multiple instructions within transaction') + allure.dynamic.story("Client sends a multiple instructions within transaction") + -@allure.label('sdk_test_id', 'multiple_instructions_within_transaction') +@allure.label("sdk_test_id", "multiple_instructions_within_transaction") @pytest.mark.xfail(reason="TO DO") -def test_multiple_instructions_within_transaction( - GIVEN_currently_authorized_account): +def test_multiple_instructions_within_transaction(GIVEN_currently_authorized_account): assert 0 diff --git a/client_cli/pytests/test/atomicity/test_pair_instructions_within_transaction.py b/client_cli/pytests/test/atomicity/test_pair_instructions_within_transaction.py index 679c7b0cfe1..5861205c651 100644 --- a/client_cli/pytests/test/atomicity/test_pair_instructions_within_transaction.py +++ b/client_cli/pytests/test/atomicity/test_pair_instructions_within_transaction.py @@ -1,13 +1,13 @@ -import allure +import allure # type: ignore import pytest + @pytest.fixture(scope="function", autouse=True) def story_client_sends_pair_instructions_within_transaction(): - allure.dynamic.story('Client sends a pair instructions within transaction') + allure.dynamic.story("Client sends a pair instructions within transaction") -@allure.label('sdk_test_id', 'pair_instruction') +@allure.label("sdk_test_id", "pair_instruction") @pytest.mark.xfail(reason="TO DO") -def test_pair_instruction( - GIVEN_currently_authorized_account): +def test_pair_instruction(GIVEN_currently_authorized_account): assert 0 diff --git a/client_cli/pytests/test/atomicity/test_wrong_instructions_within_transaction.py b/client_cli/pytests/test/atomicity/test_wrong_instructions_within_transaction.py index 66422357536..7a24a5d1821 100644 --- a/client_cli/pytests/test/atomicity/test_wrong_instructions_within_transaction.py +++ b/client_cli/pytests/test/atomicity/test_wrong_instructions_within_transaction.py @@ -1,13 +1,13 @@ -import allure +import allure # type: ignore import pytest + @pytest.fixture(scope="function", autouse=True) def story_send_pair_instructions_within_transaction(): - allure.dynamic.story('Client sends a wrong instruction in transaction') + allure.dynamic.story("Client sends a wrong instruction in transaction") -@allure.label('sdk_test_id', 'instruction_failed') +@allure.label("sdk_test_id", "instruction_failed") @pytest.mark.xfail(reason="TO DO") -def test_instruction_failed( - GIVEN_currently_authorized_account): +def test_instruction_failed(GIVEN_currently_authorized_account): assert 0 diff --git a/client_cli/pytests/test/conftest.py b/client_cli/pytests/test/conftest.py index ec2bac20bb6..99b5a99e060 100644 --- a/client_cli/pytests/test/conftest.py +++ b/client_cli/pytests/test/conftest.py @@ -1,124 +1,248 @@ # pylint: disable=redefined-outer-name +# pylint: disable=invalid-name """ This module contains pytest fixtures for testing. """ -import allure +import allure # type: ignore import pytest from common.consts import ValueTypes -from common.helpers import * -from models import Account, AssetDefinition, Domain, Asset +from common.helpers import ( + fake_asset_name, + fake_name, + generate_public_key, + generate_random_string_with_reserved_char, + generate_random_string_with_whitespace, + generate_random_string_without_reserved_chars, + key_with_invalid_character_in_key, + name_with_uppercase_letter, + not_existing_name, + random, + string, +) +from common.settings import PEERS_CONFIGS_PATH +from models import Account, Asset, AssetDefinition, Domain from src.client_cli import client_cli, config + # General fixtures -@pytest.fixture(scope='function', autouse=True) +@pytest.fixture(scope="session", autouse=True) +def before_all(): + """Initial setup for all test sessions. + This fixture generates configurations based on peers and is automatically + used for every test session.""" + config.generate_by_peers(PEERS_CONFIGS_PATH) + yield + + +@pytest.fixture(scope="function", autouse=True) def before_each(): """Fixture to set up and reset the client_cli state.""" - allure.dynamic.label('sdk', 'Client CLI') + allure.dynamic.label("sdk", "Client CLI") allure.dynamic.label("owner", "astrokov") yield client_cli.reset() + # Fixtures for creating objects (domains, accounts, asset definitions, assets) @pytest.fixture() -def GIVEN_new_one_existing_domain(): - """Fixture to create and register an existing domain.""" +def GIVEN_registered_domain(): + """Fixture to create and register a domain.""" domain = Domain(fake_name()) - with allure.step(f'GIVEN an existing domain {domain.name}'): + with allure.step(f"GIVEN a registered domain {domain.name}"): client_cli.register().domain(domain.name) return domain + @pytest.fixture() -def GIVEN_existing_domain_with_uppercase_letter( - GIVEN_new_one_existing_domain): - """Fixture to create and register an existing domain, but with uppercase letter.""" - domain = GIVEN_new_one_existing_domain +def GIVEN_registered_domain_with_uppercase_letter(GIVEN_registered_domain): + """Fixture to create and register a domain, but with uppercase letter.""" + domain = GIVEN_registered_domain domain.name = name_with_uppercase_letter(domain.name) - with allure.step(f'GIVEN an existing domain {domain.name}'): + with allure.step(f"GIVEN a registered domain {domain.name}"): client_cli.register().domain(domain.name) return domain + @pytest.fixture() -def GIVEN_new_one_existing_account(GIVEN_new_one_existing_domain, GIVEN_public_key): - """Fixture to create and register an existing account.""" +def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): + """Fixture to create an account.""" name = fake_name() account = Account( - name=name, - domain=GIVEN_new_one_existing_domain.name, - public_key=GIVEN_public_key) - with allure.step(f'GIVEN the account "{name}" in the "{GIVEN_new_one_existing_domain.name}" domain'): + name=name, domain=GIVEN_registered_domain.name, public_key=GIVEN_public_key + ) + with allure.step( + f'GIVEN the account "{name}" in the "{GIVEN_registered_domain.name}" domain' + ): client_cli.register().account( - account=account.name, - domain=account.domain, - key=account.public_key) + account=account.name, domain=account.domain, key=account.public_key + ) return account + @pytest.fixture() def GIVEN_currently_authorized_account(): """Fixture to get the currently authorized account.""" account: Account = Account( name=config.account_name, domain=config.account_domain, - public_key=config.public_key) - with allure.step(f'GIVEN the currently authorized account "{account.name}" in the "{account.domain}" domain'): + public_key=config.public_key, + ) + with allure.step( + f'GIVEN the currently authorized account "{account.name}" ' + f'in the "{account.domain}" domain' + ): return account + @pytest.fixture() def GIVEN_currently_account_quantity_with_two_quantity_of_asset( - GIVEN_currently_authorized_account, - GIVEN_quantity_value_type, - GIVEN_fake_asset_name): + GIVEN_currently_authorized_account, GIVEN_quantity_value_type, GIVEN_fake_asset_name +): """Fixture to get the currently authorized account asset""" - asset_def = AssetDefinition(name=GIVEN_fake_asset_name, - domain=GIVEN_currently_authorized_account.domain, - value_type=GIVEN_quantity_value_type) - asset = Asset(definition=asset_def, value='2') + asset_def = AssetDefinition( + name=GIVEN_fake_asset_name, + domain=GIVEN_currently_authorized_account.domain, + value_type=GIVEN_quantity_value_type, + ) + asset = Asset( + definition=asset_def, value="2", account=GIVEN_currently_authorized_account.name + ) name = fake_name() - with allure.step(f'GIVEN the asset_definition "{name}" ' - f'in the "{GIVEN_currently_authorized_account.domain}" domain'): + with allure.step( + f'GIVEN the asset_definition "{name}" ' + f'in the "{GIVEN_currently_authorized_account.domain}" domain' + ): client_cli.register().asset().definition( asset=asset.definition.name, domain=asset.definition.domain, - value_type=asset.definition.value_type) + value_type=asset.definition.value_type, + ) client_cli.mint().asset( - account=GIVEN_currently_authorized_account, - asset_definition=asset.definition, - value_of_value_type=asset.value + account=GIVEN_currently_authorized_account, + asset_definition=asset.definition, + value_of_value_type=asset.value, ) return asset +@pytest.fixture() +def GIVEN_quantity_asset_for_account( + request, GIVEN_quantity_value_type, GIVEN_fake_asset_name, GIVEN_quantity_value +): + """Fixture to get an asset for a given account and domain with specified quantity.""" + account, domain = request.param.split("@") + account = Account(name=account, domain=domain) + + asset_def = AssetDefinition( + name=GIVEN_fake_asset_name, domain=domain, value_type=GIVEN_quantity_value_type + ) + asset = Asset( + definition=asset_def, value=GIVEN_quantity_value, account=account.name + ) + + with allure.step( + f'GIVEN the asset_definition "{asset_def.name}" ' f'in the "{domain}" domain' + ): + client_cli.register().asset().definition( + asset=asset.definition.name, + domain=asset.definition.domain, + value_type=asset.definition.value_type, + ) + client_cli.mint().asset( + account=account, + asset_definition=asset.definition, + value_of_value_type=asset.value, + ) + + return asset + + +@pytest.fixture() +def GIVEN_registered_asset_definition_with_quantity_value_type( + GIVEN_registered_domain, GIVEN_quantity_value_type, GIVEN_fake_asset_name +): + """Fixture to create and register an asset definition with quantity value type.""" + asset_def = AssetDefinition( + name=GIVEN_fake_asset_name, + domain=GIVEN_registered_domain.name, + value_type=GIVEN_quantity_value_type, + ) + with allure.step( + f'GIVEN the asset_definition "{GIVEN_fake_asset_name}" ' + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().asset().definition( + asset=asset_def.name, + domain=asset_def.domain, + value_type=asset_def.value_type, + ) + return asset_def + + +@pytest.fixture() +def GIVEN_minted_asset_quantity( + GIVEN_registered_asset_definition_with_quantity_value_type, + GIVEN_registered_account, + GIVEN_quantity_value, +): + """Fixture to create and return an asset with a specified quantity. + It takes a registered asset definition, a registered account, and a quantity value. + """ + asset = Asset( + account=GIVEN_registered_account, + definition=GIVEN_registered_asset_definition_with_quantity_value_type, + value=GIVEN_quantity_value, + ) + client_cli.mint().asset( + account=asset.account, + asset_definition=asset.definition, + value_of_value_type=asset.value, + ) + return asset + @pytest.fixture() -def GIVEN_existing_asset_definition_with_quantity_value_type( - GIVEN_new_one_existing_domain, - GIVEN_quantity_value_type, - GIVEN_fake_asset_name): - """Fixture to create and register an existing asset definition with random value type.""" - asset_def = AssetDefinition(name=GIVEN_fake_asset_name, - domain=GIVEN_new_one_existing_domain.name, - value_type=GIVEN_quantity_value_type) - with allure.step(f'GIVEN the asset_definition "{GIVEN_fake_asset_name}" ' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): - client_cli.register().asset().definition(asset=asset_def.name, - domain=asset_def.domain, - value_type=asset_def.value_type) +def GIVEN_registered_asset_definition_with_big_quantity_value_type( + GIVEN_registered_domain, GIVEN_big_quantity_value_type, GIVEN_fake_asset_name +): + """Fixture to create and register an asset definition with big quantity value type.""" + asset_def = AssetDefinition( + name=GIVEN_fake_asset_name, + domain=GIVEN_registered_domain.name, + value_type=GIVEN_big_quantity_value_type, + ) + with allure.step( + f'GIVEN the asset_definition "{GIVEN_fake_asset_name}" ' + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().asset().definition( + asset=asset_def.name, + domain=asset_def.domain, + value_type=asset_def.value_type, + ) return asset_def + @pytest.fixture() -def GIVEN_existing_asset_definition_with_store_value_type( - GIVEN_new_one_existing_domain, - GIVEN_store_value_type, - GIVEN_fake_asset_name): - """Fixture to create and register an existing asset definition with store value type.""" - asset_def = AssetDefinition(name=GIVEN_fake_asset_name, - domain=GIVEN_new_one_existing_domain.name, - value_type=GIVEN_store_value_type) - with allure.step(f'GIVEN the asset_definition "{GIVEN_fake_asset_name}" ' - f'in the "{GIVEN_new_one_existing_domain.name}" domain'): - client_cli.register().asset().definition(asset=asset_def.name, - domain=asset_def.domain, - value_type=asset_def.value_type) +def GIVEN_registered_asset_definition_with_store_value_type( + GIVEN_registered_domain, GIVEN_store_value_type, GIVEN_fake_asset_name +): + """Fixture to create and register an asset definition with store value type.""" + asset_def = AssetDefinition( + name=GIVEN_fake_asset_name, + domain=GIVEN_registered_domain.name, + value_type=GIVEN_store_value_type, + ) + with allure.step( + f'GIVEN the asset_definition "{GIVEN_fake_asset_name}" ' + f'in the "{GIVEN_registered_domain.name}" domain' + ): + client_cli.register().asset().definition( + asset=asset_def.name, + domain=asset_def.domain, + value_type=asset_def.value_type, + ) return asset_def @@ -130,6 +254,7 @@ def GIVEN_fake_name(): with allure.step(f'GIVEN a "{name}" name'): return name + @pytest.fixture() def GIVEN_fake_asset_name(): """Fixture to provide a fake asset name.""" @@ -137,20 +262,23 @@ def GIVEN_fake_asset_name(): with allure.step(f'GIVEN a "{asset_name}" asset'): return asset_name + @pytest.fixture() def GIVEN_not_existing_name(): """Fixture to provide a non-existing name.""" name = not_existing_name() - with allure.step(f'GIVEN an non-existing {name}'): + with allure.step(f"GIVEN an non-existing {name}"): return name + @pytest.fixture() def GIVEN_public_key(): """Fixture to provide a public key.""" public_key = generate_public_key() - with allure.step(f'GIVEN a public key {public_key}'): + with allure.step(f"GIVEN a public key {public_key}"): return public_key + @pytest.fixture() def GIVEN_random_character(): """Fixture to provide a random character from the ASCII letters.""" @@ -158,23 +286,35 @@ def GIVEN_random_character(): with allure.step(f'GIVEN a "{letter}" name'): return letter + @pytest.fixture() def GIVEN_random_invalid_base64_character(): - """Fixture to provide a random invalid base64 character (not a-z,A-Z,0-9,+,/,=).""" - letter = random.choice([ch for ch in string.printable if not (ch.isalpha() or ch.isdigit() or ch == "=" or ch == "+" or ch == "/")]) + """Fixture to provide a random invalid base64 character + (not a-z,A-Z,0-9,+,/,=). + """ + invalid_chars = [ + ch + for ch in string.printable + if not (ch.isalpha() or ch.isdigit() or ch in ["=", "+", "/"]) + ] + letter = random.choice(invalid_chars) with allure.step(f'GIVEN a "{letter}" name'): return letter + # Fixtures for providing specific values or conditions (e.g., name length, string with spaces) @pytest.fixture() def GIVEN_key_with_invalid_character_in_key( - GIVEN_public_key, - GIVEN_random_invalid_base64_character): + GIVEN_public_key, GIVEN_random_invalid_base64_character +): """Fixture to provide a public key with an invalid character.""" - invalid_key = key_with_invalid_character_in_key(GIVEN_public_key, GIVEN_random_invalid_base64_character) + invalid_key = key_with_invalid_character_in_key( + GIVEN_public_key, GIVEN_random_invalid_base64_character + ) with allure.step(f'GIVEN an invalid key "{invalid_key}"'): return invalid_key + @pytest.fixture() def GIVEN_quantity_value_type(): """Fixture to provide a quantity value type.""" @@ -182,6 +322,7 @@ def GIVEN_quantity_value_type(): with allure.step(f'GIVEN a "{value_type}" value type'): return value_type + @pytest.fixture() def GIVEN_store_value_type(): """Fixture to provide a store value type.""" @@ -189,30 +330,43 @@ def GIVEN_store_value_type(): with allure.step(f'GIVEN a "{value_type}" value type'): return value_type + +@pytest.fixture() +def GIVEN_big_quantity_value_type(): + """Fixture to provide a big quantity value type.""" + value_type = ValueTypes.BIG_QUANTITY.value + with allure.step(f'GIVEN a "{value_type}" value type'): + return value_type + + @pytest.fixture() def GIVEN_quantity_value(): """Fixture to provide a random quantity value based on the given value type.""" - rand_int = str(random.getrandbits(32)) + rand_int = str((random.getrandbits(32)) - 1) return rand_int + @pytest.fixture() def GIVEN_128_lenght_name(): ident = generate_random_string_without_reserved_chars(128) - with allure.step(f'GIVEN a name with 128 lenght "{ident}"'): + with allure.step(f'GIVEN a name with 128 length "{ident}"'): return ident + @pytest.fixture() def GIVEN_129_lenght_name(): ident = generate_random_string_without_reserved_chars(129) - with allure.step(f'GIVEN a name with 129 lenght "{ident}"'): + with allure.step(f'GIVEN a name with 129 length "{ident}"'): return ident + @pytest.fixture() def GIVEN_127_lenght_name(): ident = generate_random_string_without_reserved_chars(127) - with allure.step(f'GIVEN a name with 127 lenght "{ident}"'): + with allure.step(f'GIVEN a name with 127 length "{ident}"'): return ident + @pytest.fixture() def GIVEN_string_with_reserved_character(): """Fixture to provide a random string with reserved characters.""" @@ -220,10 +374,10 @@ def GIVEN_string_with_reserved_character(): with allure.step(f'GIVEN a "{new_string}" string'): return new_string + @pytest.fixture() def GIVEN_string_with_whitespaces(): """Fixture to provide a random string with whitespaces.""" new_string = generate_random_string_with_whitespace() with allure.step(f'GIVEN a "{new_string}" string'): return new_string - diff --git a/client_cli/pytests/test/domains/conftest.py b/client_cli/pytests/test/domains/conftest.py index 470411fe5a5..e49acdcfd94 100644 --- a/client_cli/pytests/test/domains/conftest.py +++ b/client_cli/pytests/test/domains/conftest.py @@ -1,21 +1,24 @@ -import pytest -import allure - from test import ( GIVEN_128_lenght_name, GIVEN_129_lenght_name, - GIVEN_new_one_existing_domain, + GIVEN_currently_authorized_account, GIVEN_fake_name, + GIVEN_public_key, GIVEN_random_character, + GIVEN_registered_account, + GIVEN_registered_domain, + GIVEN_registered_domain_with_uppercase_letter, GIVEN_string_with_reserved_character, GIVEN_string_with_whitespaces, - GIVEN_existing_domain_with_uppercase_letter, - GIVEN_currently_authorized_account, - GIVEN_new_one_existing_account, - GIVEN_public_key, - before_each) + before_all, + before_each, +) + +import allure # type: ignore +import pytest + @pytest.fixture(scope="function", autouse=True) def domain_test_setup(): - allure.dynamic.feature('Domains') - allure.dynamic.label('permission', 'no_permission_required') + allure.dynamic.feature("Domains") + allure.dynamic.label("permission", "no_permission_required") diff --git a/client_cli/pytests/test/domains/test_domains_query_filters.py b/client_cli/pytests/test/domains/test_domains_query_filters.py index 95c7bcb8887..7f0cfed50c8 100644 --- a/client_cli/pytests/test/domains/test_domains_query_filters.py +++ b/client_cli/pytests/test/domains/test_domains_query_filters.py @@ -1,19 +1,25 @@ import json -import allure -from src.client_cli import iroha, client_cli +import allure # type: ignore -def test_filter_by_domain(GIVEN_new_one_existing_domain): +from src.client_cli import client_cli, iroha + + +def test_filter_by_domain(GIVEN_registered_domain): def condition(): - domain_name = GIVEN_new_one_existing_domain.name + domain_name = GIVEN_registered_domain.name with allure.step( - f'WHEN client_cli query domains filtered by name "{domain_name}"'): - domains = iroha.list_filter(f'{{"Identifiable": {{"Is": "{domain_name}"}}}}').domains() + f'WHEN client_cli query domains filtered by name "{domain_name}"' + ): + domains = iroha.list_filter({"Identifiable": {"Is": domain_name}}).domains() with allure.step( - f'THEN Iroha should return only return domains with "{domain_name}" name'): + f'THEN Iroha should return only return domains with "{domain_name}" name' + ): allure.attach( json.dumps(domains), - name='domains', - attachment_type=allure.attachment_type.JSON) + name="domains", + attachment_type=allure.attachment_type.JSON, + ) return domains and all(domain == domain_name for domain in domains) + client_cli.wait_for(condition) diff --git a/client_cli/pytests/test/domains/test_register_domains.py b/client_cli/pytests/test/domains/test_register_domains.py index df921966500..173c054119b 100644 --- a/client_cli/pytests/test/domains/test_register_domains.py +++ b/client_cli/pytests/test/domains/test_register_domains.py @@ -1,4 +1,4 @@ -import allure +import allure # type: ignore import pytest from common.consts import Stderr @@ -7,97 +7,111 @@ @pytest.fixture(scope="function", autouse=True) def story_account_registers_domain(): - allure.dynamic.story('Account registers a domain') - allure.dynamic.label('permission', 'no_permission_required') + allure.dynamic.story("Account registers a domain") + allure.dynamic.label("permission", "no_permission_required") -@allure.label('sdk_test_id', 'register_domain') -def test_register_domain( - GIVEN_fake_name): - with allure.step( - f'WHEN client_cli registers the domain name "{GIVEN_fake_name}"'): - client_cli.execute(f'domain register --id={GIVEN_fake_name}') - with allure.step( - f'THEN Iroha should have the domain name "{GIVEN_fake_name}"'): + +@allure.label("sdk_test_id", "register_domain") +def test_register_domain(GIVEN_fake_name): + with allure.step(f'WHEN client_cli registers the domain name "{GIVEN_fake_name}"'): + client_cli.execute(f"domain register --id={GIVEN_fake_name}") + with allure.step(f'THEN Iroha should have the domain name "{GIVEN_fake_name}"'): iroha.should(have.domain(GIVEN_fake_name)) -@allure.label('sdk_test_id', 'register_empty_domain') + +@allure.label("sdk_test_id", "register_empty_domain") def test_register_empty_domain( - # GIVEN_empty_string + # GIVEN_empty_string ): + with allure.step("WHEN client_cli registers an empty domain"): + client_cli.register().domain("") with allure.step( - 'WHEN client_cli registers an empty domain'): - client_cli.register().domain('') - with allure.step( - f'THEN client_cli should have the domain error: "{Stderr.CANNOT_BE_EMPTY}"'): + f'THEN client_cli should have the domain error: "{Stderr.CANNOT_BE_EMPTY}"' + ): client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) -@allure.label('sdk_test_id', 'register_existing_domain') -def test_register_existing_domain( - GIVEN_new_one_existing_domain): + +@allure.label("sdk_test_id", "register_existing_domain") +def test_register_existing_domain(GIVEN_registered_domain): with allure.step( - f'WHEN client_cli registers an existing domain "{GIVEN_new_one_existing_domain.name}"'): - client_cli.register().domain(GIVEN_new_one_existing_domain.name) + f'WHEN client_cli registers an existing domain "{GIVEN_registered_domain.name}"' + ): + client_cli.register().domain(GIVEN_registered_domain.name) with allure.step( - f'THEN client_cli should have the domain error: "{GIVEN_new_one_existing_domain.name}"'): + f'THEN client_cli should have the domain error: "{GIVEN_registered_domain.name}"' + ): client_cli.should(have.error(Stderr.REPETITION.value)) -@allure.label('sdk_test_id', 'register_existing_domain_with_uppercase_letter') + +@allure.label("sdk_test_id", "register_existing_domain_with_uppercase_letter") def test_register_existing_domain_uppercase_with_uppercase_letter( - GIVEN_existing_domain_with_uppercase_letter): + GIVEN_registered_domain_with_uppercase_letter, +): with allure.step( - f'WHEN client_cli registers an existing domain, ' - f'but with uppercase letter "{GIVEN_existing_domain_with_uppercase_letter.name}"'): - client_cli.register().domain(GIVEN_existing_domain_with_uppercase_letter.name) + f"WHEN client_cli registers an existing domain, " + f'but with uppercase letter "{GIVEN_registered_domain_with_uppercase_letter.name}"' + ): + client_cli.register().domain(GIVEN_registered_domain_with_uppercase_letter.name) with allure.step( - f'THEN Iroha should have the domain name "{GIVEN_existing_domain_with_uppercase_letter.name}"'): - iroha.should(have.domain(GIVEN_existing_domain_with_uppercase_letter.name)) + f'THEN Iroha should have the domain name "{GIVEN_registered_domain_with_uppercase_letter.name}"' + ): + iroha.should(have.domain(GIVEN_registered_domain_with_uppercase_letter.name)) + -@allure.label('sdk_test_id', 'register_one_letter_domain') -def test_register_one_letter_domain( - GIVEN_random_character): +@allure.label("sdk_test_id", "register_one_letter_domain") +def test_register_one_letter_domain(GIVEN_random_character): with allure.step( - f'WHEN client_cli registers the one letter domain "{GIVEN_random_character}"'): + f'WHEN client_cli registers the one letter domain "{GIVEN_random_character}"' + ): client_cli.register().domain(GIVEN_random_character) - with allure.step( - f'THEN Iroha should have the domain "{GIVEN_random_character}"'): + with allure.step(f'THEN Iroha should have the domain "{GIVEN_random_character}"'): iroha.should(have.domain(GIVEN_random_character)) -@allure.label('sdk_test_id', 'register_max_lenght_domain') -def test_register_max_lenght_domain( - GIVEN_128_lenght_name): + +@allure.label("sdk_test_id", "register_max_lenght_domain") +def test_register_max_lenght_domain(GIVEN_128_lenght_name): with allure.step( - f'WHEN client_cli registers the longest domain "{GIVEN_128_lenght_name}"'): + f'WHEN client_cli registers the longest domain "{GIVEN_128_lenght_name}"' + ): client_cli.register().domain(GIVEN_128_lenght_name) with allure.step( - f'THEN Iroha should have the longest domain "{GIVEN_128_lenght_name}"'): + f'THEN Iroha should have the longest domain "{GIVEN_128_lenght_name}"' + ): iroha.should(have.domain(GIVEN_128_lenght_name)) -@allure.label('sdk_test_id', 'register_domain_with_too_long_name') -def test_register_domain_with_too_long_name( - GIVEN_129_lenght_name): + +@allure.label("sdk_test_id", "register_domain_with_too_long_name") +def test_register_domain_with_too_long_name(GIVEN_129_lenght_name): with allure.step( - f'WHEN client_cli registers the domain "{GIVEN_129_lenght_name}" with too long name'): + f'WHEN client_cli registers the domain "{GIVEN_129_lenght_name}" with too long name' + ): client_cli.register().domain(GIVEN_129_lenght_name) with allure.step( - f'THEN client_cli should have the too long domain error: "{Stderr.TOO_LONG}"'): + f'THEN client_cli should have the too long domain error: "{Stderr.TOO_LONG}"' + ): client_cli.should(have.error(Stderr.TOO_LONG.value)) -@allure.label('sdk_test_id', 'register_domain_with_reserved_character') -def test_register_domain_with_reserved_character( - GIVEN_string_with_reserved_character): + +@allure.label("sdk_test_id", "register_domain_with_reserved_character") +def test_register_domain_with_reserved_character(GIVEN_string_with_reserved_character): with allure.step( - f'WHEN client_cli registers a domain "{GIVEN_string_with_reserved_character}" with reserved characters'): + f'WHEN client_cli registers a domain "' + f'{GIVEN_string_with_reserved_character}" with reserved characters' + ): client_cli.register().domain(GIVEN_string_with_reserved_character) with allure.step( - f'THEN client_cli should has the domain error: "{Stderr.RESERVED_CHARACTER.value}"'): + f'THEN client_cli should has the domain error: "{Stderr.RESERVED_CHARACTER.value}"' + ): client_cli.should(have.error(Stderr.RESERVED_CHARACTER.value)) -@allure.label('sdk_test_id', 'register_domain_with_whitespaces') -def test_register_domain_with_whitespaces( - GIVEN_string_with_whitespaces): + +@allure.label("sdk_test_id", "register_domain_with_whitespaces") +def test_register_domain_with_whitespaces(GIVEN_string_with_whitespaces): with allure.step( - f'WHEN client_cli registers a domain "{GIVEN_string_with_whitespaces}" with whitespaces'): + f'WHEN client_cli registers a domain "{GIVEN_string_with_whitespaces}" with whitespaces' + ): client_cli.register().domain(GIVEN_string_with_whitespaces) with allure.step( - f'THEN client_cli should has the domain error: "{Stderr.WHITESPACES.value}"'): + f'THEN client_cli should has the domain error: "{Stderr.WHITESPACES.value}"' + ): client_cli.should(have.error(Stderr.WHITESPACES.value)) diff --git a/client_cli/pytests/test/domains/test_transfer_domains.py b/client_cli/pytests/test/domains/test_transfer_domains.py index ac8cb4d0f3d..430f95e692c 100644 --- a/client_cli/pytests/test/domains/test_transfer_domains.py +++ b/client_cli/pytests/test/domains/test_transfer_domains.py @@ -1,26 +1,34 @@ -import allure +import allure # type: ignore import pytest -from src.client_cli import client_cli, iroha, have +from src.client_cli import client_cli, have, iroha + @pytest.fixture(scope="function", autouse=True) def story_account_transfers_domain(): - allure.dynamic.story('Account transfers a domain') - allure.dynamic.label('permission', 'no_permission_required') + allure.dynamic.story("Account transfers a domain") + allure.dynamic.label("permission", "no_permission_required") + -@allure.label('sdk_test_id', 'transfer_domain_successfully') +@allure.label("sdk_test_id", "transfer_domain_successfully") def test_transfer_domain( - GIVEN_currently_authorized_account, - GIVEN_new_one_existing_account, - GIVEN_new_one_existing_domain, + GIVEN_currently_authorized_account, + GIVEN_registered_account, + GIVEN_registered_domain, ): with allure.step( - f'WHEN {GIVEN_currently_authorized_account} transfers domains ' - f'to {GIVEN_new_one_existing_account}'): - client_cli.execute(f'domain transfer ' - f'--from={GIVEN_currently_authorized_account} ' - f'--to={GIVEN_new_one_existing_account} ' - f'--id={GIVEN_new_one_existing_domain.name}') + f"WHEN {GIVEN_currently_authorized_account} transfers domains " + f"to {GIVEN_registered_account}" + ): + client_cli.execute( + f"domain transfer " + f"--from={GIVEN_currently_authorized_account} " + f"--to={GIVEN_registered_account} " + f"--id={GIVEN_registered_domain.name}" + ) with allure.step( - f'THEN {GIVEN_new_one_existing_account} should own {GIVEN_new_one_existing_domain}'): - iroha.should(have.domain(GIVEN_new_one_existing_domain.name, owned_by=GIVEN_new_one_existing_account)) + f"THEN {GIVEN_registered_account} should own {GIVEN_registered_domain}" + ): + iroha.should( + have.domain(GIVEN_registered_domain.name, owned_by=GIVEN_registered_account) + ) diff --git a/client_cli/pytests/test/roles/conftest.py b/client_cli/pytests/test/roles/conftest.py index 3e68a370cae..ab552d49bbb 100644 --- a/client_cli/pytests/test/roles/conftest.py +++ b/client_cli/pytests/test/roles/conftest.py @@ -1,10 +1,9 @@ +from test import GIVEN_fake_name, before_all, before_each + +import allure # type: ignore import pytest -import allure -from test import ( - GIVEN_fake_name, - before_each) @pytest.fixture(scope="function", autouse=True) -def permission_test_setup(): - allure.dynamic.feature('Roles') +def role_test_setup(): + allure.dynamic.feature("Roles") diff --git a/client_cli/pytests/test/roles/test_register_roles.py b/client_cli/pytests/test/roles/test_register_roles.py index 84d5d3a32b6..583976956a6 100644 --- a/client_cli/pytests/test/roles/test_register_roles.py +++ b/client_cli/pytests/test/roles/test_register_roles.py @@ -1,30 +1,31 @@ -import allure +import allure # type: ignore import pytest + @pytest.fixture(scope="function", autouse=True) def story_account_registers_roles(): - allure.dynamic.story('Account registers a role') + allure.dynamic.story("Account registers a role") -@allure.label('sdk_test_id', 'register_role') +@allure.label("sdk_test_id", "register_role") @pytest.mark.xfail(reason="TO DO") -def test_register_role( - GIVEN_fake_name): +def test_register_role(GIVEN_fake_name): assert 0 - -@allure.label('sdk_test_id', 'attach_permissions_to_role') +@allure.label("sdk_test_id", "attach_permissions_to_role") @pytest.mark.xfail(reason="TO DO") def test_attach_permissions_to_role( - GIVEN_existing_asset_definition_with_store_value_type): + GIVEN_registered_asset_definition_with_store_value_type, +): assert 0 -@allure.label('sdk_test_id', 'grant_role_to_account') +@allure.label("sdk_test_id", "grant_role_to_account") @pytest.mark.xfail(reason="TO DO") def test_grant_role_to_account( - GIVEN_currently_authorized_account, - GIVEN_new_one_existing_account, - GIVEN_existing_asset_definition_with_store_value_type): + GIVEN_currently_authorized_account, + GIVEN_registered_account, + GIVEN_registered_asset_definition_with_store_value_type, +): assert 0 diff --git a/client_cli/pytests/test/triggers/conftest.py b/client_cli/pytests/test/triggers/conftest.py new file mode 100644 index 00000000000..be5e82bd37e --- /dev/null +++ b/client_cli/pytests/test/triggers/conftest.py @@ -0,0 +1,9 @@ +from test import GIVEN_currently_authorized_account + +import allure # type: ignore +import pytest + + +@pytest.fixture(scope="function", autouse=True) +def trigger_test_setup(): + allure.dynamic.feature("Triggers") diff --git a/client_cli/pytests/test/triggers/test_register_trigger.py b/client_cli/pytests/test/triggers/test_register_trigger.py new file mode 100644 index 00000000000..e1c0e5169a2 --- /dev/null +++ b/client_cli/pytests/test/triggers/test_register_trigger.py @@ -0,0 +1,23 @@ +import allure # type: ignore +import pytest + +from src.client_cli import client_cli, have, iroha + + +@pytest.fixture(scope="function", autouse=True) +def story_account_registers_trigger(): + allure.dynamic.story("Account register a register_trigger") + allure.dynamic.label("permission", "no_permission_required") + + +@allure.label("sdk_test_id", "register_trigger") +@pytest.mark.xfail(reason="wait for #4151") +def test_register_trigger(GIVEN_currently_authorized_account): + with allure.step( + f'WHEN client_cli registers a register_trigger for "{GIVEN_currently_authorized_account}"' + ): + client_cli.register_trigger(GIVEN_currently_authorized_account) + with allure.step( + "THEN Iroha should have the asset with nft_number_1_for_genesis##genesis@genesis" + ): + iroha.should(have.asset("nft_number_1_for_genesis##genesis@genesis"))