diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..3811355 --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203 +per-file-ignores = + features/steps/*:F811 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..717c373 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +# https://github.com/actions/setup-python#caching-packages-dependencies +name: Continuous Integration +on: + push: + branches: + - 'main' + pull_request: + types: [opened, reopened] +jobs: + continuous-integration: + runs-on: ubuntu-latest + strategy: + matrix: + py: ["python:3.11", "python:3.10", "python:3.9"] + name: ${{ matrix.python-version }} testing & linters + steps: + - uses: actions/checkout@v4 + - name: Install poetry + run: pipx install poetry + - uses: actions/setup-python@v4 + id: pyver + with: + python-version: ${{ matrix.py }} + check-latest: true + cache: 'poetry' + - name: ${{ steps.pyver.outputs.python-version }} + run: python --version + - run: poetry install --no-interaction --sync + - run: poetry run tox --recreate \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0edd10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,69 @@ +# These are some examples of commonly ignored file patterns. +# You should customize this list as applicable to your project. +# Learn more about .gitignore: +# https://www.atlassian.com/git/tutorials/saving-changes/gitignore + +# Node artifact files +node_modules/ +dist/ + +# Compiled Java class files +*.class + +# Compiled Python bytecode +*.py[cod] + +# Log files +*.log + +# Package files +*.jar + +# Maven +target/ + +# JetBrains IDE +.idea/ + +# Unit test reports +TEST*.xml + +# Coverage +.coverage + +# Generated by MacOS +.DS_Store + +# Generated by Windows +Thumbs.db + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv + +# Automation artifacts +/htmlcov +/.pytest_cache +/.pytype +/.mypy_cache + +# Terraform +*.tfstate + +# Local settings +.env + +# Tox +.tox + +# integration test data +tests/data/integration \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..55a8788 --- /dev/null +++ b/README.md @@ -0,0 +1,257 @@ +## Introduction +Welcome to the python ishare package. This package implements helpers for the +authentication flow between iShare services. Specifically, the part of the process +where a json web token (per ishare specification) is transformed into an access token. +This access token can then be used to communicate with the rest of a Role's service +endpoints. + +This package could be relevant for connecting to/from the following +[roles](https://ishare.eu/about-ishare/roles/): +- Satellite or Data Space Authorities +- Authentication Registries +- Identity Providers +- Service or Data Provider +- Service or Data Consumers + +For more information on iShare; +* [The iShare website](https://ishare.eu/nl/) +* [The developer documentation](https://dev.ishare.eu/) +* [The iShare Framework](https://ishareworks.atlassian.net/wiki/spaces/IS/overview) +* [The iShare Satellite repository](https://github.com/iSHAREScheme/iSHARESatellite) + +## Usage + +### Prerequisites +For a working connection with, for example, an iShare satellite you need a participant +registration. + +- From the registration with an iShare Satellite. + - (encryption) The registered Certificate's *private* RSA key. This must be kept SECRET! + - (`x509 header`) The registered Certificate's *public* x509 certificate chain. + - (`client_id`) The registered participant's EORI number + - The registered participant adherence status is "Active". +- For every participant Service you want to connect to: + - (`audience`) The target service's EORI number + - (decryption) Their public x509 (can be retrieved from an iShare Satellite) + - The domain of the service you're connecting to + +All of these are required to encrypt and decrypt communication between different the +iShare services. For more detailed information refer to the `private key jwt` json web +token flow [here](https://oauth.net/private-key-jwt/). + +### The three-step methodology + +1. Create a json web token per ishare [specification](https://dev.ishare.eu/introduction/jwt.html)). +2. Use a client interface to communicate with a role +3. Use the `IShareSatelliteClient` interface to verify a participant + +#### I. Creating the json web token + +There is a convenience method (`python_ishare.create_jwt`) in the package to help create +the token. + +```python +from pathlib import Path + +from cryptography.x509 import load_pem_x509_certificates, Certificate +from cryptography.hazmat.primitives.serialization import load_pem_private_key +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey + +from python_ishare import create_jwt + +YOUR_PARTICIPANT_EORI = "XXX" +THEIR_PARTICIPANT_EORI = "YYY" + +# Load your RSA key to an RSAPrivateKey +with Path("path/to/my/key.pem") as file: + private_key: RSAPrivateKey = load_pem_private_key( + file.read_bytes(), + password=b"your_password_or_None" + ) + +with Path("path/to/my/certs.pem") as file: + chain: list[Certificate] = load_pem_x509_certificates( + file.read_bytes() + ) + +# Create the actual token +my_token = create_jwt( + payload={ + "iss": YOUR_PARTICIPANT_EORI, + "sub": YOUR_PARTICIPANT_EORI, + "aud": THEIR_PARTICIPANT_EORI, + "jti": "your-unique-id" # optional + }, + private_key=private_key, + x5c_certificate_chain=chain +) +``` + +This method is strictly seperated out from the client interfacing (next step). This is +for two reasons; + +- It makes you responsible for loading the `Certificate`'s from the chain and +`RSAPrivateKey` as such that these important files can be stored anywhere. +- It makes it possible to sign the json web token externally using an AWS asymmetric key +for example. A great security solution. + +#### II. Connecting to an iShare Satellite + +To connect to an iShare satellite this package provides an `IShareSatelliteClient` +interface class. + +```python +from python_ishare import IShareSatelliteClient + +# From step 1 +YOUR_PARTICIPANT_EORI = "XXX" +my_token = create_jwt(...) +public_key = ... + +client = IShareSatelliteClient( + target_domain="satellite.ishare.com", + target_public_key=public_key, + client_eori=YOUR_PARTICIPANT_EORI, + json_web_token=my_token +) + +# To retrieve the satellite's capabilities +capabilities = client.get_capabilities() +print(capabilities) + +# To retrieve the satellite's public capabilities +public_capabilities = client.get_capabilities(use_token=False) +print(public_capabilities) +``` + +The value of this interface class is that you never needed to worry about access tokens. +This is handled for you underwater. Tokens are re-used whenever a new request is made. + +#### III. Verifying an iShare participant + +Verifying a participant is a key responsibility for a number of roles. The +`IShareSatelliteClient` has a method to do this for you. + +```python +# From step 2 +YOUR_PARTICIPANT_EORI = "XXX" +client = IShareSatelliteClient(...) + +# Assuming you have some python web framework there will be a request +request = ... + +# If you're a service provider, you can use this to verify other parties ishare tokens +client.verify_json_web_token( + audience=YOUR_PARTICIPANT_EORI, + client_id=request.param["client_id"], + client_assertion=request.param["client_assertion"], + client_assertion_type=request.param["client_assertion_type"], + grant_type=request.param["grant_type"], + scope=request.param["scope"], +) +``` + +> [!IMPORTANT] +> The `verify_json_web_token` currently does not implement full Certificate validation! + +## Developer Setup +Everything needed to start developing on this package. + +## Quick start `||` tl;dr + +1. Install the python package management tool; [`poetry`](https://python-poetry.org/docs/#installation). + ```shell + curl -sSL https://install.python-poetry.org | python3 - + ``` + +2. Install the local python project. + ```shell + poetry install + ``` + +3. Run the test suite and linters using [`tox`](https://tox.wiki/en/4.11.4/). + ```shell + poetry run tox + ``` + +4. Run development commands inside the virtualenv. + + ```shell + poetry run + ``` + +## Setup +This project runs on a central `pyproject.toml` configuration. Here you can find the +information for what python version to use and dependencies. Configuration for the +various linters, testing frameworks is also defined there. + +### Poetry - Project installer +Poetry is a smooth dependency manager for Python that is seemingly taking over the +market. The [Documentation](https://python-poetry.org/docs/_) is +of excellent quality. + +[Poetry installation](https://python-poetry.org/docs/#installation): + +```shell +curl -sSL https://install.python-poetry.org | python3 - +``` + +[For a specific version of poetry](https://python-poetry.org/docs/1.5/#installing-with-the-official-installer): + +```shell +curl -sSL https://install.python-poetry.org | python3 - --version +``` + +[For users of pipx](https://python-poetry.org/docs/1.5/#installing-with-pipx): +```shell +pipx install poetry +``` + +### Project Installation + +Running `poetry install` will install the source code and all it's dependencies +including development packages. + +### Pythonic Dependencies +Behind the scenes Poetry creates and manages the virtualenv with all the python +package dependencies necessary for the project. This means that to access packages and +scripts installed by Poetry you need to run commands in that environment. + +There are two different ways, pick your favorite; + +- Wrap your command in a `poetry run` format. For example; + + ```shell + # poetry run + poetry run black + ``` + +- Activate the virtualenv in your shell, and all commands will run accordingly. + + ```shell + # + poetry shell + # Spawns a shell with the venv activated + black + # (And all other commands after that) + ``` + +💡 *There are IDE's that understand Poetry and handle it for you so you don't have to +do anything else. For instance in PyCharm, you can configure your interpreter to use +Poetry.* + +### Linting libraries used +The following packages are used to ensure code cody cleanliness and monitor some +known/preventable security issues. + +- [`black`](https://github.com/psf/black) is used to auto-format code. +- [`isort`](https://pycqa.github.io/isort/) is used to automatically sort imports. +- [`flake8`](https://flake8.pycqa.org/en/latest/) is used enforce standard style guide +conventions not automatically handled by black. +- [`bandit`](https://bandit.readthedocs.io/en/latest/) is used to detect some common +security issues. +- [`mypy`](https://mypy-lang.org/) is used to validate python typehints for correctness. +- [`safety`](https://pypi.org/project/safety/) is used to check +dependencies for known security issues against a database. +- [`tox`](https://tox.wiki/) is used to run all of the above commands inside an isolated python +environment. \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..dc76ebb --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1699 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "aiohttp" +version = "3.9.1" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501"}, + {file = "aiohttp-3.9.1-cp310-cp310-win32.whl", hash = "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489"}, + {file = "aiohttp-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a"}, + {file = "aiohttp-3.9.1-cp311-cp311-win32.whl", hash = "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544"}, + {file = "aiohttp-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f"}, + {file = "aiohttp-3.9.1-cp312-cp312-win32.whl", hash = "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed"}, + {file = "aiohttp-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8"}, + {file = "aiohttp-3.9.1-cp38-cp38-win32.whl", hash = "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4"}, + {file = "aiohttp-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c"}, + {file = "aiohttp-3.9.1-cp39-cp39-win32.whl", hash = "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7"}, + {file = "aiohttp-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf"}, + {file = "aiohttp-3.9.1.tar.gz", hash = "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.1.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"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,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]"] + +[[package]] +name = "bandit" +version = "1.7.5" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.7" +files = [ + {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, + {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +GitPython = ">=1.0.1" +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" +tomli = {version = ">=1.1.0", optional = true, markers = "python_version < \"3.11\" and extra == \"toml\""} + +[package.extras] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] + +[[package]] +name = "black" +version = "23.11.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, + {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, + {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, + {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, + {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, + {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, + {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, + {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, + {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, + {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, + {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, + {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, + {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, + {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, + {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, + {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, + {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, + {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, +] + +[package.dependencies] +aiohttp = {version = ">=3.7.4", optional = true, markers = "extra == \"d\""} +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.3.2" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, + {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, +] + +[[package]] +name = "certifi" +version = "2023.11.17" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {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 = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[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" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.3.2" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, + {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, + {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, + {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, + {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, + {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, + {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, + {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, + {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, + {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, + {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, + {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, + {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cryptography" +version = "41.0.7" +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.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"}, + {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"}, + {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"}, + {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"}, + {file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"}, + {file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"}, + {file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[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)"] +nox = ["nox"] +pep8test = ["black", "check-sdist", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "distlib" +version = "0.3.7" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, + {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, +] + +[[package]] +name = "dparse" +version = "0.6.3" +description = "A parser for Python dependency files" +optional = false +python-versions = ">=3.6" +files = [ + {file = "dparse-0.6.3-py3-none-any.whl", hash = "sha256:0d8fe18714056ca632d98b24fbfc4e9791d4e47065285ab486182288813a5318"}, + {file = "dparse-0.6.3.tar.gz", hash = "sha256:27bb8b4bcaefec3997697ba3f6e06b2447200ba273c0b085c3d012a04571b528"}, +] + +[package.dependencies] +packaging = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv (<=2022.12.19)"] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.13.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "flake8" +version = "6.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" + +[[package]] +name = "frozenlist" +version = "1.4.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, + {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, + {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, + {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, + {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, + {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, + {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, + {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, + {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, + {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, +] + +[[package]] +name = "gitdb" +version = "4.0.11" +description = "Git Object Database" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, + {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, +] + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.40" +description = "GitPython is a Python library used to interact with Git repositories" +optional = false +python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, + {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, +] + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[package.extras] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.12.0" +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"}, +] + +[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"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + +[[package]] +name = "marshmallow" +version = "3.20.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.20.1-py3-none-any.whl", hash = "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c"}, + {file = "marshmallow-3.20.1.tar.gz", hash = "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.13)", "autodocsumm (==0.2.11)", "sphinx (==7.0.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "multidict" +version = "6.0.4" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + +[[package]] +name = "mypy" +version = "1.7.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340"}, + {file = "mypy-1.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49"}, + {file = "mypy-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5"}, + {file = "mypy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d"}, + {file = "mypy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"}, + {file = "mypy-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7"}, + {file = "mypy-1.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51"}, + {file = "mypy-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a"}, + {file = "mypy-1.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28"}, + {file = "mypy-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42"}, + {file = "mypy-1.7.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1"}, + {file = "mypy-1.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33"}, + {file = "mypy-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb"}, + {file = "mypy-1.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea"}, + {file = "mypy-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82"}, + {file = "mypy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200"}, + {file = "mypy-1.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7"}, + {file = "mypy-1.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e"}, + {file = "mypy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9"}, + {file = "mypy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7"}, + {file = "mypy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe"}, + {file = "mypy-1.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce"}, + {file = "mypy-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a"}, + {file = "mypy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120"}, + {file = "mypy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6"}, + {file = "mypy-1.7.1-py3-none-any.whl", hash = "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea"}, + {file = "mypy-1.7.1.tar.gz", hash = "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {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.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "pbr" +version = "6.0.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, +] + +[[package]] +name = "platformdirs" +version = "4.0.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, + {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[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" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pyflakes" +version = "3.1.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyjwt" +version = "2.8.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pyproject-api" +version = "1.6.1" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, + {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, +] + +[package.dependencies] +packaging = ">=23.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] + +[[package]] +name = "pytest" +version = "7.4.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, + {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "responses" +version = "0.24.1" +description = "A utility library for mocking out the `requests` Python library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "responses-0.24.1-py3-none-any.whl", hash = "sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9"}, + {file = "responses-0.24.1.tar.gz", hash = "sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c"}, +] + +[package.dependencies] +pyyaml = "*" +requests = ">=2.30.0,<3.0" +urllib3 = ">=1.25.10,<3.0" + +[package.extras] +tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-PyYAML", "types-requests"] + +[[package]] +name = "rich" +version = "13.7.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "ruamel-yaml" +version = "0.18.5" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.5-py3-none-any.whl", hash = "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada"}, + {file = "ruamel.yaml-0.18.5.tar.gz", hash = "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" +files = [ + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +] + +[[package]] +name = "safety" +version = "2.4.0b2" +description = "Checks installed dependencies for known vulnerabilities and licenses." +optional = false +python-versions = "*" +files = [ + {file = "safety-2.4.0b2-py3-none-any.whl", hash = "sha256:63773ce92e17f5f80e7dff4c8a25d8abb7d62d375897b5f3bb4afe9313b100ff"}, + {file = "safety-2.4.0b2.tar.gz", hash = "sha256:9907010c6ca7720861ca7fa1496bdb80449b0619ca136eb7ac7e02bd3516cd4f"}, +] + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.6.2" +jinja2 = {version = ">=3.1.0", markers = "python_version >= \"3.7\""} +marshmallow = {version = ">=3.15.0", markers = "python_version >= \"3.7\""} +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" +setuptools = {version = ">=65.5.1", markers = "python_version >= \"3.7\""} +urllib3 = ">=1.26.5" + +[package.extras] +github = ["pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] + +[[package]] +name = "setuptools" +version = "69.0.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, + {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "smmap" +version = "5.0.1" +description = "A pure Python implementation of a sliding window memory map manager" +optional = false +python-versions = ">=3.7" +files = [ + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, +] + +[[package]] +name = "stevedore" +version = "5.1.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, + {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, +] + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tox" +version = "4.11.4" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.11.4-py3-none-any.whl", hash = "sha256:2adb83d68f27116812b69aa36676a8d6a52249cb0d173649de0e7d0c2e3e7229"}, + {file = "tox-4.11.4.tar.gz", hash = "sha256:73a7240778fabf305aeb05ab8ea26e575e042ab5a18d71d0ed13e343a51d6ce1"}, +] + +[package.dependencies] +cachetools = ">=5.3.1" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.12.3" +packaging = ">=23.1" +platformdirs = ">=3.10" +pluggy = ">=1.3" +pyproject-api = ">=1.6.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.24.3" + +[package.extras] +docs = ["furo (>=2023.8.19)", "sphinx (>=7.2.4)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.24)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=1)", "diff-cover (>=7.7)", "distlib (>=0.3.7)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.18)", "psutil (>=5.9.5)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.12)", "wheel (>=0.41.2)"] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.12" +description = "Typing stubs for PyYAML" +optional = false +python-versions = "*" +files = [ + {file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"}, + {file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"}, +] + +[[package]] +name = "types-requests" +version = "2.31.0.10" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "types-requests-2.31.0.10.tar.gz", hash = "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92"}, + {file = "types_requests-2.31.0.10-py3-none-any.whl", hash = "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc"}, +] + +[package.dependencies] +urllib3 = ">=2" + +[[package]] +name = "typing-extensions" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[[package]] +name = "urllib3" +version = "2.1.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, + {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.24.7" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.24.7-py3-none-any.whl", hash = "sha256:a18b3fd0314ca59a2e9f4b556819ed07183b3e9a3702ecfe213f593d44f7b3fd"}, + {file = "virtualenv-20.24.7.tar.gz", hash = "sha256:69050ffb42419c91f6c1284a7b24e0475d793447e35929b488bf6a0aade39353"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "yarl" +version = "1.9.3" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32435d134414e01d937cd9d6cc56e8413a8d4741dea36af5840c7750f04d16ab"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9a5211de242754b5e612557bca701f39f8b1a9408dff73c6db623f22d20f470e"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:525cd69eff44833b01f8ef39aa33a9cc53a99ff7f9d76a6ef6a9fb758f54d0ff"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc94441bcf9cb8c59f51f23193316afefbf3ff858460cb47b5758bf66a14d130"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e36021db54b8a0475805acc1d6c4bca5d9f52c3825ad29ae2d398a9d530ddb88"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0f17d1df951336a02afc8270c03c0c6e60d1f9996fcbd43a4ce6be81de0bd9d"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5f3faeb8100a43adf3e7925d556801d14b5816a0ac9e75e22948e787feec642"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aed37db837ecb5962469fad448aaae0f0ee94ffce2062cf2eb9aed13328b5196"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:721ee3fc292f0d069a04016ef2c3a25595d48c5b8ddc6029be46f6158d129c92"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b8bc5b87a65a4e64bc83385c05145ea901b613d0d3a434d434b55511b6ab0067"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:dd952b9c64f3b21aedd09b8fe958e4931864dba69926d8a90c90d36ac4e28c9a"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:c405d482c320a88ab53dcbd98d6d6f32ada074f2d965d6e9bf2d823158fa97de"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9df9a0d4c5624790a0dea2e02e3b1b3c69aed14bcb8650e19606d9df3719e87d"}, + {file = "yarl-1.9.3-cp310-cp310-win32.whl", hash = "sha256:d34c4f80956227f2686ddea5b3585e109c2733e2d4ef12eb1b8b4e84f09a2ab6"}, + {file = "yarl-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:cf7a4e8de7f1092829caef66fd90eaf3710bc5efd322a816d5677b7664893c93"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d61a0ca95503867d4d627517bcfdc28a8468c3f1b0b06c626f30dd759d3999fd"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73cc83f918b69110813a7d95024266072d987b903a623ecae673d1e71579d566"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d81657b23e0edb84b37167e98aefb04ae16cbc5352770057893bd222cdc6e45f"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a1a8443091c7fbc17b84a0d9f38de34b8423b459fb853e6c8cdfab0eacf613"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe34befb8c765b8ce562f0200afda3578f8abb159c76de3ab354c80b72244c41"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c757f64afe53a422e45e3e399e1e3cf82b7a2f244796ce80d8ca53e16a49b9f"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a57b41a0920b9a220125081c1e191b88a4cdec13bf9d0649e382a822705c65"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:632c7aeb99df718765adf58eacb9acb9cbc555e075da849c1378ef4d18bf536a"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b0b8c06afcf2bac5a50b37f64efbde978b7f9dc88842ce9729c020dc71fae4ce"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1d93461e2cf76c4796355494f15ffcb50a3c198cc2d601ad8d6a96219a10c363"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4003f380dac50328c85e85416aca6985536812c082387255c35292cb4b41707e"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4d6d74a97e898c1c2df80339aa423234ad9ea2052f66366cef1e80448798c13d"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b61e64b06c3640feab73fa4ff9cb64bd8182de52e5dc13038e01cfe674ebc321"}, + {file = "yarl-1.9.3-cp311-cp311-win32.whl", hash = "sha256:29beac86f33d6c7ab1d79bd0213aa7aed2d2f555386856bb3056d5fdd9dab279"}, + {file = "yarl-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:f7271d6bd8838c49ba8ae647fc06469137e1c161a7ef97d778b72904d9b68696"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:dd318e6b75ca80bff0b22b302f83a8ee41c62b8ac662ddb49f67ec97e799885d"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4b1efb11a8acd13246ffb0bee888dd0e8eb057f8bf30112e3e21e421eb82d4a"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f034386e5550b5dc8ded90b5e2ff7db21f0f5c7de37b6efc5dac046eb19c10"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd49a908cb6d387fc26acee8b7d9fcc9bbf8e1aca890c0b2fdfd706057546080"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa4643635f26052401750bd54db911b6342eb1a9ac3e74f0f8b58a25d61dfe41"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e741bd48e6a417bdfbae02e088f60018286d6c141639359fb8df017a3b69415a"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c86d0d0919952d05df880a1889a4f0aeb6868e98961c090e335671dea5c0361"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d5434b34100b504aabae75f0622ebb85defffe7b64ad8f52b8b30ec6ef6e4b9"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79e1df60f7c2b148722fb6cafebffe1acd95fd8b5fd77795f56247edaf326752"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:44e91a669c43f03964f672c5a234ae0d7a4d49c9b85d1baa93dec28afa28ffbd"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3cfa4dbe17b2e6fca1414e9c3bcc216f6930cb18ea7646e7d0d52792ac196808"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:88d2c3cc4b2f46d1ba73d81c51ec0e486f59cc51165ea4f789677f91a303a9a7"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cccdc02e46d2bd7cb5f38f8cc3d9db0d24951abd082b2f242c9e9f59c0ab2af3"}, + {file = "yarl-1.9.3-cp312-cp312-win32.whl", hash = "sha256:96758e56dceb8a70f8a5cff1e452daaeff07d1cc9f11e9b0c951330f0a2396a7"}, + {file = "yarl-1.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:c4472fe53ebf541113e533971bd8c32728debc4c6d8cc177f2bff31d011ec17e"}, + {file = "yarl-1.9.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:126638ab961633f0940a06e1c9d59919003ef212a15869708dcb7305f91a6732"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c99ddaddb2fbe04953b84d1651149a0d85214780e4d0ee824e610ab549d98d92"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dab30b21bd6fb17c3f4684868c7e6a9e8468078db00f599fb1c14e324b10fca"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:828235a2a169160ee73a2fcfb8a000709edf09d7511fccf203465c3d5acc59e4"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc391e3941045fd0987c77484b2799adffd08e4b6735c4ee5f054366a2e1551d"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51382c72dd5377861b573bd55dcf680df54cea84147c8648b15ac507fbef984d"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:28a108cb92ce6cf867690a962372996ca332d8cda0210c5ad487fe996e76b8bb"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8f18a7832ff85dfcd77871fe677b169b1bc60c021978c90c3bb14f727596e0ae"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:7eaf13af79950142ab2bbb8362f8d8d935be9aaf8df1df89c86c3231e4ff238a"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:66a6dbf6ca7d2db03cc61cafe1ee6be838ce0fbc97781881a22a58a7c5efef42"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a0a4f3aaa18580038cfa52a7183c8ffbbe7d727fe581300817efc1e96d1b0e9"}, + {file = "yarl-1.9.3-cp37-cp37m-win32.whl", hash = "sha256:946db4511b2d815979d733ac6a961f47e20a29c297be0d55b6d4b77ee4b298f6"}, + {file = "yarl-1.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2dad8166d41ebd1f76ce107cf6a31e39801aee3844a54a90af23278b072f1ccf"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bb72d2a94481e7dc7a0c522673db288f31849800d6ce2435317376a345728225"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9a172c3d5447b7da1680a1a2d6ecdf6f87a319d21d52729f45ec938a7006d5d8"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2dc72e891672343b99db6d497024bf8b985537ad6c393359dc5227ef653b2f17"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8d51817cf4b8d545963ec65ff06c1b92e5765aa98831678d0e2240b6e9fd281"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53ec65f7eee8655bebb1f6f1607760d123c3c115a324b443df4f916383482a67"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cfd77e8e5cafba3fb584e0f4b935a59216f352b73d4987be3af51f43a862c403"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e73db54c967eb75037c178a54445c5a4e7461b5203b27c45ef656a81787c0c1b"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09c19e5f4404574fcfb736efecf75844ffe8610606f3fccc35a1515b8b6712c4"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6280353940f7e5e2efaaabd686193e61351e966cc02f401761c4d87f48c89ea4"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c25ec06e4241e162f5d1f57c370f4078797ade95c9208bd0c60f484834f09c96"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7217234b10c64b52cc39a8d82550342ae2e45be34f5bff02b890b8c452eb48d7"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4ce77d289f8d40905c054b63f29851ecbfd026ef4ba5c371a158cfe6f623663e"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f74b015c99a5eac5ae589de27a1201418a5d9d460e89ccb3366015c6153e60a"}, + {file = "yarl-1.9.3-cp38-cp38-win32.whl", hash = "sha256:8a2538806be846ea25e90c28786136932ec385c7ff3bc1148e45125984783dc6"}, + {file = "yarl-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:6465d36381af057d0fab4e0f24ef0e80ba61f03fe43e6eeccbe0056e74aadc70"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2f3c8822bc8fb4a347a192dd6a28a25d7f0ea3262e826d7d4ef9cc99cd06d07e"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7831566595fe88ba17ea80e4b61c0eb599f84c85acaa14bf04dd90319a45b90"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff34cb09a332832d1cf38acd0f604c068665192c6107a439a92abfd8acf90fe2"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe8080b4f25dfc44a86bedd14bc4f9d469dfc6456e6f3c5d9077e81a5fedfba7"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8535e111a064f3bdd94c0ed443105934d6f005adad68dd13ce50a488a0ad1bf3"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d155a092bf0ebf4a9f6f3b7a650dc5d9a5bbb585ef83a52ed36ba46f55cc39d"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:778df71c8d0c8c9f1b378624b26431ca80041660d7be7c3f724b2c7a6e65d0d6"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f9cafaf031c34d95c1528c16b2fa07b710e6056b3c4e2e34e9317072da5d1a"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ca6b66f69e30f6e180d52f14d91ac854b8119553b524e0e28d5291a724f0f423"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0e7e83f31e23c5d00ff618045ddc5e916f9e613d33c5a5823bc0b0a0feb522f"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:af52725c7c39b0ee655befbbab5b9a1b209e01bb39128dce0db226a10014aacc"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0ab5baaea8450f4a3e241ef17e3d129b2143e38a685036b075976b9c415ea3eb"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d350388ba1129bc867c6af1cd17da2b197dff0d2801036d2d7d83c2d771a682"}, + {file = "yarl-1.9.3-cp39-cp39-win32.whl", hash = "sha256:e2a16ef5fa2382af83bef4a18c1b3bcb4284c4732906aa69422cf09df9c59f1f"}, + {file = "yarl-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:d92d897cb4b4bf915fbeb5e604c7911021a8456f0964f3b8ebbe7f9188b9eabb"}, + {file = "yarl-1.9.3-py3-none-any.whl", hash = "sha256:271d63396460b6607b588555ea27a1a02b717ca2e3f2cf53bdde4013d7790929"}, + {file = "yarl-1.9.3.tar.gz", hash = "sha256:4a14907b597ec55740f63e52d7fee0e9ee09d5b9d57a4f399a7423268e457b57"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[metadata] +lock-version = "2.0" +python-versions = "^3.8.1" +content-hash = "89441fae65a9361890536b348c211413683fcf2fd4d25b703b15467d7396ab40" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..52a3a9b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,52 @@ +[tool.poetry] +name = "python_ishare" +version = "0.1.0" +description = "iShare python interface client package" +readme = "README.md" +authors = [ + +] +packages = [ + { include = "python_ishare", from = "src" } +] + +[tool.poetry.dependencies] +python = "^3.8.1" +pyjwt = "^2.8.0" +cryptography = "^41.0.5" + +[tool.poetry.group.dev.dependencies] +bandit = {extras = ["toml"], version = "^1.7.5"} +black = {extras = ["d"], version = "^23.11.0"} +flake8 = "^6" +isort = "^5.12.0" +mypy = "^1.7.0" +pytest = "^7.4.2" +pytest-cov = "^4.1.0" +pytest-mock = "^3.11.1" +responses = "^0.24.0" +safety = "^2.3.5" +tox = "^4.0.0" +types-pyyaml = "^6.0.12.12" +types-requests = ">2.31.0.7" + +[tool.isort] +profile = "black" + +[tool.bandit] +exclude_dirs = ["tests"] + +[tool.mypy] +files = ["src/*"] +exclude = ["tests"] +ignore_missing_imports = true +strict = true +show_error_codes = true + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "--tb=native --cov=python_ishare --cov-report=html --cov-branch" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/src/python_ishare/__init__.py b/src/python_ishare/__init__.py new file mode 100644 index 0000000..6e83ec2 --- /dev/null +++ b/src/python_ishare/__init__.py @@ -0,0 +1,12 @@ +# flake8: noqa +import logging + +from python_ishare.authentication import ( + create_jwt, + decode_jwt, + x5c_b64_to_certificate, +) +from python_ishare.clients import CommonBaseClient, IShareSatelliteClient +from python_ishare.verification import validate_client_assertion + +logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/src/python_ishare/authentication.py b/src/python_ishare/authentication.py new file mode 100644 index 0000000..bfd0f20 --- /dev/null +++ b/src/python_ishare/authentication.py @@ -0,0 +1,149 @@ +import base64 +import json +import time +import uuid +from typing import Any, Optional, TypedDict, Union, Iterator + +import jwt +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.serialization import Encoding +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey +from cryptography.x509 import Certificate, load_der_x509_certificate +from typing_extensions import NotRequired + +from python_ishare.exceptions import IShareInvalidTokenX5C + +ENCRYPTION_ALGORITHM = "RS256" + + +class Payload(TypedDict, total=False): + # The iss claim is the EORI number of the client. + iss: str + # The sub claim is (also) the EORI number of the client. + sub: str + # The aud claim contains only the EORI number of the server. + aud: str + # The jti is an identifier to track the JWT for audit or logging purposes. + jti: NotRequired[str] + + +class CertificateInfo(TypedDict, total=False): + # rfc4514 string of the certificate subject + subject: str + # The fingerprint of the certificate, the HEX representation of the SHA256, upper... + finger_print: str + + +def x5c_b64_to_certificate(b64_certs: list[str]) -> Iterator[Certificate]: + """Revert the certificate from a x5c header.""" + for cert in b64_certs: + yield load_der_x509_certificate(data=base64.b64decode(cert)) + + +def x5c_certificates_to_b64(certs: list[Certificate]) -> Iterator[str]: + """Convert the certificate to a x509 header.""" + for cert in certs: + yield str( + base64.b64encode( + cert.public_bytes( + encoding=Encoding.DER + ) + ), + encoding="utf8" + ) + + +def get_b64_x5c_fingerprints(json_web_token: str) -> list[CertificateInfo]: + """ + Extracts the x5c header certificate metadata. + """ + headers = jwt.get_unverified_header(jwt=json_web_token) + x5c = headers.get("x5c") + + if not isinstance(x5c, list): + raise IShareInvalidTokenX5C("Extracted x5c header is not a list.") + + _hash = hashes.SHA256() + _finger_prints: list[CertificateInfo] = [] + + for cert in x5c_b64_to_certificate(x5c): + _finger_print = cert.fingerprint(algorithm=_hash).hex().upper() + _finger_prints.append( + dict( + finger_print=_finger_print, + subject=cert.subject.rfc4514_string(), + ) + ) + + return _finger_prints + + +def create_jwt( + payload: Payload, + private_key: RSAPrivateKey, + x5c_certificate_chain: list[Certificate], + **kwargs: dict[str, Union[json.JSONEncoder, bool]] +) -> str: + """ + Create a valid JWT for iShare specific use. As per the "private_key_jwt" + methodology in openid connect. + + Note: iShare spec dictates a lifetime of 30 seconds. + + https://openid.net/specs/openid-connect-core-1_0.html + + :param payload: the payload to be included into the JWT; + {"iss": "eori", "sub": "eori_number", "aud": "eori_number"} + :param private_key: The RSAPrivateKey instance of the private key to be used to + encrypt the payload. + :param x5c_certificate_chain: An array of the complete certificate chain that should + be used to validate the JWT signature. Up until the issuing CA from the + trust_list from the relevant iShare Satellite. + """ + headers = { + "alg": ENCRYPTION_ALGORITHM, + "typ": "JWT", + "x5c": [c for c in x5c_certificates_to_b64(certs=x5c_certificate_chain)] + } + + _payload = {**payload} + + if "jti" not in _payload: + _payload["jti"] = str(uuid.uuid4()) + + unix_epoch = time.time() + _payload["iat"] = unix_epoch + _payload["exp"] = unix_epoch + 30 + + return jwt.encode( + payload=_payload, + key=private_key, # type: ignore[arg-type,unused-ignore] + headers=headers, + algorithm=ENCRYPTION_ALGORITHM, + **kwargs # type: ignore[arg-type] + ) + + +def decode_jwt( + json_web_token: Union[str, bytes], + public_x509_cert: list[Certificate], + audience: Optional[str] = None, + **kwargs: Any +) -> Any: + """ + Raises specific exception if IShare JWT is not valid. + + :param json_web_token: + :param public_x509_cert: + The public X509 certificate uploaded to an iShare Satellite. + :param audience: eori of the party receiving the json web token + :return: they payload of the decoded json_web_token. + :raises IShareAuthenticationException: if JWT token not valid + """ + return jwt.decode( + jwt=json_web_token, + key=public_x509_cert[0].public_key(), # type: ignore[arg-type,unused-ignore] + audience=audience, + algorithms=[ENCRYPTION_ALGORITHM], + **kwargs + ) diff --git a/src/python_ishare/clients.py b/src/python_ishare/clients.py new file mode 100644 index 0000000..7c20945 --- /dev/null +++ b/src/python_ishare/clients.py @@ -0,0 +1,326 @@ +import logging +from typing import Any, Callable, Literal, Optional, TypedDict, Union +from urllib.parse import urljoin + +import requests +from cryptography.x509 import Certificate + +from python_ishare.authentication import decode_jwt, get_b64_x5c_fingerprints +from python_ishare.exceptions import ( + IShareInvalidCertificateIssuer, + ISharePartyStatusInvalid, +) +from python_ishare.verification import validate_client_assertion + +logger = logging.getLogger("ishare_auth.authentication") + +CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" +GRANT_TYPE = "client_credentials" +SCOPE = "iSHARE" + + +class AccessToken(TypedDict): + access_token: str + token_type: str + expires_in: int + + +class CommonBaseClient: + """ + Standalone base client that can be extended for role specific methods. + + - Implements the common auth flow for all iShare participants / roles. + - Implements common api methods + - Can be extended for different access_token/jwt flow. + + Common endpoint documentation: + - https://dev.ishare.eu/common/token.html + - https://dev.ishare.eu/common/capabilities.html + + The base class handles two important things; + - basic authentication flow (transforming an JWS into an access_token) + - decryption of the json response token + """ + + _access_token: Optional[AccessToken] + _json_web_token: Union[str, Callable[[], str]] + + def __init__( + self, + target_domain: str, + target_public_key: list[Certificate], + client_eori: str, + json_web_token: Union[str, Callable[[], str]], + timeout: float = 10.0, + extra: Optional[Any] = None, + ) -> None: + """ + :param target_domain: Domain part of the target service, including scheme + :param target_public_key: Public key to verify the received json web tokens + :param client_eori: ishare participant eori of the client + :param json_web_token: + - type str: pre-made jwt for ishare (see authentication.create_jwt) + - type Callable: a function/class that returns a jwt on demand + :param timeout: used to stop the web requests to avoid hanging requests + :param extra: If methods below are overriden this can be used to pass arguments. + """ + self.target_domain = target_domain + self.target_public_key = target_public_key + self.client_eori = client_eori + self.timeout = timeout + self.extra = extra + + self._json_web_token = json_web_token + self._access_token: Optional[AccessToken] = None + + @property + def access_token(self) -> Optional[str]: + if self._access_token: + return self._access_token["access_token"] + return None + + @property + def json_web_token(self) -> str: + """ + Subclass this method if an alternative method is required for generating the JWS + f.e. for security purposes or when it needs refreshing *or* use pass a Callable + argument for the json_web_token. + """ + if callable(self._json_web_token): + return self._json_web_token() + return self._json_web_token + + @property + def is_valid_access_token(self) -> bool: + """ + Assumed here is that the access_token of the target has indefinite lifespan. + + If not, subclass this class and override this method. + """ + return self._access_token is not None + + def _get_auth(self, use_token: bool) -> Optional[dict[str, str]]: + """ + Returns an access_token if variable use_token resolves to true; + - returns existing one if already retrieved + - returns the result of requesting a new one + (as long as the jwt of the constructor is still valid) + """ + if not use_token: + return None + + if not self.is_valid_access_token: + self._access_token = self.request_access_token() + + return {"Authorization": f"Bearer {self.access_token}"} + + def request_access_token(self) -> Any: + """ + Request the target_domain for an access_token for use in api requests. + + - https://dev.ishare.eu/common/token.html + + :raises HTTPError: originating from requests library + :raises requests.exceptions.JSONDecodeError: if json body is invalid + :return: + """ + response = requests.post( + urljoin(self.target_domain, "/connect/token"), + data={ + "grant_type": GRANT_TYPE, + "scope": SCOPE, + "client_id": self.client_eori, # EORI number + "client_assertion_type": CLIENT_ASSERTION_TYPE, + "client_assertion": self.json_web_token, + }, + headers={"Content-Type": "application/x-www-form-urlencoded"}, + timeout=self.timeout, + ) + + # Raise exceptions here, let client handle them. + response.raise_for_status() + return response.json() + + def _get_request( + self, + endpoint_path: str, + decode_key: str, + use_token: bool = True, + params: Optional[dict[str, Any]] = None, + ) -> Any: + """ + + :param endpoint_path: + :param use_token: + :param decode_key: + Key to pop from the results and decode the jwt inside the json response body + :raises HTTPError: originating from requests library + :raises requests.exceptions.JSONDecodeError: if json body is invalid + :return: json as dict of the request *or* + decoded jws token as dict if decode_key given + """ + response = requests.get( + url=urljoin(self.target_domain, endpoint_path), + headers=self._get_auth(use_token=use_token), + timeout=self.timeout, + params=params, + ) + response.raise_for_status() + json: dict[str, Any] = response.json() + + _audience = self.client_eori if use_token else None + + return decode_jwt( + json_web_token=json[decode_key], + public_x509_cert=self.target_public_key, + audience=_audience, + ) + + def get_capabilities(self, use_token: bool = True) -> Any: + """ + https://dev.ishare.eu/common/capabilities.html + + :param use_token: If False the token will not be used and the unauthenticated + response is returned as per specification. + :return: + """ + return self._get_request( + endpoint_path="/capabilities", + use_token=use_token, + decode_key="capabilities_token", + ) + + +class IShareSatelliteClient(CommonBaseClient): + """ + Responsible for communicating with an iShare Satellite/Scheme Owner. + """ + + def get_trusted_list(self) -> Any: # pragma: no cover + """ + https://dev.ishare.eu/scheme-owner/trusted-list.html + :return: + """ + return self._get_request( + endpoint_path="/trusted_list", decode_key="trusted_list_token" + ) + + def get_parties(self, params: dict[str, Any]) -> Any: # pragma: no cover + """ + https://dev.ishare.eu/scheme-owner/parties.html + :return: + """ + return self._get_request( + endpoint_path="/parties", decode_key="parties_token", params=params + ) + + def get_versions(self, use_token: bool) -> Any: # pragma: no cover + """ + https://dev.ishare.eu/scheme-owner/versions.html + + :param use_token: If False the token will not be used and the unauthenticated + response is returned as per specification. + :return: + """ + return self._get_request( + endpoint_path="/versions", use_token=use_token, decode_key="versions_token" + ) + + def verify_party(self, party_eori: str, certified_only: bool = True) -> Any: + """Implements the verification logic for a participant's status.""" + result = self.get_parties( + params=dict( + eori=party_eori, certified_only=certified_only, active_only=True + ) + ) + + parties_info = result.get("parties_info") + parties_count = parties_info.get("count", 0) + + if not parties_info or parties_count != 1: + raise ISharePartyStatusInvalid( + f"Invalid result, received {parties_count} parties." + ) + + parties_data = parties_info.get("data", {}) + only_party = parties_data[0] + + if only_party["party_id"] != party_eori: + raise ISharePartyStatusInvalid("Invalid party returned.") + + adherence = only_party.get("adherence", {}) + if not adherence or adherence["status"] != "Active": + raise ISharePartyStatusInvalid("Inactive party returned.") + + return result + + def verify_ca_certificate(self, json_web_token: str) -> None: + """ + Verify whether the given certificate is signed by an CA trusted by iShare. + :param json_web_token: + :return: + """ + x5c_finger_print = get_b64_x5c_fingerprints(json_web_token=json_web_token)[-1] + trusted_ca_list = self.get_trusted_list() + certificates = trusted_ca_list.get("trusted_list", []) + + for cert in certificates: + if ( + cert["subject"] == x5c_finger_print["subject"] + and cert["certificate_fingerprint"] == x5c_finger_print["finger_print"] + ): + return + else: + raise IShareInvalidCertificateIssuer("No trusted CA found.") + + def verify_json_web_token( + self, + client_id: str, + client_assertion: str, + audience: str, + grant_type: Literal["client_credentials"], + scope: Literal["iSHARE"], + client_assertion_type: Literal[ + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" + ], + ) -> None: + """ + :param client_id: iShare identifier of the Service Consumer. + :param client_assertion: the json web token as per iShare specification. + :param audience: ishare participant eori of the target service + :param grant_type: + :param scope: + :param client_assertion_type: + :raises IShareAuthenticationException: and all it's inheriting classes + :return: + """ + # TODO: Method flow needs to check the revocation list + # TODO: Method flow cert also needs a check in the verify_party (undocumented?) + validate_client_assertion( + client_id=client_id, + client_assertion=client_assertion, + audience=audience, + grant_type=grant_type, + scope=scope, + client_assertion_type=client_assertion_type, + ) + self.verify_ca_certificate(json_web_token=client_assertion) + self.verify_party(party_eori=client_id, certified_only=True) + + +class IShareAuthorizationRegistryClient(CommonBaseClient): + """Responsible for communicating with an iShare Authorization Registry.""" + + def request_delegation(self, use_token: bool) -> Any: # pragma: no cover + """ + https://dev.ishare.eu/delegation/endpoint.html + + :param use_token: If False the token will not be used and the unauthenticated + response is returned as per specification. + :return: + """ + return self._get_request( + endpoint_path="/delegation", + use_token=use_token, + decode_key="delegation_token", + ) diff --git a/src/python_ishare/exceptions.py b/src/python_ishare/exceptions.py new file mode 100644 index 0000000..faabe31 --- /dev/null +++ b/src/python_ishare/exceptions.py @@ -0,0 +1,83 @@ +class IShareAuthenticationException(Exception): + """ + Base class exception to be able to catch *all* other types of sub exception raised + by this package. + """ + + pass + + +class IShareInvalidGrantType(IShareAuthenticationException): + pass + + +class IShareInvalidScope(IShareAuthenticationException): + pass + + +class IShareInvalidAudience(IShareAuthenticationException): + pass + + +class IShareInvalidClientId(IShareAuthenticationException): + pass + + +class IShareInvalidClientAssertionType(IShareAuthenticationException): + pass + + +class IShareInvalidToken(IShareAuthenticationException): + pass + + +class IShareInvalidTokenAlgorithm(IShareInvalidToken): + pass + + +class IShareInvalidTokenType(IShareInvalidToken): + pass + + +class IShareInvalidTokenX5C(IShareInvalidToken): + pass + + +class IShareInvalidTokenIssuerOrSubscriber(IShareInvalidToken): + pass + + +class IShareInvalidTokenJTI(IShareInvalidToken): + pass + + +class IShareTokenExpirationInvalid(IShareInvalidToken): + pass + + +class IShareTokenExpired(IShareInvalidToken): + pass + + +class IShareTokenNotValidYet(IShareInvalidToken): + pass + + +class IShareInvalidTokenSigner(IShareInvalidToken): + pass + + +class IShareInvalidCertificate(IShareAuthenticationException): + pass + + +class IShareCertificateExpired(IShareInvalidCertificate): + pass + + +class IShareInvalidCertificateIssuer(IShareInvalidCertificate): + pass + + +class ISharePartyStatusInvalid(IShareAuthenticationException): + pass diff --git a/src/python_ishare/verification.py b/src/python_ishare/verification.py new file mode 100644 index 0000000..d30947d --- /dev/null +++ b/src/python_ishare/verification.py @@ -0,0 +1,109 @@ +import base64 +import re +from typing import Any, Literal + +import jwt +from cryptography.x509 import load_der_x509_certificate +from jwt import ExpiredSignatureError, ImmatureSignatureError, InvalidAudienceError + +from python_ishare import exceptions as ishare_exc +from python_ishare.authentication import decode_jwt, x5c_b64_to_certificate + +ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" + + +def is_valid_eori_number(eori_number: str, strict_eori: bool) -> bool: + """ + Returns true if the eori number is valid for use within iShare. + + The 'strict_eori' can be set to False if your connecting satellite is not enforcing + valid eori numbers. + + :param eori_number: + :param strict_eori: Whether to check eori number format. + :return: + """ + if eori_number is None or eori_number == "": + return False + + if not strict_eori: # Prevent eori number validation. + return True + + pattern = r"EU.EORI.[A-Z]{2}[0-9]{1,15}" + matches = re.fullmatch(pattern, eori_number) + return matches is not None + + +def validate_client_assertion( + client_id: str, + client_assertion: str, + audience: str, + grant_type: Literal["client_credentials"], + scope: Literal["iSHARE"], + client_assertion_type: Literal[ + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" + ], + strict_eori: bool = False, +) -> dict[str, Any]: + """ + https://dev.ishare.eu/m2m/authentication.html + + :param client_id: iShare identifier of the Service Consumer. + :param client_assertion: the json web token as per iShare specification. + :param audience: the iShare identifier of the target service. + :param grant_type: + :param scope: + :param client_assertion_type: + :param strict_eori: Whether to validate eori numbers per specification. + :raises IShareAuthenticationException: and all it's inheriting classes + :return: The decrypted payload + """ + if grant_type != "client_credentials": + raise ishare_exc.IShareInvalidGrantType() + + if scope != "iSHARE": + raise ishare_exc.IShareInvalidScope() + + if client_assertion_type != ASSERTION_TYPE: + raise ishare_exc.IShareInvalidClientAssertionType() + + headers = jwt.get_unverified_header(client_assertion) + + if headers["alg"] != "RS256": + raise ishare_exc.IShareInvalidTokenAlgorithm() + + if headers["typ"] != "JWT": + raise ishare_exc.IShareInvalidTokenType() + + if headers["x5c"] is None or len(headers["x5c"]) < 1: + raise ishare_exc.IShareInvalidCertificate() + + try: + jwt_payload: dict[str, Any] = decode_jwt( + json_web_token=client_assertion, + public_x509_cert=list(x5c_b64_to_certificate(headers["x5c"])), + audience=audience, + ) + except ExpiredSignatureError: + raise ishare_exc.IShareTokenExpired() + except ImmatureSignatureError: + raise ishare_exc.IShareTokenNotValidYet() + except InvalidAudienceError: + raise ishare_exc.IShareInvalidAudience() + + if not is_valid_eori_number(eori_number=client_id, strict_eori=strict_eori): + raise ishare_exc.IShareInvalidClientId() + + if client_id != jwt_payload.get("sub") or client_id != jwt_payload.get("iss"): + raise ishare_exc.IShareInvalidTokenIssuerOrSubscriber() + + if not jwt_payload.get("jti"): + raise ishare_exc.IShareInvalidTokenJTI() + + expires: int = jwt_payload.get("exp", 0) + issued: int = jwt_payload.get("iat", 0) + + if expires - issued != 30: + raise ishare_exc.IShareTokenExpirationInvalid() + + return jwt_payload diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..ef09980 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,55 @@ +import base64 +from pathlib import Path + +import pytest +from cryptography.hazmat.primitives.serialization import Encoding +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey +from cryptography.hazmat.primitives.serialization import load_pem_private_key +from cryptography.x509 import Certificate, load_pem_x509_certificates + + +@pytest.fixture(scope="session") +def test_data_directory() -> Path: + """ + Fixture to retrieve a file for testing purposes from the /tests/data directory. + :return: + """ + return Path(__file__).parent / "data" + + +@pytest.fixture(scope="session") +def read_file_bytes(): + """ + Read a given file from a given file_path as bytes .open("rb"). + :return: + """ + + def _read_file_bytes(file_path: Path) -> bytes: + with file_path.open("rb") as file: + return file.read() + + return _read_file_bytes + + +@pytest.fixture(scope="session") +def get_key_and_certs(read_file_bytes): + """ + Load the RSA key and certificate file from the given paths, add certificate base64. + :return: + """ + + def _get_key_and_certs( + key_file: Path, cert_file: Path + ) -> tuple[RSAPrivateKey, list[Certificate], list[str]]: + key = load_pem_private_key(read_file_bytes(file_path=key_file), password=None) + x509_bytes = read_file_bytes(file_path=cert_file) + x509 = load_pem_x509_certificates(x509_bytes) + + x509_b64 = [] + for cert in x509: + x509_b64.append( + str(base64.b64encode(cert.public_bytes(encoding=Encoding.DER)), encoding="utf8")) + + return key, x509, x509_b64 + + return _get_key_and_certs diff --git a/tests/data/README.md b/tests/data/README.md new file mode 100644 index 0000000..c3e79eb --- /dev/null +++ b/tests/data/README.md @@ -0,0 +1,3 @@ +# WARNING + +NEVER use these certificates for anything else other than these integration tests. \ No newline at end of file diff --git a/tests/data/participant_consumer_one_certificate.pem b/tests/data/participant_consumer_one_certificate.pem new file mode 100644 index 0000000..15b2b38 --- /dev/null +++ b/tests/data/participant_consumer_one_certificate.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFhDCCA2ygAwIBAgIUTFp7/LlyaMe/tNVJ858Zw9NORmYwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCTkwxFTATBgNVBAgMDFp1aWQtSG9sbGFuZDESMBAGA1UE +BwwJUm90dGVyZGFtMSEwHwYDVQQKDBhwYXJ0aWNpcGFudF9jb25zdW1lcl9vbmUx +EjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzEyMDEwOTQ5NDhaFw0yMzEyMTEwOTQ5 +NDhaMG8xCzAJBgNVBAYTAk5MMRUwEwYDVQQIDAxadWlkLUhvbGxhbmQxEjAQBgNV +BAcMCVJvdHRlcmRhbTEhMB8GA1UECgwYcGFydGljaXBhbnRfY29uc3VtZXJfb25l +MRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQDAnDdnE1KAUThN1WY3GJnExdEB6P9UqVb2kR3Oc5u4/DiCURu8JAndfv3D +Ln5HHJSYaeWVib3hIg6NxJ5+Uab7tqH35V/MIUgqwiM518MzmAs1qWXEjOTlG+r2 +A8nmJKinVotO3v8ZXnK6FPFUuPGoKlrAKNjk7dbkfSNoS4gura7cwG3GKsbgtVg/ +l3TRWC6hhde1o9neK9QTFm1AuGCysH94IEDPWhoKo1Jm5epkwWAAGArwDvDRiMrw +QheIkO6XZt0f4uC/YP06DZfCAZADjNWHtPbLfuZEKLLF4Okq6v61wZpGjg1imK0b +vDnpOXnj5TfN/GlI/A77aKas6CvKW7BpRYnkKyLeCY0t+8EK9tAffHyL7DM6CYnx +QLRbMyaqWYoQEzrODjoZIDDYYs8Ij1gfrSr378D29t+hUP+nFRHWEns4F+DAfGrP +GRCFmWzABh1+fs4xd+CnS1sZOQx6xHmSrCUYm0NTsZSGuUbjmfxc9DeV74Ia3WMb +gIee9Fib6DKjDUYIX+ASfWBbwdZN5kWrexBOkEXk+489yhoSnc/E5VvKFlGIT2ae +415XSNL4cYbXXQTKeZewPNn67aNcJNK1g0BmNzElYFPf61Q/fyEP5ej24o4dsqXH +nMsMzUBKvTWngtjA4OeZP8bSbyrpBK7Sgr1FG0lYD/XrGZmcYwIDAQABoxgwFjAU +BgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIBADzR7pbXP8bZ +UbWxbAwmMH0cWfBJTpeyPCleBZhkLlG1HaLVqwmQBX/ta8OQm2GRTFMQUgmzzPMd +flRF0MbJrqM7PwD4xKqSSYPg6JDwoofc6XEHUKcbYQMczKX2VLSlv2XduehVqh2V +fldUGUF1qSBlHUfIaexoA+EXN3LLRApV4uB/TVBRTJFOub/raGxznOyHkG4L011M +HmuXU/eBo7B5jPobbZy1AqhfnUqu/hbwwOTx/oe0lVyFTtDHItDd11IXNYzUfPI3 +gMQidvjb76JJsYLFULU2TNRVU1sSovd+5pBJ1Cc/F94JnrqNqoc5sQ28imuWcOfw +2JgrR5hQGOYFjoZAsys50XH7S9E1G/pDK/upLa44CbRIpPkwTwaV4Z4Z5C1/44K6 +a+kjSAf0vs2prmlSx/N6yXaSD7yDHEbeukQ9/eMQ/eBDmXBUBbHDz1p/yXhZ/yLL ++SuGAUTuXq6vnK5Ro6pvHyNxcEG+9AjT8XWrNdgjIzepwhvH6dnZ2n8EAhF3YSTD +6030sKQt7A9KQXBQChynuRtiMuTWr02bKEoz/8jk4NfwlMWTaVP8QqjLXKkDnN7v +BLiC1Rt/KNegXoJGF6S+1YumoER0hzD5+GM2chzbo57rEkBe538farykkcl2p0qL +ITGtqiLL6/h4JV4Q8lwu0WOjQHn7JbIM +-----END CERTIFICATE----- diff --git a/tests/data/participant_consumer_one_rsa_key.pem b/tests/data/participant_consumer_one_rsa_key.pem new file mode 100644 index 0000000..4bc5fb7 --- /dev/null +++ b/tests/data/participant_consumer_one_rsa_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/data/participant_registry_certificate.pem b/tests/data/participant_registry_certificate.pem new file mode 100644 index 0000000..816b187 --- /dev/null +++ b/tests/data/participant_registry_certificate.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFfDCCA2SgAwIBAgIUZ2agNG71YYN57MTeT00Bb7LqfMowDQYJKoZIhvcNAQEL +BQAwazELMAkGA1UEBhMCTkwxFTATBgNVBAgMDFp1aWQtSG9sbGFuZDESMBAGA1UE +BwwJUm90dGVyZGFtMR0wGwYDVQQKDBRwYXJ0aWNpcGFudF9yZWdpc3RyeTESMBAG +A1UEAwwJbG9jYWxob3N0MB4XDTIzMTIwMTA5NDk1NFoXDTIzMTIxMTA5NDk1NFow +azELMAkGA1UEBhMCTkwxFTATBgNVBAgMDFp1aWQtSG9sbGFuZDESMBAGA1UEBwwJ +Um90dGVyZGFtMR0wGwYDVQQKDBRwYXJ0aWNpcGFudF9yZWdpc3RyeTESMBAGA1UE +AwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyJ3Q +47cVw/b5a16LNW2K/yWdBzbb2Kv7o9Xa7WrtmvpVd2dfHNFMAQFj+gkuCP0SBHQe +7vlVXprxaHdNK2oxqCXT7KFuCdCpKkkMAXImjVjexiZLbLG3z9i5SLn+pDr2B3tB +94g3smHxfVidpUAwIqLO3WGQacnMjZn82laC7iM1W5ED2XqMSHAl4Y0Kp8JADNAo +Ba4Eo8V6Ue3OZFrfN9DhVfLv61y4YxsJYxRVfKRw1j9nAdrIwrrruYSSCvbYnnL5 +KOGAsgAWrFxxWOekytu9y8C215w50SUGCewaM5d7RhjhxYgcLBKCLr5JS5F3NCU5 +jfsuCqr3MiO0KjQzrQfzh8caRHV5pbwunOqnQLk+W+HDO7SZl3+RlcBNmj4BQlUZ +3ScCeJ0JmDifM2Og4HZr6X1W0nz1JpgnRuJnv0kd4O+OFyDLsQ43LfJveR/P5vVj +ZnwglEMnB95GU74Y7zDivR4HpSvIajDefepx77fJlunHSBTn2r8VaE4wr42e7h8a +1ZOnw5sgf61aAcERCRyIhfvLsfaRx5zHGhHIJBdaWsmG6I+4BrFW28z73AZEkgFu +QUkULPGMC9qCLb3ZK4xuvqdsB9oMnN2YhnC+FKvJ9guOp6GBlavgjGhy99sFQFVw +vkMfjPUKzgykBotxblyh1tz/Tc17rnzs/iqj6tsCAwEAAaMYMBYwFAYDVR0RBA0w +C4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4ICAQBgP/HX92V3k3lPpA26s0fd +hEDTjmnjw+uWWHzBlVCv/ZuIbeLyZqReVu/MHtAr0MfpTKcDUHsSYQFmrX7GtSH2 +ZVQ/prKXOaMjLSaAYf1EHHndQQPhK2r56p2u5iHjHlZAtLELrfJlAoF4edvoE9mG +kVtri5AhPi7n4XTPKXsmjTqLXON2YdlgTv9KlQezS6l2VaY2pUst5DFxueXXLtmZ +zn8re+354RXjorkCj7CXCG8Upy1GX8AreTojsP71kuwmjJVV0ugiZGx2Nh9VyaNh +h4bOpyfUq4jxITg8dshbpauL6ckQ6NmM1UEbHQSz1SykIWTCEo4ydiDn0n4t9u+w +PjQv7uxtYALTmlr/kEUjzgrQG0zZedvW0XmkzeneRndnHkPINDWJYiL0fZ6B9K55 +JhzJfBGzjC52o4s8xOPfgLFFZlJYCqCMRn9jaQFzBqhaGpQL3mhVCCap9a4xRN43 +eoP5/FWsEUig3JiRCNOME7VGfVGJfxwdN7N1CCH7bDPPjaoXM3lJ7McEAzI5/FVY +lBwIj+i6uzeIZmT8/RpnARX36I+sH8tKx6rgk/nwrmZPk5GVHyBaY2BwSCX0LOlC +L16kUYBJgDtiVCU4DsUmFPHlFOlxLPlMHIu78s80qwAlE2w8DrkuWgaznQUCpC4j +o98DROk/gCePzx4A3JQVxw== +-----END CERTIFICATE----- diff --git a/tests/data/participant_registry_rsa_key.pem b/tests/data/participant_registry_rsa_key.pem new file mode 100644 index 0000000..2452aae --- /dev/null +++ b/tests/data/participant_registry_rsa_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/data/participant_satellite_certificate.pem b/tests/data/participant_satellite_certificate.pem new file mode 100644 index 0000000..9c226f0 --- /dev/null +++ b/tests/data/participant_satellite_certificate.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFfjCCA2agAwIBAgIUMXeFS+qeoNza3RTgO39r7BWsXj0wDQYJKoZIhvcNAQEL +BQAwbDELMAkGA1UEBhMCTkwxFTATBgNVBAgMDFp1aWQtSG9sbGFuZDESMBAGA1UE +BwwJUm90dGVyZGFtMR4wHAYDVQQKDBVwYXJ0aWNpcGFudF9zYXRlbGxpdGUxEjAQ +BgNVBAMMCWxvY2FsaG9zdDAeFw0yMzEyMDEwOTUwMDZaFw0yMzEyMTEwOTUwMDZa +MGwxCzAJBgNVBAYTAk5MMRUwEwYDVQQIDAxadWlkLUhvbGxhbmQxEjAQBgNVBAcM +CVJvdHRlcmRhbTEeMBwGA1UECgwVcGFydGljaXBhbnRfc2F0ZWxsaXRlMRIwEAYD +VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCq +4aYNAxTW3cPTRF84P0zGnDesxWSvQc/gkBirpnTDwWvadhYEc5H88c6WAaLiitM7 +Zl80KCwRrRHL6pnF0y0U2t5SFxt++FsX2F9oBUn+4aPQi636NseiTL5y7rvvhAXs +Fxxb6HOIddVGYo4INRA2jyLEjlU/gWSe7iDVqbdZRzCjWHWzKijjhSLyL+SJmouw +UZASfNQgcBIYOh2rdX2Q9qc525qnKJBQp7gCFZ0XYmKrwwUWRB78Y6kNsVKJ9M2F +PESGNR+BMas4Z4BPwnHOC9kqtkeHgkDyp4MX/IhQXJFVhG/ir515wjC3bnOukZoH +sYTgL8oePltvlwM2TUoznAXrRC1iIjPL3XNVNq8UHEE7GUqd+jjqxSGmBdm4sBRz +FVkUGt2xTKbYOCf1R2YDjr5hMCThTyxpDQz0qQHhji2KCidhku/je9I4AuzWfYM1 +hEb7+k+D8JbMbDip50npVRLtbH5vQUhsJV3AkirKNsWEQSDunNwIseT+VO8tjP+H +zwfj4e+k8jiw0RW0H9JcXE0I3q3WZjWYxo5OIFl9ze6lS4QILd5tVa+Gw0R/b9dC +P8E9eNg4CQ7W6vOhOecrIiPLkTgDd4/kDRDgVt0c1oBBEpCSjKxibQPof8Aqojjg +YSK1YsC98A0/I9yLJhL+0d7VB8tDLJUiC4CP8NttzwIDAQABoxgwFjAUBgNVHREE +DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIBAIBFJV9HSwgYMbsyUJe8 +tbv/rsrTZINasVufezv4xxTug99Xa/ScNjXxKXjPLBLgbY/GD/9wqJ+buTwEuu/u +Z4PHoum5ynrR8vdH4YxD2ZhtXcBaZT4MXssgBWfvBgQh+hLHvC3H2arfHKeVeNui +qSqPgkn18tliyBE8kzjSGFGY6ZoIHDbGcD+GvXWpRIty7zFlGSeKQW5aBgoQKkbU +OypUPu0BSRumOeDScW682LzLHS2EuWEo0FltM00cOx1uwctcBo1KCsLm5psSyR/j +M9t2s+XHQ9hgvneYetZsdKo+MPnb7B6yJS7WGasaskYYU7zjs76Vfa5WX/Uw1CoJ +hAau8KxMPUdG3X4Jo7A1aREH/M2NYDJOcflMZYiXyoxRG3fJoe4gi+jybjLXUVNz +GcVU/Qe1wjQkhvhE0N+OohUjxMihoVp/diDT4AwM7I2wIYGU55JiGNuui41rEaC/ +FsHSZhGMnhMsav7MHg+DhIMmg2FnmN8KekAed7W1XHUt2HtrbzgnpAqy16KzJnp4 +9CDyCKsIFtFgbajmEap4U6EplDdYdDyw1dKKyqwAluMjb6LIOehGUVDKZ0V8KGgI +9Lxr+C1ih2cRwhM5uCvS5I5ag8evS65hboVWWqOpZwZoEfWu36f8R95VCbEhAzI9 +iz5Gu4qOo20Lvm/1boAWEp94 +-----END CERTIFICATE----- diff --git a/tests/data/participant_satellite_rsa_key.pem b/tests/data/participant_satellite_rsa_key.pem new file mode 100644 index 0000000..0d6dbe4 --- /dev/null +++ b/tests/data/participant_satellite_rsa_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py new file mode 100644 index 0000000..16770e6 --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,150 @@ +import pytest +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey +from cryptography.x509 import ( + Certificate, + load_pem_x509_certificate, + load_der_x509_certificate +) + +from python_ishare.authentication import create_jwt +from python_ishare.clients import CommonBaseClient, IShareSatelliteClient + +PB_CLIENT_EORI = "EU.EORI.PBADAPTER" + + +@pytest.fixture(scope="session") +def integration_data_directory(test_data_directory): + return test_data_directory / "integration" + + +@pytest.fixture(scope="session") +def private_key_bytes(integration_data_directory) -> bytes: + """ + from project root: + openssl genrsa -des3 -out ./tests/data/jwt-key.pem 4048 + """ + path = integration_data_directory / "pb_adapter_private.key" + + with path.open(mode="rb") as f: + return f.read() + + +@pytest.fixture(scope="session") +def rsa_private_key(private_key_bytes) -> RSAPrivateKey: + """ + from project root: + openssl genrsa -des3 -out ./tests/data/jwt-key.pem 4048 + """ + return serialization.load_pem_private_key(private_key_bytes, password=b"pw123") + + +@pytest.fixture(scope="session") +def rsa_private_key_password() -> bytes: + return b"pw123" + + +@pytest.fixture(scope="session") +def rsa_private_key_unencrypted( + rsa_private_key: bytes, + rsa_private_key_password, +) -> RSAPrivateKey: + return serialization.load_pem_private_key( + data=rsa_private_key, + password=rsa_private_key_password, + backend=default_backend(), + ) + + +@pytest.fixture(scope="session") +def pb_public_x509_chain( + read_file_bytes, + integration_data_directory +) -> list[Certificate]: + return [ + load_der_x509_certificate( + read_file_bytes(integration_data_directory / "pb_public_cert_1.der"), + ), + load_der_x509_certificate( + read_file_bytes(integration_data_directory / "pb_public_cert_2.der"), + ), + load_der_x509_certificate( + read_file_bytes(integration_data_directory / "pb_public_cert_3.der"), + ) + ] + + +@pytest.fixture(scope="session") +def satellite_public_certificate( + integration_data_directory, +) -> Certificate: + with (integration_data_directory / "certificate.pem").open("rb") as file: + return load_pem_x509_certificate(file.read()) + + +@pytest.fixture() +def pb_jwt_for_satellite( + rsa_private_key, pb_public_x509_chain, rsa_private_key_password +): + _payload = { + "iss": PB_CLIENT_EORI, + "sub": PB_CLIENT_EORI, + "aud": "EU.EORI.NLDILSATTEST1", + } + + return create_jwt( + payload=_payload, + private_key=rsa_private_key, + x5c_certificate_chain=pb_public_x509_chain, + ) + + +@pytest.fixture() +def pb_jwt_invalid(integration_data_directory, get_key_and_certs): + rsa_key, pb_public_x509_chain, _ = get_key_and_certs( + key_file=integration_data_directory / "participant_invalid_cert_rsa_key.pem", + cert_file=integration_data_directory + / "participant_invalid_cert_certificate.pem", + ) + + _payload = { + "iss": "EU.EORI.WEIRD", + "sub": "EU.EORI.WEIRD", + "aud": "EU.EORI.NLDILSATTEST1", + } + + return create_jwt( + payload=_payload, + private_key=rsa_key, + x5c_certificate_chain=pb_public_x509_chain, + ) + + +@pytest.fixture() +def integration_client_settings(pb_jwt_for_satellite, satellite_public_certificate): + return { + "target_domain": "https://dilsat1-mw.pg.bdinetwork.org", + "target_public_key": [satellite_public_certificate], + "client_eori": PB_CLIENT_EORI, + "json_web_token": pb_jwt_for_satellite, + } + + +@pytest.fixture() +def integrated_common_auth_client(integration_client_settings) -> CommonBaseClient: + """ + CommonBaseClient with integration configuration. + + TODO: In time, when we have a working IShareAuthorizationRegistryClient + (e.g. a working Registry) this should be replaced with that client. + """ + return CommonBaseClient(**integration_client_settings) + + +@pytest.fixture() +def integrated_ishare_satellite_client( + integration_client_settings, +) -> IShareSatelliteClient: + """IShareSatelliteClient with integration configuration.""" + return IShareSatelliteClient(**integration_client_settings) diff --git a/tests/integration/test_common.py b/tests/integration/test_common.py new file mode 100644 index 0000000..b1656e5 --- /dev/null +++ b/tests/integration/test_common.py @@ -0,0 +1,43 @@ +import pytest + + +@pytest.mark.parametrize( + "client", + [ + "integrated_common_auth_client", + "integrated_ishare_satellite_client", + ], +) +def test_access_token(client, request): + test_client = request.getfixturevalue(client) + token_response = test_client.request_access_token() + assert "access_token" in token_response + + +@pytest.mark.parametrize( + "client", + [ + "integrated_common_auth_client", + "integrated_ishare_satellite_client", + ], +) +def test_authed_capabilities(client, request): + test_client = request.getfixturevalue(client) + capabilities = test_client.get_capabilities(use_token=True) + assert "capabilities_info" in capabilities.keys() + + +@pytest.mark.parametrize( + "client", + [ + "integrated_common_auth_client", + "integrated_ishare_satellite_client", + ], +) +def test_no_auth_capabilities(client, request): + test_client = request.getfixturevalue(client) + capabilities = test_client.get_capabilities(use_token=False) + assert "capabilities_info" in capabilities.keys() + assert "sub" in capabilities.keys() + # No access token means no audience token verification. + assert "aud" not in capabilities.keys() diff --git a/tests/integration/test_satellite.py b/tests/integration/test_satellite.py new file mode 100644 index 0000000..67b6702 --- /dev/null +++ b/tests/integration/test_satellite.py @@ -0,0 +1,74 @@ +import pytest + +from python_ishare.exceptions import ( + IShareInvalidCertificateIssuer, + ISharePartyStatusInvalid, +) + + +def test_trusted_list(integrated_ishare_satellite_client): + """Test connection with the satellite, using a satellite specific endpoint.""" + trusted_list = integrated_ishare_satellite_client.get_trusted_list() + + assert "trusted_list" in trusted_list.keys() + assert "aud" in trusted_list.keys() + + +def test_verify_party(integrated_ishare_satellite_client, integration_client_settings): + """Test whether we can verify our own ishare party status.""" + result = integrated_ishare_satellite_client.verify_party( + party_eori=integration_client_settings["client_eori"] + ) + + assert "parties_info" in result + assert len(result["parties_info"]["data"]) == 1 + assert result["parties_info"]["data"][0]["adherence"]["status"] == "Active" + + +def test_verify_invalid_party( + integrated_ishare_satellite_client, integration_client_settings +): + """Test whether a pre-existing inactive party fails.""" + eori_existing_invalid_party = "EU.EORI.TESTENTPARTY127" + + with pytest.raises(ISharePartyStatusInvalid): + integrated_ishare_satellite_client.verify_party( + party_eori=eori_existing_invalid_party + ) + + +def test_verify_certificate(integrated_ishare_satellite_client, pb_jwt_for_satellite): + """Test whether we can verify our own certificate using the satellite.""" + try: + integrated_ishare_satellite_client.verify_ca_certificate( + json_web_token=pb_jwt_for_satellite + ) + except IShareInvalidCertificateIssuer: + pytest.fail( + "This should be a valid CA check. If this fails the certificate is not " + "signed by a correct CA *or* the CA was removed from the trusted_list." + ) + + +def test_verify_invalid_certificate(integrated_ishare_satellite_client, pb_jwt_invalid): + """Test whether we can verify our own certificate using the satellite.""" + with pytest.raises(IShareInvalidCertificateIssuer): + integrated_ishare_satellite_client.verify_ca_certificate( + json_web_token=pb_jwt_invalid + ) + + +def test_verify_json_web_token( + integrated_ishare_satellite_client, integration_client_settings +): + """ + Test whether we can fully verify our own json web token. + """ + integrated_ishare_satellite_client.verify_json_web_token( + client_id=integration_client_settings["client_eori"], + client_assertion=integration_client_settings["json_web_token"], + audience="EU.EORI.NLDILSATTEST1", + grant_type="client_credentials", + scope="iSHARE", + client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + ) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py new file mode 100644 index 0000000..4feb24d --- /dev/null +++ b/tests/unit/conftest.py @@ -0,0 +1,146 @@ +from typing import Any + +import pytest +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey +from cryptography.x509 import Certificate + +from python_ishare.authentication import create_jwt +from python_ishare.clients import CommonBaseClient, IShareSatelliteClient + + +@pytest.fixture(scope="session") +def registry_key_and_certs( + test_data_directory, get_key_and_certs +) -> tuple[RSAPrivateKey, list[Certificate], list[str]]: + """ + testing certificates + + python utils/generate_key_and_certificate.py './tests/data' participant_registry + + :return: + """ + return get_key_and_certs( + key_file=test_data_directory / "participant_registry_rsa_key.pem", + cert_file=test_data_directory / "participant_registry_certificate.pem", + ) + + +@pytest.fixture(scope="session") +def satellite_key_and_certs( + test_data_directory, get_key_and_certs +) -> tuple[RSAPrivateKey, list[Certificate], list[str]]: + """ + testing certificates + + python utils/generate_key_and_certificate.py './tests/data' participant_satellite + + :return: + """ + return get_key_and_certs( + key_file=test_data_directory / "participant_satellite_rsa_key.pem", + cert_file=test_data_directory / "participant_satellite_certificate.pem", + ) + + +@pytest.fixture(scope="session") +def consumer_one_key_and_certs( + test_data_directory, get_key_and_certs +) -> tuple[RSAPrivateKey, list[Certificate], list[str]]: + """ + testing certificates + + python utils/generate_key_and_certificate.py './tests/data' participant_consumer_one + + :return: + """ + return get_key_and_certs( + key_file=test_data_directory / "participant_consumer_one_rsa_key.pem", + cert_file=test_data_directory / "participant_consumer_one_certificate.pem", + ) + + +@pytest.fixture(scope="module") +def test_client_arguments(satellite_key_and_certs): + """Make sure clients don't execute web requests.""" + _, x509_cert, _ = satellite_key_and_certs + + return { + "target_domain": "https://localhost", + "target_public_key": x509_cert, + "client_eori": "NL123456789", + "json_web_token": "fake", + } + + +@pytest.fixture() +def common_auth_client(test_client_arguments) -> CommonBaseClient: + """CommonBaseClient with test configuration.""" + return CommonBaseClient(**test_client_arguments) + + +@pytest.fixture() +def satellite_client(test_client_arguments) -> IShareSatelliteClient: + """IShareSatelliteClient with test configuration.""" + return IShareSatelliteClient(**test_client_arguments) + + +@pytest.fixture(scope="session") +def trusted_fingerprints( + satellite_key_and_certs, consumer_one_key_and_certs, registry_key_and_certs +) -> list[dict[str, Any]]: + """ + Returns all the fingerprints in the expected format as returned by a Satellite. + + The content of the "trusted_list" in the decoded payload. + + https://dev.ishare.eu/scheme-owner/trusted-list.html + """ + _, satellite_public_key, _ = satellite_key_and_certs + _, consumer_public_key, _ = consumer_one_key_and_certs + _, registry_public_key, _ = registry_key_and_certs + _algorithm = hashes.SHA256() + + return [ + { + "subject": satellite_public_key[0].subject.rfc4514_string(), + "certificate_fingerprint": satellite_public_key[0].fingerprint( + algorithm=_algorithm + ), + "validity": "valid", + "status": "granted", + }, + { + "subject": consumer_public_key[0].subject.rfc4514_string(), + "certificate_fingerprint": consumer_public_key[0].fingerprint( + algorithm=_algorithm + ), + "validity": "valid", + "status": "granted", + }, + { + "subject": registry_public_key[0].subject.rfc4514_string(), + "certificate_fingerprint": registry_public_key[0].fingerprint( + algorithm=_algorithm + ), + "validity": "valid", + "status": "granted", + }, + ] + + +@pytest.fixture() +def create_jwt_response(satellite_key_and_certs, test_client_arguments): + rsa_key, x509_certs, _ = satellite_key_and_certs + + def _create(payload, iss="NL123", sub="NL123"): + audience: str = test_client_arguments["client_eori"] + + _payload = {"iss": iss, "sub": sub, "aud": audience, **payload} + return create_jwt( + payload=_payload, + private_key=rsa_key, + x5c_certificate_chain=x509_certs, + ) + + return _create diff --git a/tests/unit/test_authentication.py b/tests/unit/test_authentication.py new file mode 100644 index 0000000..ee49dd9 --- /dev/null +++ b/tests/unit/test_authentication.py @@ -0,0 +1,83 @@ +import pytest + +from python_ishare.authentication import ( + create_jwt, + decode_jwt, + get_b64_x5c_fingerprints, +) + + +@pytest.mark.parametrize( + "key_and_certs", + [ + "registry_key_and_certs", + "satellite_key_and_certs", + "consumer_one_key_and_certs", + ], +) +def test_create_jwt_with_rsa_key(key_and_certs, request): + rsa_key, x509_cert, _ = request.getfixturevalue(key_and_certs) + + my_tracker = "sabertooth" + audience = "louis" + + token = create_jwt( + payload={ + "iss": "something", + "sub": "something", + "aud": audience, + "jti": my_tracker, + }, + private_key=rsa_key, + x5c_certificate_chain=x509_cert, + ) + + assert "sabertooth" not in token + assert "something" not in token + + decoded = decode_jwt( + json_web_token=token, + audience=audience, + public_x509_cert=x509_cert, + ) + + assert decoded["aud"] == audience + assert decoded["jti"] == my_tracker + + +@pytest.mark.parametrize( + "key_and_certs,subject", + [ + ( + "registry_key_and_certs", + "CN=localhost,O=participant_registry,L=Rotterdam,ST=Zuid-Holland,C=NL", + ), + ( + "satellite_key_and_certs", + "CN=localhost,O=participant_satellite,L=Rotterdam,ST=Zuid-Holland,C=NL", + ), + ( + "consumer_one_key_and_certs", + "CN=localhost,O=participant_consumer_one,L=Rotterdam,ST=Zuid-Holland,C=NL", + ), + ], +) +def test_get_x5c_fingerprints(key_and_certs, subject, request): + rsa_key, x509_cert, _ = request.getfixturevalue(key_and_certs) + + audience = "louis" + + token = create_jwt( + payload={ + "iss": "something", + "sub": "something", + "aud": audience, + "jti": "123", + }, + private_key=rsa_key, + x5c_certificate_chain=x509_cert, + ) + + prints = get_b64_x5c_fingerprints(json_web_token=token) + # The following subject is set when creating a (self-signed) certificate. + assert prints[0]["subject"] == subject diff --git a/tests/unit/test_clients.py b/tests/unit/test_clients.py new file mode 100644 index 0000000..619319c --- /dev/null +++ b/tests/unit/test_clients.py @@ -0,0 +1,157 @@ +import pytest +import responses +from requests import HTTPError +from responses import matchers + +from python_ishare.clients import CommonBaseClient + + +@pytest.mark.parametrize( + "client", + [ + "common_auth_client", + "satellite_client", + ], +) +def test_common_token_flow_first_time_setup(client, request): + test_client = request.getfixturevalue(client) + assert test_client.is_valid_access_token is False + assert test_client.json_web_token == "fake" + + +@pytest.mark.parametrize( + "client", + [ + "common_auth_client", + "satellite_client", + ], +) +@responses.activate +def test_access_token(client, request): + test_client = request.getfixturevalue(client) + + endpoint = f"{test_client.target_domain}/connect/token" + responses.post( + url=endpoint, + json={"access_token": "test"}, + match=[matchers.header_matcher({"Authorization": "Bearer Fake"})], + ) + token = test_client.request_access_token() + assert token == {"access_token": "test"} + responses.assert_call_count(url=endpoint, count=1) + + +@responses.activate +def test_callable_access_token_fake_jwt(test_client_arguments, request): + def get_test_jwt(): + return "super-fake-jwt" + + test_client_arguments["json_web_token"] = get_test_jwt() + test_client = CommonBaseClient(**test_client_arguments) + + endpoint = f"{test_client.target_domain}/connect/token" + responses.post( + url=endpoint, + json={"access_token": "test"}, + match=[matchers.header_matcher({"Authorization": "Bearer super-fake-jwt"})], + ) + + token = test_client.request_access_token() + assert token == {"access_token": "test"} + responses.assert_call_count(url=endpoint, count=1) + + +@responses.activate +def test_callable_access_token(test_client_arguments, request): + def get_test_jwt(): + return "super-fake-jwt" + + test_client_arguments["json_web_token"] = get_test_jwt() + test_client = CommonBaseClient(**test_client_arguments) + + endpoint = f"{test_client.target_domain}/connect/token" + responses.post(url=endpoint, json={"access_token": "test"}) + + token = test_client.request_access_token() + assert token == {"access_token": "test"} + responses.assert_call_count(url=endpoint, count=1) + + +@pytest.mark.parametrize( + "client", + [ + "common_auth_client", + "satellite_client", + ], +) +@responses.activate +def test_access_token_post_return_error(monkeypatch, request, client): + test_client = request.getfixturevalue(client) + + endpoint = f"{test_client.target_domain}/connect/token" + responses.post(url=endpoint, status=401, json={"access_token": "abc"}) + + with pytest.raises(HTTPError) as e: + test_client.request_access_token() + + assert "401 Client Error" in str(e) + + +@pytest.mark.parametrize( + "client", + [ + "common_auth_client", + "satellite_client", + ], +) +@responses.activate +def test_token_reuse(client, create_jwt_response, request): + """ + If use_token is true + > retrieve access token + > get capabilities + if use_token is False + > get capabilities + """ + test_client = request.getfixturevalue(client) + + access_token = f"{test_client.target_domain}/connect/token" + responses.post(url=access_token, json={"access_token": "test"}) + + test_client._get_auth(use_token=True) + test_client._get_auth(use_token=True) + test_client._get_auth(use_token=True) + responses.assert_call_count(url=access_token, count=1) + + +@pytest.mark.parametrize( + "client,use_token", + [ + ("common_auth_client", True), + ("satellite_client", True), + ], +) +@responses.activate +def test_capabilities(client, use_token, create_jwt_response, request): + """ + If use_token is true + > retrieve access token + > get capabilities + if use_token is False + > get capabilities + """ + test_client = request.getfixturevalue(client) + + access_token = f"{test_client.target_domain}/connect/token" + responses.post( + url=access_token, json={"access_token": "test", "aud": test_client.client_eori} + ) + + jwt = create_jwt_response(payload={"test": "this"}) + capabilities = f"{test_client.target_domain}/capabilities" + responses.get(url=capabilities, json={"capabilities_token": jwt}) + + capabilities_result = test_client.get_capabilities(use_token=use_token) + assert capabilities_result["test"] == "this" + responses.assert_call_count(url=access_token, count=int(use_token)) + responses.assert_call_count(url=capabilities, count=1) diff --git a/tests/unit/test_verification.py b/tests/unit/test_verification.py new file mode 100644 index 0000000..b1f7767 --- /dev/null +++ b/tests/unit/test_verification.py @@ -0,0 +1,325 @@ +from datetime import datetime + +import jwt +import pytest +from python_ishare.authentication import create_jwt +from python_ishare.exceptions import ( + IShareInvalidAudience, + IShareInvalidClientAssertionType, + IShareInvalidClientId, + IShareInvalidGrantType, + IShareInvalidScope, + IShareInvalidTokenAlgorithm, + IShareInvalidTokenIssuerOrSubscriber, + IShareInvalidTokenJTI, + IShareInvalidTokenType, + IShareTokenExpirationInvalid, + IShareTokenExpired, + IShareTokenNotValidYet, +) +from python_ishare.verification import validate_client_assertion + +CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" + + +def test_invalid_grant_type(): + with pytest.raises(IShareInvalidGrantType): + validate_client_assertion( + grant_type="", + client_id="", + client_assertion_type="", + scope="", + client_assertion="", + audience="", + ) + + +def test_invalid_scope(): + with pytest.raises(IShareInvalidScope): + validate_client_assertion( + grant_type="client_credentials", + client_assertion_type="", + scope="", + client_id="", + client_assertion="", + audience="", + ) + + +def test_audience_mismatch(satellite_key_and_certs): + rsa_key, public_cert_chain, x509 = satellite_key_and_certs + audience = "my-super-eori" + + token = create_jwt( + payload={"aud": audience}, + private_key=rsa_key, + x5c_certificate_chain=public_cert_chain, + ) + + with pytest.raises(IShareInvalidAudience): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id="", + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="not-my-super-eori", + ) + + +def test_no_client_id(satellite_key_and_certs): + rsa_key, public_cert_chain, _ = satellite_key_and_certs + audience = "my-super-eori" + + token = create_jwt( + payload={"aud": audience}, + private_key=rsa_key, + x5c_certificate_chain=public_cert_chain, + ) + + with pytest.raises(IShareInvalidClientId): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id="", + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience=audience, + ) + + +def test_invalid_client_id(satellite_key_and_certs): + rsa_key, public_cert_chain, x509 = satellite_key_and_certs + + token = create_jwt( + payload={"aud": "test"}, + private_key=rsa_key, + x5c_certificate_chain=public_cert_chain, + ) + + with pytest.raises(IShareInvalidClientId): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id="", + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="test", + ) + + +def test_invalid_client_assertion_type(): + with pytest.raises(IShareInvalidClientAssertionType): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id="", + client_assertion_type="", + client_assertion="", + audience="", + ) + + +def test_invalid_algorithm(satellite_key_and_certs): + rsa_key, public_cert_chain, x509_b64 = satellite_key_and_certs + token_headers = {"alg": "RS384", "typ": "oops", "x5c": [x509_b64]} + + token = jwt.encode( + payload={}, key=rsa_key, headers=token_headers, algorithm="RS384" + ) + + with pytest.raises(IShareInvalidTokenAlgorithm): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id="", + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="", + ) + + +def test_invalid_token_type(satellite_key_and_certs): + rsa_key, public_cert_chain, _ = satellite_key_and_certs + + token_headers = {"alg": "RS256", "typ": "oops", "x5c": [""]} + token = jwt.encode( + payload={}, key=rsa_key, headers=token_headers, algorithm="RS256" + ) + + with pytest.raises(IShareInvalidTokenType): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id="", + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="", + ) + + +def test_invalid_issuer_or_subscriber(satellite_client, satellite_key_and_certs): + rsa_key, public_cert_chain, _ = satellite_key_and_certs + client_id = "EU.EORI.NL123456" + + token = create_jwt( + payload={ + "aud": "test", + "sub": client_id, + "iss": "EU.EORI.NL654321", + }, + private_key=rsa_key, + x5c_certificate_chain=public_cert_chain, + ) + + with pytest.raises(IShareInvalidTokenIssuerOrSubscriber): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id=client_id, + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="test", + ) + + +def test_invalid_jti(satellite_key_and_certs): + rsa_key, public_cert_chain, x509_cert = satellite_key_and_certs + client_id = "EU.EORI.NL000000001" + + headers = {"alg": "RS256", "typ": "JWT", "x5c": x509_cert} + token = jwt.encode( + payload={ + "aud": "test", + "iss": client_id, + "sub": client_id, + }, + key=rsa_key, + headers=headers, + algorithm="RS256", + ) + + with pytest.raises(IShareInvalidTokenJTI): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id=client_id, + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="test", + ) + + +def test_invalid_expiration(satellite_key_and_certs): + rsa_key, public_cert_chain, x509_cert = satellite_key_and_certs + client_id = "EU.EORI.NL000000001" + + headers = {"alg": "RS256", "typ": "JWT", "x5c": x509_cert} + token = jwt.encode( + payload={ + "aud": "test", + "iss": client_id, + "sub": client_id, + "jti": "A", + "exp": datetime.now().timestamp() + 30, + "iat": 0, + }, + key=rsa_key, + headers=headers, + algorithm="RS256", + ) + + with pytest.raises(IShareTokenExpirationInvalid): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id=client_id, + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="test", + ) + + +def test_token_not_valid_yet(satellite_key_and_certs): + rsa_key, public_cert_chain, x509_cert = satellite_key_and_certs + client_id = "EU.EORI.NL000000001" + + headers = {"alg": "RS256", "typ": "JWT", "x5c": x509_cert} + token = jwt.encode( + payload={ + "aud": "test", + "iss": client_id, + "sub": client_id, + "jti": "A", + "exp": datetime.now().timestamp() + 40, + "iat": datetime.now().timestamp() + 10, + }, + key=rsa_key, + headers=headers, + algorithm="RS256", + ) + + with pytest.raises(IShareTokenNotValidYet): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id=client_id, + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="test", + ) + + +def test_token_expired(satellite_key_and_certs): + rsa_key, public_cert_chain, x509_cert = satellite_key_and_certs + + headers = {"alg": "RS256", "typ": "JWT", "x5c": x509_cert} + token = jwt.encode( + payload={ + "aud": "test", + "iss": "EU.EORI.NL000000001", + "sub": "EU.EORI.NL000000001", + "jti": "A", + "exp": datetime.now().timestamp(), + "iat": datetime.now().timestamp() - 30, + }, + key=rsa_key, + headers=headers, + algorithm="RS256", + ) + + with pytest.raises(IShareTokenExpired): + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id="", + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="test", + ) + + +def test_valid_token(satellite_key_and_certs): + rsa_key, public_cert_chain, _ = satellite_key_and_certs + client_id = "EU.EORI.NL000000001" + + print(public_cert_chain) + + token = create_jwt( + payload={ + "aud": "test", + "iss": client_id, + "sub": client_id, + }, + private_key=rsa_key, + x5c_certificate_chain=public_cert_chain, + ) + + validate_client_assertion( + grant_type="client_credentials", + scope="iSHARE", + client_id=client_id, + client_assertion_type=CLIENT_ASSERTION_TYPE, + client_assertion=token, + audience="test", + ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..9e7ac14 --- /dev/null +++ b/tox.ini @@ -0,0 +1,24 @@ +[tox] +isolated_build = True +envlist = py,linters + +[testenv] +description = Run unit tests and safety checks +deps = poetry +allowlist_externals = + poetry +commands = + poetry run pytest {posargs:./tests/unit} + +[testenv:linters] +description = Basic linting checks +set_env = + AUTO_FORMAT_DIRS = src tests + AUTO_VALIDATE_DIRS = src +commands = + poetry run black --diff --check {env:AUTO_FORMAT_DIRS} + poetry run isort --diff --check-only {env:AUTO_FORMAT_DIRS} + poetry run bandit {env:AUTO_VALIDATE_DIRS} + poetry run flake8 {env:AUTO_VALIDATE_DIRS} + poetry run safety check --output bare + poetry run mypy --install-types --non-interactive diff --git a/utils/generate_key_and_certificate.py b/utils/generate_key_and_certificate.py new file mode 100644 index 0000000..e1b2d7d --- /dev/null +++ b/utils/generate_key_and_certificate.py @@ -0,0 +1,121 @@ +""" +This function will generate the required files necessary to successfully call the +various functions in the ishare_auth package. + +source: https://cryptography.io/en/latest/x509/tutorial/ + +example from root dir: + + python utils/generate_key_and_certificate.py \ + './ishare_adapter/tests/data' \ + participant_new + +NOTE: This generates a self-signed certificate. This cannot be used as a certificate in +iShare context and can only be used for local testing purposes. An iShare acceptable +certificate must be signed by a trusted CA. +""" +import argparse +import datetime +from pathlib import Path + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey +from cryptography.x509 import Certificate + +parser = argparse.ArgumentParser( + description=""" + This function will generate the required files necessary to successfully call the + various functions in the ishare_auth package. + NOTE: This generates a self-signed certificate. This cannot be used as a certificate + in iShare context and can only be used for local testing purposes. An iShare + certificate must be signed by a trusted CA. + """ +) +parser.add_argument("directory") +parser.add_argument("name") +parser.add_argument("password", nargs="?", default=None) +parsed_args = parser.parse_args() + + +CA_CERT = Path(__name__).parent / "iSHARETestCA.cacert.pem" + + +def check_dir(directory: str) -> Path: + path = Path(directory) + + if not path.exists() or not path.is_dir(): + raise Exception("Provided directory path doesn't exist or isn't a directory.") + + return path + + +def generate_rsa_key(directory: Path, basename: str, password: str) -> RSAPrivateKey: + encryption = serialization.NoEncryption() + key: RSAPrivateKey = rsa.generate_private_key(public_exponent=65537, key_size=4096) + + if password: + encryption = serialization.BestAvailableEncryption( + password=password.encode("utf8") + ) + + # Write our key to disk for safe keeping + with (directory / f"{basename}_rsa_key.pem").open("wb") as file: + file.write( + key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=encryption, + ) + ) + + return key + + +def generate_x509_certificate( + key: RSAPrivateKey, directory: Path, basename: str +) -> Certificate: + subject = issuer = x509.Name( + [ + x509.NameAttribute(x509.NameOID.COUNTRY_NAME, "NL"), + x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, "Zuid-Holland"), + x509.NameAttribute(x509.NameOID.LOCALITY_NAME, "Rotterdam"), + x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, basename), + x509.NameAttribute(x509.NameOID.COMMON_NAME, "localhost"), + ] + ) + + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(issuer) + .public_key(key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.datetime.now(datetime.timezone.utc)) + .not_valid_after( + datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=10) + ) + .add_extension( + x509.SubjectAlternativeName([x509.DNSName("localhost")]), critical=False + ) + .sign(key, hashes.SHA256()) + ) + + # Write our certificate out to disk. + with (directory / f"{basename}_certificate.pem").open("wb") as file: + file.write(cert.public_bytes(serialization.Encoding.PEM)) + + return cert + + +def run(arguments): + directory: Path = check_dir(arguments.directory) + key = generate_rsa_key( + directory=directory, basename=arguments.name, password=arguments.password + ) + generate_x509_certificate(directory=directory, basename=arguments.name, key=key) + + +if __name__ == "__main__": + run(parsed_args)