├── .bumpversion.cfg ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── build-docs.yml │ ├── python-publish.yml │ └── python-test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── .travis.yml ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── SECURITY.md ├── docs ├── conf.py ├── contributing.rst ├── format.rst ├── index.rst ├── make.bat ├── references.rst └── validation.rst ├── openapi_schema_validator ├── __init__.py ├── _format.py ├── _keywords.py ├── _types.py ├── py.typed ├── shortcuts.py └── validators.py ├── poetry.lock ├── pyproject.toml └── tests ├── integration └── test_validators.py └── unit └── test_shortcut.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.6.3 3 | tag = True 4 | tag_name = {new_version} 5 | commit = True 6 | message = Version {new_version} 7 | parse = (?P\d+)\.(?P\d+)\.(?P\d+) 8 | serialize = 9 | {major}.{minor}.{patch} 10 | 11 | [bumpversion:file:openapi_schema_validator/__init__.py] 12 | 13 | [bumpversion:file:pyproject.toml] 14 | search = version = "{current_version}" 15 | replace = version = "{new_version}" 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [p1c2u] 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /.github/workflows/build-docs.yml: -------------------------------------------------------------------------------- 1 | name: Build documentation 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: [opened, synchronize] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Set up Python 3.12 15 | uses: actions/setup-python@v5 16 | with: 17 | python-version: 3.12 18 | 19 | - name: Get full Python version 20 | id: full-python-version 21 | run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") 22 | 23 | - name: Set up poetry 24 | uses: Gr1N/setup-poetry@v9 25 | 26 | - name: Configure poetry 27 | run: poetry config virtualenvs.in-project true 28 | 29 | - name: Set up cache 30 | uses: actions/cache@v4 31 | id: cache 32 | with: 33 | path: .venv 34 | key: venv-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} 35 | 36 | - name: Ensure cache is healthy 37 | if: steps.cache.outputs.cache-hit == 'true' 38 | run: timeout 10s poetry run pip --version || rm -rf .venv 39 | 40 | - name: Install dependencies 41 | run: poetry install --with docs 42 | 43 | - name: Build documentation 44 | run: | 45 | poetry run python -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs docs/_build/html -n -W 46 | 47 | - uses: actions/upload-artifact@v4 48 | name: Upload docs as artifact 49 | with: 50 | name: docs-html 51 | path: './docs/_build/html' 52 | if-no-files-found: error 53 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Publish python packages 5 | 6 | on: 7 | workflow_dispatch: 8 | release: 9 | types: 10 | - published 11 | 12 | jobs: 13 | publish: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | id-token: write 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Set up Python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: '3.x' 24 | 25 | - name: Set up poetry 26 | uses: Gr1N/setup-poetry@v9 27 | 28 | - name: Build 29 | run: poetry build 30 | 31 | - name: Publish 32 | uses: pypa/gh-action-pypi-publish@release/v1 33 | with: 34 | packages-dir: dist/ 35 | -------------------------------------------------------------------------------- /.github/workflows/python-test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Test python code 5 | 6 | on: 7 | push: 8 | pull_request: 9 | types: [opened, synchronize] 10 | 11 | jobs: 12 | test: 13 | name: "Tests" 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] 18 | fail-fast: false 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | 27 | - name: Get full Python version 28 | id: full-python-version 29 | run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") 30 | 31 | - name: Set up poetry 32 | uses: Gr1N/setup-poetry@v9 33 | 34 | - name: Configure poetry 35 | run: poetry config virtualenvs.in-project true 36 | 37 | - name: Set up cache 38 | uses: actions/cache@v4 39 | id: cache 40 | with: 41 | path: .venv 42 | key: venv-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} 43 | 44 | - name: Ensure cache is healthy 45 | if: steps.cache.outputs.cache-hit == 'true' 46 | run: timeout 10s poetry run pip --version || rm -rf .venv 47 | 48 | - name: Install dependencies 49 | run: poetry install --all-extras 50 | 51 | - name: Test 52 | env: 53 | PYTEST_ADDOPTS: "--color=yes" 54 | run: poetry run pytest 55 | 56 | - name: Static type check 57 | run: poetry run mypy 58 | 59 | - name: Check dependencies 60 | run: poetry run deptry . 61 | 62 | - name: Upload coverage 63 | uses: codecov/codecov-action@v5 64 | 65 | static-checks: 66 | name: "Static checks" 67 | runs-on: ubuntu-latest 68 | steps: 69 | - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )" 70 | uses: actions/checkout@v4 71 | 72 | - name: "Setup Python" 73 | uses: actions/setup-python@v5 74 | with: 75 | python-version: 3.9 76 | 77 | - name: Get full Python version 78 | id: full-python-version 79 | run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") 80 | 81 | - name: Set up poetry 82 | uses: Gr1N/setup-poetry@v9 83 | 84 | - name: Configure poetry 85 | run: poetry config virtualenvs.in-project true 86 | 87 | - name: Set up cache 88 | uses: actions/cache@v4 89 | id: cache 90 | with: 91 | path: .venv 92 | key: venv-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} 93 | 94 | - name: Ensure cache is healthy 95 | if: steps.cache.outputs.cache-hit == 'true' 96 | run: timeout 10s poetry run pip --version || rm -rf .venv 97 | 98 | - name: Install dependencies 99 | run: poetry install 100 | 101 | - name: Run static checks 102 | run: poetry run pre-commit run -a 103 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | reports/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 96 | __pypackages__/ 97 | 98 | # Celery stuff 99 | celerybeat-schedule 100 | celerybeat.pid 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # asdf versions 125 | .tool-versions 126 | .default-python-packages 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_stages: [commit, push] 3 | default_language_version: 4 | # force all unspecified python hooks to run python3 5 | python: python3 6 | minimum_pre_commit_version: "1.20.0" 7 | repos: 8 | - repo: meta 9 | hooks: 10 | - id: check-hooks-apply 11 | 12 | - repo: https://github.com/asottile/pyupgrade 13 | rev: v2.38.4 14 | hooks: 15 | - id: pyupgrade 16 | args: ["--py36-plus"] 17 | 18 | - repo: local 19 | hooks: 20 | - id: flynt 21 | name: Convert to f-strings with flynt 22 | entry: flynt 23 | language: python 24 | additional_dependencies: ['flynt==0.76'] 25 | 26 | - id: black 27 | name: black 28 | entry: black 29 | language: system 30 | require_serial: true 31 | types: [python] 32 | 33 | - id: isort 34 | name: isort 35 | entry: isort 36 | args: ['--filter-files'] 37 | language: system 38 | require_serial: true 39 | types: [python] 40 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/conf.py 5 | 6 | formats: all 7 | 8 | build: 9 | os: ubuntu-20.04 10 | tools: 11 | python: "3.9" 12 | jobs: 13 | post_create_environment: 14 | - pip install poetry 15 | - poetry config virtualenvs.create false 16 | post_install: 17 | - poetry install --with docs 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | matrix: 4 | include: 5 | - python: 3.9 6 | - python: 3.10 7 | - python: 3.11 8 | - python: 3.12 9 | - python: nightly 10 | - python: pypy3 11 | allow_failures: 12 | - python: nightly 13 | before_install: 14 | - curl -sL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python - -y 15 | - export PATH=$PATH:$HOME/.local/bin 16 | install: 17 | - poetry install 18 | script: 19 | - poetry run pytest 20 | after_success: 21 | - codecov 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Please read the `Contributing `__ guidelines in the documentation site. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, A 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | include requirements.txt 4 | include requirements_dev.txt 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .EXPORT_ALL_VARIABLES: 2 | 3 | PROJECT_NAME=openapi-schema-validator 4 | PACKAGE_NAME=$(subst -,_,${PROJECT_NAME}) 5 | VERSION=`git describe --abbrev=0` 6 | 7 | PYTHONDONTWRITEBYTECODE=1 8 | 9 | params: 10 | @echo "Project name: ${PROJECT_NAME}" 11 | @echo "Package name: ${PACKAGE_NAME}" 12 | @echo "Version: ${VERSION}" 13 | 14 | dist-build: 15 | @poetry build 16 | 17 | dist-cleanup: 18 | @rm -rf build dist ${PACKAGE_NAME}.egg-info 19 | 20 | dist-upload: 21 | @poetry publish 22 | 23 | test-python: 24 | @pytest 25 | 26 | test-cache-cleanup: 27 | @rm -rf .pytest_cache 28 | 29 | reports-cleanup: 30 | @rm -rf reports 31 | 32 | test-cleanup: test-cache-cleanup reports-cleanup 33 | 34 | docs-html: 35 | sphinx-build -b html docs docs/_build 36 | 37 | docs-cleanup: 38 | @rm -rf docs/_build 39 | 40 | cleanup: dist-cleanup test-cleanup 41 | 42 | release/patch: 43 | @bump2version patch 44 | 45 | release/minor: 46 | @bump2version minor 47 | 48 | release/major: 49 | @bump2version major 50 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ************************ 2 | openapi-schema-validator 3 | ************************ 4 | 5 | .. image:: https://img.shields.io/pypi/v/openapi-schema-validator.svg 6 | :target: https://pypi.python.org/pypi/openapi-schema-validator 7 | .. image:: https://travis-ci.org/python-openapi/openapi-schema-validator.svg?branch=master 8 | :target: https://travis-ci.org/python-openapi/openapi-schema-validator 9 | .. image:: https://img.shields.io/codecov/c/github/python-openapi/openapi-schema-validator/master.svg?style=flat 10 | :target: https://codecov.io/github/python-openapi/openapi-schema-validator?branch=master 11 | .. image:: https://img.shields.io/pypi/pyversions/openapi-schema-validator.svg 12 | :target: https://pypi.python.org/pypi/openapi-schema-validator 13 | .. image:: https://img.shields.io/pypi/format/openapi-schema-validator.svg 14 | :target: https://pypi.python.org/pypi/openapi-schema-validator 15 | .. image:: https://img.shields.io/pypi/status/openapi-schema-validator.svg 16 | :target: https://pypi.python.org/pypi/openapi-schema-validator 17 | 18 | About 19 | ##### 20 | 21 | Openapi-schema-validator is a Python library that validates schema against: 22 | 23 | * `OpenAPI Schema Specification v3.0 `__ which is an extended subset of the `JSON Schema Specification Wright Draft 00 `__. 24 | * `OpenAPI Schema Specification v3.1 `__ which is an extended superset of the `JSON Schema Specification Draft 2020-12 `__. 25 | 26 | 27 | Documentation 28 | ############# 29 | 30 | Check documentation to see more details about the features. All documentation is in the "docs" directory and online at `openapi-schema-validator.readthedocs.io `__ 31 | 32 | 33 | Installation 34 | ############ 35 | 36 | Recommended way (via pip): 37 | 38 | .. code-block:: console 39 | 40 | pip install openapi-schema-validator 41 | 42 | Alternatively you can download the code and install from the repository: 43 | 44 | .. code-block:: console 45 | 46 | pip install -e git+https://github.com/python-openapi/openapi-schema-validator.git#egg=openapi_schema_validator 47 | 48 | 49 | Usage 50 | ##### 51 | 52 | To validate an OpenAPI v3.1 schema: 53 | 54 | .. code-block:: python 55 | 56 | from openapi_schema_validator import validate 57 | 58 | # A sample schema 59 | schema = { 60 | "type": "object", 61 | "required": [ 62 | "name" 63 | ], 64 | "properties": { 65 | "name": { 66 | "type": "string" 67 | }, 68 | "age": { 69 | "type": ["integer", "null"], 70 | "format": "int32", 71 | "minimum": 0, 72 | }, 73 | "birth-date": { 74 | "type": "string", 75 | "format": "date", 76 | }, 77 | "address": { 78 | "type": 'array', 79 | "prefixItems": [ 80 | { "type": "number" }, 81 | { "type": "string" }, 82 | { "enum": ["Street", "Avenue", "Boulevard"] }, 83 | { "enum": ["NW", "NE", "SW", "SE"] } 84 | ], 85 | "items": False, 86 | } 87 | }, 88 | "additionalProperties": False, 89 | } 90 | 91 | # If no exception is raised by validate(), the instance is valid. 92 | validate({"name": "John", "age": 23, "address": [1600, "Pennsylvania", "Avenue"]}, schema) 93 | 94 | validate({"name": "John", "city": "London"}, schema) 95 | 96 | Traceback (most recent call last): 97 | ... 98 | ValidationError: Additional properties are not allowed ('city' was unexpected) 99 | 100 | By default, the latest OpenAPI schema syntax is expected. 101 | 102 | For more details read about `Validation `__. 103 | 104 | Related projects 105 | ################ 106 | * `openapi-core `__ 107 | Python library that adds client-side and server-side support for the OpenAPI. 108 | * `openapi-spec-validator `__ 109 | Python library that validates OpenAPI Specs against the OpenAPI 2.0 (aka Swagger) and OpenAPI 3.0 specification 110 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you believe you have found a security vulnerability in the repository, please report it to us as described below. 6 | 7 | **Please do not report security vulnerabilities through public GitHub issues.** 8 | 9 | Instead, please report them directly to the repository maintainer. 10 | 11 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 12 | 13 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 14 | * Full paths of source file(s) related to the manifestation of the issue 15 | * The location of the affected source code (tag/branch/commit or direct URL) 16 | * Any special configuration required to reproduce the issue 17 | * Step-by-step instructions to reproduce the issue 18 | * Proof-of-concept or exploit code (if possible) 19 | * Impact of the issue, including how an attacker might exploit the issue 20 | * This information will help us triage your report more quickly. 21 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import openapi_schema_validator 2 | 3 | project = "openapi-schema-validator" 4 | copyright = "2023, Artur Maciag" 5 | author = "Artur Maciag" 6 | 7 | release = openapi_schema_validator.__version__ 8 | 9 | extensions = [ 10 | "sphinx.ext.autodoc", 11 | "sphinx.ext.doctest", 12 | "sphinx.ext.intersphinx", 13 | "sphinx.ext.coverage", 14 | "sphinx.ext.viewcode", 15 | "sphinx_immaterial", 16 | ] 17 | 18 | templates_path = ["_templates"] 19 | 20 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 21 | 22 | html_theme = "sphinx_immaterial" 23 | 24 | html_static_path = [] 25 | 26 | html_title = "openapi-schema-validator" 27 | 28 | html_theme_options = { 29 | "analytics": { 30 | "provider": "google", 31 | "property": "G-11RDPBZ7EJ", 32 | }, 33 | "repo_url": "https://github.com/python-openapi/openapi-schema-validator/", 34 | "repo_name": "openapi-schema-validator", 35 | "icon": { 36 | "repo": "fontawesome/brands/github-alt", 37 | "edit": "material/file-edit-outline", 38 | }, 39 | "palette": [ 40 | { 41 | "media": "(prefers-color-scheme: dark)", 42 | "scheme": "slate", 43 | "primary": "lime", 44 | "accent": "amber", 45 | "scheme": "slate", 46 | "toggle": { 47 | "icon": "material/toggle-switch", 48 | "name": "Switch to light mode", 49 | }, 50 | }, 51 | { 52 | "media": "(prefers-color-scheme: light)", 53 | "scheme": "default", 54 | "primary": "lime", 55 | "accent": "amber", 56 | "toggle": { 57 | "icon": "material/toggle-switch-off-outline", 58 | "name": "Switch to dark mode", 59 | }, 60 | }, 61 | ], 62 | "globaltoc_collapse": False, 63 | } 64 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Firstly, thank you all for taking the time to contribute. 5 | 6 | The following section describes how you can contribute to the openapi-schema-validator project on GitHub. 7 | 8 | Reporting bugs 9 | -------------- 10 | 11 | Before you report 12 | ^^^^^^^^^^^^^^^^^ 13 | 14 | * Check whether your issue does not already exist in the `Issue tracker `__. 15 | * Make sure it is not a support request or question better suited for `Discussion board `__. 16 | 17 | How to submit a report 18 | ^^^^^^^^^^^^^^^^^^^^^^ 19 | 20 | * Include clear title. 21 | * Describe your runtime environment with exact versions you use. 22 | * Describe the exact steps which reproduce the problem, including minimal code snippets. 23 | * Describe the behavior you observed after following the steps, pasting console outputs. 24 | * Describe expected behavior to see and why, including links to documentations. 25 | 26 | Code contribution 27 | ----------------- 28 | 29 | Prerequisites 30 | ^^^^^^^^^^^^^ 31 | 32 | Install `Poetry `__ by following the `official installation instructions `__. Optionally (but recommended), configure Poetry to create a virtual environment in a folder named ``.venv`` within the root directory of the project: 33 | 34 | .. code-block:: console 35 | 36 | poetry config virtualenvs.in-project true 37 | 38 | Setup 39 | ^^^^^ 40 | 41 | To create a development environment and install the runtime and development dependencies, run: 42 | 43 | .. code-block:: console 44 | 45 | poetry install 46 | 47 | Then enter the virtual environment created by Poetry: 48 | 49 | .. code-block:: console 50 | 51 | poetry shell 52 | 53 | Static checks 54 | ^^^^^^^^^^^^^ 55 | 56 | The project uses static checks using fantastic `pre-commit `__. Every change is checked on CI and if it does not pass the tests it cannot be accepted. If you want to check locally then run following command to install pre-commit. 57 | 58 | To turn on pre-commit checks for commit operations in git, enter: 59 | 60 | .. code-block:: console 61 | 62 | pre-commit install 63 | 64 | To run all checks on your staged files, enter: 65 | 66 | .. code-block:: console 67 | 68 | pre-commit run 69 | 70 | To run all checks on all files, enter: 71 | 72 | .. code-block:: console 73 | 74 | pre-commit run --all-files 75 | 76 | Pre-commit check results are also attached to your PR through integration with Github Action. 77 | -------------------------------------------------------------------------------- /docs/format.rst: -------------------------------------------------------------------------------- 1 | Format check 2 | ============ 3 | 4 | You can check format for predefined OAS primitive types 5 | 6 | .. code-block:: python 7 | 8 | from openapi_schema_validator import oas31_format_checker 9 | 10 | validate({"name": "John", "birth-date": "-12"}, schema, format_checker=oas31_format_checker) 11 | 12 | Traceback (most recent call last): 13 | ... 14 | ValidationError: '-12' is not a 'date' 15 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | openapi-schema-validator 2 | ======================== 3 | 4 | .. toctree:: 5 | :hidden: 6 | :maxdepth: 2 7 | 8 | validation 9 | format 10 | references 11 | contributing 12 | 13 | Openapi-schema-validator is a Python library that validates schema against: 14 | 15 | * `OpenAPI Schema Specification v3.0 `__ which is an extended subset of the `JSON Schema Specification Wright Draft 00 `__. 16 | * `OpenAPI Schema Specification v3.1 `__ which is an extended superset of the `JSON Schema Specification Draft 2020-12 `__. 17 | 18 | Installation 19 | ------------ 20 | 21 | .. md-tab-set:: 22 | 23 | .. md-tab-item:: Pip + PyPI (recommented) 24 | 25 | .. code-block:: console 26 | 27 | pip install openapi-schema-validator 28 | 29 | .. md-tab-item:: Pip + the source 30 | 31 | .. code-block:: console 32 | 33 | pip install -e git+https://github.com/python-openapi/openapi-schema-validator.git#egg=openapi_schema_validator 34 | 35 | Usage 36 | ----- 37 | 38 | .. code-block:: python 39 | 40 | from openapi_schema_validator import validate 41 | 42 | # A sample schema 43 | schema = { 44 | "type": "object", 45 | "required": [ 46 | "name" 47 | ], 48 | "properties": { 49 | "name": { 50 | "type": "string" 51 | }, 52 | "age": { 53 | "type": ["integer", "null"], 54 | "format": "int32", 55 | "minimum": 0, 56 | }, 57 | "birth-date": { 58 | "type": "string", 59 | "format": "date", 60 | }, 61 | "address": { 62 | "type": 'array', 63 | "prefixItems": [ 64 | { "type": "number" }, 65 | { "type": "string" }, 66 | { "enum": ["Street", "Avenue", "Boulevard"] }, 67 | { "enum": ["NW", "NE", "SW", "SE"] } 68 | ], 69 | "items": False, 70 | } 71 | }, 72 | "additionalProperties": False, 73 | } 74 | 75 | # If no exception is raised by validate(), the instance is valid. 76 | validate({"name": "John", "age": 23, "address": [1600, "Pennsylvania", "Avenue"]}, schema) 77 | 78 | validate({"name": "John", "city": "London"}, schema) 79 | 80 | Traceback (most recent call last): 81 | ... 82 | ValidationError: Additional properties are not allowed ('city' was unexpected) 83 | 84 | Read more about the :doc:`validation`. 85 | 86 | Related projects 87 | ---------------- 88 | 89 | * `openapi-core `__ 90 | Python library that adds client-side and server-side support for the OpenAPI v3.0 and OpenAPI v3.1 specification. 91 | * `openapi-spec-validator `__ 92 | Python library that validates OpenAPI Specs against the OpenAPI 2.0 (aka Swagger), OpenAPI 3.0 and OpenAPI 3.1 specification. The validator aims to check for full compliance with the Specification. 93 | 94 | License 95 | ------- 96 | 97 | The project is under the terms of BSD 3-Clause License. 98 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/references.rst: -------------------------------------------------------------------------------- 1 | References 2 | ========== 3 | 4 | You can resolve JSON Schema references by passing registry 5 | 6 | .. code-block:: python 7 | 8 | from openapi_schema_validator import validate 9 | from referencing import Registry, Resource 10 | from referencing.jsonschema import DRAFT202012 11 | 12 | # A schema with reference 13 | schema = { 14 | "type" : "object", 15 | "required": [ 16 | "name" 17 | ], 18 | "properties": { 19 | "name": { 20 | "$ref": "urn:name-schema" 21 | }, 22 | "age": { 23 | "$ref": "urn:age-schema" 24 | }, 25 | "birth-date": { 26 | "$ref": "urn:birth-date-schema" 27 | } 28 | }, 29 | "additionalProperties": False, 30 | } 31 | # Referenced schemas 32 | # In-schema identifier 33 | name_schema = Resource.from_contents({ 34 | "$schema": "https://json-schema.org/draft/2020-12/schema", 35 | "type": "string", 36 | }) 37 | # Explicit identifier 38 | age_schema = DRAFT202012.create_resource({ 39 | "type": "integer", 40 | "format": "int32", 41 | "minimum": 0, 42 | "maximum": 120, 43 | }) 44 | # Default identifier 45 | birth_date_schema = Resource.from_contents({ 46 | "type": "string", 47 | "format": "date", 48 | }, default_specification=DRAFT202012) 49 | registry = Registry().with_resources( 50 | [ 51 | ("urn:name-schema", name_schema), 52 | ("urn:age-schema", age_schema), 53 | ("urn:birth-date-schema", birth_date_schema), 54 | ], 55 | ) 56 | 57 | # If no exception is raised by validate(), the instance is valid. 58 | validate({"name": "John", "age": 23}, schema, registry=registry) 59 | 60 | # raises error 61 | validate({"birth-date": "yesterday", "age": -1}, schema, registry=registry) 62 | 63 | Traceback (most recent call last): 64 | ... 65 | ValidationError: 'name' is a required property 66 | 67 | For more information about resolving references see `JSON (Schema) Referencing `__ 68 | -------------------------------------------------------------------------------- /docs/validation.rst: -------------------------------------------------------------------------------- 1 | Schema validation 2 | ================= 3 | 4 | The simplest way to validate an instance under OAS schema is to use the ``validate`` function. 5 | 6 | Validate 7 | -------- 8 | 9 | To validate an OpenAPI v3.1 schema: 10 | 11 | .. code-block:: python 12 | 13 | from openapi_schema_validator import validate 14 | 15 | # A sample schema 16 | schema = { 17 | "type": "object", 18 | "required": [ 19 | "name" 20 | ], 21 | "properties": { 22 | "name": { 23 | "type": "string" 24 | }, 25 | "age": { 26 | "type": ["integer", "null"], 27 | "format": "int32", 28 | "minimum": 0, 29 | }, 30 | "birth-date": { 31 | "type": "string", 32 | "format": "date", 33 | }, 34 | "address": { 35 | "type": 'array', 36 | "prefixItems": [ 37 | { "type": "number" }, 38 | { "type": "string" }, 39 | { "enum": ["Street", "Avenue", "Boulevard"] }, 40 | { "enum": ["NW", "NE", "SW", "SE"] } 41 | ], 42 | "items": False, 43 | } 44 | }, 45 | "additionalProperties": False, 46 | } 47 | 48 | # If no exception is raised by validate(), the instance is valid. 49 | validate({"name": "John", "age": 23, "address": [1600, "Pennsylvania", "Avenue"]}, schema) 50 | 51 | validate({"name": "John", "city": "London"}, schema) 52 | 53 | Traceback (most recent call last): 54 | ... 55 | ValidationError: Additional properties are not allowed ('city' was unexpected) 56 | 57 | By default, the latest OpenAPI schema syntax is expected. 58 | 59 | Validators 60 | ---------- 61 | 62 | if you want to disambiguate the expected schema version, import and use ``OAS31Validator``: 63 | 64 | .. code-block:: python 65 | 66 | from openapi_schema_validator import OAS31Validator 67 | 68 | validate({"name": "John", "age": 23}, schema, cls=OAS31Validator) 69 | 70 | In order to validate OpenAPI 3.0 schema, import and use ``OAS30Validator`` instead of ``OAS31Validator``. 71 | 72 | .. code-block:: python 73 | 74 | from openapi_schema_validator import OAS30Validator 75 | 76 | # A sample schema 77 | schema = { 78 | "type": "object", 79 | "required": [ 80 | "name" 81 | ], 82 | "properties": { 83 | "name": { 84 | "type": "string" 85 | }, 86 | "age": { 87 | "type": "integer", 88 | "format": "int32", 89 | "minimum": 0, 90 | "nullable": True, 91 | }, 92 | "birth-date": { 93 | "type": "string", 94 | "format": "date", 95 | } 96 | }, 97 | "additionalProperties": False, 98 | } 99 | 100 | validate({"name": "John", "age": None}, schema, cls=OAS30Validator) 101 | 102 | Read/write context 103 | ------------------ 104 | 105 | OpenAPI 3.0 schema comes with ``readOnly`` and ``writeOnly`` keywords. In order to validate read/write context in OpenAPI 3.0 schema, import and use ``OAS30ReadValidator`` or ``OAS30WriteValidator``. 106 | 107 | .. code-block:: python 108 | 109 | from openapi_schema_validator import OAS30WriteValidator 110 | 111 | # A sample schema 112 | schema = { 113 | "type": "object", 114 | "required": [ 115 | "name" 116 | ], 117 | "properties": { 118 | "name": { 119 | "type": "string" 120 | }, 121 | "age": { 122 | "type": "integer", 123 | "format": "int32", 124 | "minimum": 0, 125 | "readOnly": True, 126 | }, 127 | "birth-date": { 128 | "type": "string", 129 | "format": "date", 130 | } 131 | }, 132 | "additionalProperties": False, 133 | } 134 | 135 | validate({"name": "John", "age": 23}, schema, cls=OAS30WriteValidator) 136 | 137 | Traceback (most recent call last): 138 | ... 139 | ValidationError: Tried to write read-only property with 23 140 | -------------------------------------------------------------------------------- /openapi_schema_validator/__init__.py: -------------------------------------------------------------------------------- 1 | from openapi_schema_validator._format import oas30_format_checker 2 | from openapi_schema_validator._format import oas31_format_checker 3 | from openapi_schema_validator.shortcuts import validate 4 | from openapi_schema_validator.validators import OAS30ReadValidator 5 | from openapi_schema_validator.validators import OAS30Validator 6 | from openapi_schema_validator.validators import OAS30WriteValidator 7 | from openapi_schema_validator.validators import OAS31Validator 8 | 9 | __author__ = "Artur Maciag" 10 | __email__ = "maciag.artur@gmail.com" 11 | __version__ = "0.6.3" 12 | __url__ = "https://github.com/python-openapi/openapi-schema-validator" 13 | __license__ = "3-clause BSD License" 14 | 15 | __all__ = [ 16 | "validate", 17 | "OAS30ReadValidator", 18 | "OAS30WriteValidator", 19 | "OAS30Validator", 20 | "oas30_format_checker", 21 | "OAS31Validator", 22 | "oas31_format_checker", 23 | ] 24 | -------------------------------------------------------------------------------- /openapi_schema_validator/_format.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | from base64 import b64decode 3 | from base64 import b64encode 4 | from numbers import Number 5 | from typing import Any 6 | from typing import Union 7 | 8 | from jsonschema._format import FormatChecker 9 | 10 | 11 | def is_int32(instance: Any) -> bool: 12 | # bool inherits from int, so ensure bools aren't reported as ints 13 | if isinstance(instance, bool): 14 | return True 15 | if not isinstance(instance, int): 16 | return True 17 | return ~(1 << 31) < instance < 1 << 31 18 | 19 | 20 | def is_int64(instance: Any) -> bool: 21 | # bool inherits from int, so ensure bools aren't reported as ints 22 | if isinstance(instance, bool): 23 | return True 24 | if not isinstance(instance, int): 25 | return True 26 | return ~(1 << 63) < instance < 1 << 63 27 | 28 | 29 | def is_float(instance: Any) -> bool: 30 | # bool inherits from int 31 | if isinstance(instance, int): 32 | return True 33 | if not isinstance(instance, Number): 34 | return True 35 | return isinstance(instance, float) 36 | 37 | 38 | def is_double(instance: Any) -> bool: 39 | # bool inherits from int 40 | if isinstance(instance, int): 41 | return True 42 | if not isinstance(instance, Number): 43 | return True 44 | # float has double precision in Python 45 | # It's double in CPython and Jython 46 | return isinstance(instance, float) 47 | 48 | 49 | def is_binary(instance: Any) -> bool: 50 | if not isinstance(instance, (str, bytes)): 51 | return True 52 | if isinstance(instance, str): 53 | return False 54 | return True 55 | 56 | 57 | def is_byte(instance: Union[str, bytes]) -> bool: 58 | if not isinstance(instance, (str, bytes)): 59 | return True 60 | if isinstance(instance, str): 61 | instance = instance.encode() 62 | 63 | encoded = b64encode(b64decode(instance)) 64 | return encoded == instance 65 | 66 | 67 | def is_password(instance: Any) -> bool: 68 | # A hint to UIs to obscure input 69 | return True 70 | 71 | 72 | oas30_format_checker = FormatChecker() 73 | oas30_format_checker.checks("int32")(is_int32) 74 | oas30_format_checker.checks("int64")(is_int64) 75 | oas30_format_checker.checks("float")(is_float) 76 | oas30_format_checker.checks("double")(is_double) 77 | oas30_format_checker.checks("binary")(is_binary) 78 | oas30_format_checker.checks("byte", (binascii.Error, TypeError))(is_byte) 79 | oas30_format_checker.checks("password")(is_password) 80 | 81 | oas31_format_checker = FormatChecker() 82 | oas31_format_checker.checks("int32")(is_int32) 83 | oas31_format_checker.checks("int64")(is_int64) 84 | oas31_format_checker.checks("float")(is_float) 85 | oas31_format_checker.checks("double")(is_double) 86 | oas31_format_checker.checks("password")(is_password) 87 | -------------------------------------------------------------------------------- /openapi_schema_validator/_keywords.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | from typing import Any 3 | from typing import Dict 4 | from typing import Hashable 5 | from typing import ItemsView 6 | from typing import Iterator 7 | from typing import List 8 | from typing import Mapping 9 | from typing import Union 10 | 11 | from jsonschema._keywords import allOf as _allOf 12 | from jsonschema._keywords import anyOf as _anyOf 13 | from jsonschema._keywords import oneOf as _oneOf 14 | from jsonschema._utils import extras_msg 15 | from jsonschema._utils import find_additional_properties 16 | from jsonschema.exceptions import FormatError 17 | from jsonschema.exceptions import ValidationError 18 | from jsonschema.protocols import Validator 19 | 20 | 21 | def handle_discriminator( 22 | validator: Validator, _: Any, instance: Any, schema: Mapping[Hashable, Any] 23 | ) -> Iterator[ValidationError]: 24 | """ 25 | Handle presence of discriminator in anyOf, oneOf and allOf. 26 | The behaviour is the same in all 3 cases because at most 1 schema will match. 27 | """ 28 | discriminator = schema["discriminator"] 29 | prop_name = discriminator["propertyName"] 30 | prop_value = instance.get(prop_name) 31 | if not prop_value: 32 | # instance is missing $propertyName 33 | yield ValidationError( 34 | f"{instance!r} does not contain discriminating property {prop_name!r}", 35 | context=[], 36 | ) 37 | return 38 | 39 | # Use explicit mapping if available, otherwise try implicit value 40 | ref = ( 41 | discriminator.get("mapping", {}).get(prop_value) 42 | or f"#/components/schemas/{prop_value}" 43 | ) 44 | 45 | if not isinstance(ref, str): 46 | # this is a schema error 47 | yield ValidationError( 48 | "{!r} mapped value for {!r} should be a string, was {!r}".format( 49 | instance, prop_value, ref 50 | ), 51 | context=[], 52 | ) 53 | return 54 | 55 | try: 56 | validator._validate_reference(ref=ref, instance=instance) 57 | except: 58 | yield ValidationError( 59 | f"{instance!r} reference {ref!r} could not be resolved", 60 | context=[], 61 | ) 62 | return 63 | 64 | yield from validator.descend(instance, {"$ref": ref}) 65 | 66 | 67 | def anyOf( 68 | validator: Validator, 69 | anyOf: List[Mapping[Hashable, Any]], 70 | instance: Any, 71 | schema: Mapping[Hashable, Any], 72 | ) -> Iterator[ValidationError]: 73 | if "discriminator" not in schema: 74 | yield from _anyOf(validator, anyOf, instance, schema) 75 | else: 76 | yield from handle_discriminator(validator, anyOf, instance, schema) 77 | 78 | 79 | def oneOf( 80 | validator: Validator, 81 | oneOf: List[Mapping[Hashable, Any]], 82 | instance: Any, 83 | schema: Mapping[Hashable, Any], 84 | ) -> Iterator[ValidationError]: 85 | if "discriminator" not in schema: 86 | yield from _oneOf(validator, oneOf, instance, schema) 87 | else: 88 | yield from handle_discriminator(validator, oneOf, instance, schema) 89 | 90 | 91 | def allOf( 92 | validator: Validator, 93 | allOf: List[Mapping[Hashable, Any]], 94 | instance: Any, 95 | schema: Mapping[Hashable, Any], 96 | ) -> Iterator[ValidationError]: 97 | if "discriminator" not in schema: 98 | yield from _allOf(validator, allOf, instance, schema) 99 | else: 100 | yield from handle_discriminator(validator, allOf, instance, schema) 101 | 102 | 103 | def type( 104 | validator: Validator, 105 | data_type: str, 106 | instance: Any, 107 | schema: Mapping[Hashable, Any], 108 | ) -> Iterator[ValidationError]: 109 | if instance is None: 110 | # nullable implementation based on OAS 3.0.3 111 | # * nullable is only meaningful if its value is true 112 | # * nullable: true is only meaningful in combination with a type 113 | # assertion specified in the same Schema Object. 114 | # * nullable: true operates within a single Schema Object 115 | if "nullable" in schema and schema["nullable"] == True: 116 | return 117 | yield ValidationError("None for not nullable") 118 | 119 | if not validator.is_type(instance, data_type): 120 | data_repr = repr(data_type) 121 | yield ValidationError(f"{instance!r} is not of type {data_repr}") 122 | 123 | 124 | def format( 125 | validator: Validator, 126 | format: str, 127 | instance: Any, 128 | schema: Mapping[Hashable, Any], 129 | ) -> Iterator[ValidationError]: 130 | if instance is None: 131 | return 132 | 133 | if validator.format_checker is not None: 134 | try: 135 | validator.format_checker.check(instance, format) 136 | except FormatError as error: 137 | yield ValidationError(str(error), cause=error.cause) 138 | 139 | 140 | def items( 141 | validator: Validator, 142 | items: Mapping[Hashable, Any], 143 | instance: Any, 144 | schema: Mapping[Hashable, Any], 145 | ) -> Iterator[ValidationError]: 146 | if not validator.is_type(instance, "array"): 147 | return 148 | 149 | for index, item in enumerate(instance): 150 | yield from validator.descend(item, items, path=index) 151 | 152 | 153 | def required( 154 | validator: Validator, 155 | required: List[str], 156 | instance: Any, 157 | schema: Mapping[Hashable, Any], 158 | ) -> Iterator[ValidationError]: 159 | if not validator.is_type(instance, "object"): 160 | return 161 | for property in required: 162 | if property not in instance: 163 | prop_schema = schema.get("properties", {}).get(property) 164 | if prop_schema: 165 | read_only = prop_schema.get("readOnly", False) 166 | write_only = prop_schema.get("writeOnly", False) 167 | if ( 168 | getattr(validator, "write", True) 169 | and read_only 170 | or getattr(validator, "read", True) 171 | and write_only 172 | ): 173 | continue 174 | yield ValidationError(f"{property!r} is a required property") 175 | 176 | 177 | def read_required( 178 | validator: Validator, 179 | required: List[str], 180 | instance: Any, 181 | schema: Mapping[Hashable, Any], 182 | ) -> Iterator[ValidationError]: 183 | if not validator.is_type(instance, "object"): 184 | return 185 | for property in required: 186 | if property not in instance: 187 | prop_schema = schema.get("properties", {}).get(property) 188 | if prop_schema: 189 | write_only = prop_schema.get("writeOnly", False) 190 | if getattr(validator, "read", True) and write_only: 191 | continue 192 | yield ValidationError(f"{property!r} is a required property") 193 | 194 | 195 | def write_required( 196 | validator: Validator, 197 | required: List[str], 198 | instance: Any, 199 | schema: Mapping[Hashable, Any], 200 | ) -> Iterator[ValidationError]: 201 | if not validator.is_type(instance, "object"): 202 | return 203 | for property in required: 204 | if property not in instance: 205 | prop_schema = schema.get("properties", {}).get(property) 206 | if prop_schema: 207 | read_only = prop_schema.get("readOnly", False) 208 | if read_only: 209 | continue 210 | yield ValidationError(f"{property!r} is a required property") 211 | 212 | 213 | def additionalProperties( 214 | validator: Validator, 215 | aP: Union[Mapping[Hashable, Any], bool], 216 | instance: Any, 217 | schema: Mapping[Hashable, Any], 218 | ) -> Iterator[ValidationError]: 219 | if not validator.is_type(instance, "object"): 220 | return 221 | 222 | extras = set(find_additional_properties(instance, schema)) 223 | 224 | if not extras: 225 | return 226 | 227 | if validator.is_type(aP, "object"): 228 | for extra in extras: 229 | for error in validator.descend(instance[extra], aP, path=extra): 230 | yield error 231 | elif validator.is_type(aP, "boolean"): 232 | if not aP: 233 | error = "Additional properties are not allowed (%s %s unexpected)" 234 | yield ValidationError(error % extras_msg(extras)) 235 | 236 | 237 | def write_readOnly( 238 | validator: Validator, 239 | ro: bool, 240 | instance: Any, 241 | schema: Mapping[Hashable, Any], 242 | ) -> Iterator[ValidationError]: 243 | yield ValidationError(f"Tried to write read-only property with {instance}") 244 | 245 | 246 | def read_writeOnly( 247 | validator: Validator, 248 | wo: bool, 249 | instance: Any, 250 | schema: Mapping[Hashable, Any], 251 | ) -> Iterator[ValidationError]: 252 | yield ValidationError(f"Tried to read write-only property with {instance}") 253 | 254 | 255 | def not_implemented( 256 | validator: Validator, 257 | value: Any, 258 | instance: Any, 259 | schema: Mapping[Hashable, Any], 260 | ) -> Iterator[ValidationError]: 261 | return 262 | yield 263 | -------------------------------------------------------------------------------- /openapi_schema_validator/_types.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from jsonschema._types import TypeChecker 4 | from jsonschema._types import draft202012_type_checker 5 | from jsonschema._types import is_array 6 | from jsonschema._types import is_bool 7 | from jsonschema._types import is_integer 8 | from jsonschema._types import is_number 9 | from jsonschema._types import is_object 10 | 11 | 12 | def is_string(checker: TypeChecker, instance: Any) -> bool: 13 | return isinstance(instance, (str, bytes)) 14 | 15 | 16 | oas30_type_checker = TypeChecker( 17 | { 18 | "string": is_string, 19 | "number": is_number, 20 | "integer": is_integer, 21 | "boolean": is_bool, 22 | "array": is_array, 23 | "object": is_object, 24 | }, 25 | ) 26 | oas31_type_checker = draft202012_type_checker 27 | -------------------------------------------------------------------------------- /openapi_schema_validator/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-openapi/openapi-schema-validator/bf12c9bf5130fac2768d13a9010ede8fa7ec2aa4/openapi_schema_validator/py.typed -------------------------------------------------------------------------------- /openapi_schema_validator/shortcuts.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from typing import Hashable 3 | from typing import Mapping 4 | from typing import Type 5 | 6 | from jsonschema.exceptions import best_match 7 | from jsonschema.protocols import Validator 8 | 9 | from openapi_schema_validator.validators import OAS31Validator 10 | 11 | 12 | def validate( 13 | instance: Any, 14 | schema: Mapping[Hashable, Any], 15 | cls: Type[Validator] = OAS31Validator, 16 | *args: Any, 17 | **kwargs: Any 18 | ) -> None: 19 | cls.check_schema(schema) 20 | validator = cls(schema, *args, **kwargs) 21 | error = best_match(validator.evolve(schema=schema).iter_errors(instance)) 22 | if error is not None: 23 | raise error 24 | -------------------------------------------------------------------------------- /openapi_schema_validator/validators.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | from typing import Any 3 | from typing import Type 4 | 5 | from jsonschema import _keywords 6 | from jsonschema import _legacy_keywords 7 | from jsonschema.validators import Draft202012Validator 8 | from jsonschema.validators import create 9 | from jsonschema.validators import extend 10 | from jsonschema_specifications import REGISTRY as SPECIFICATIONS 11 | 12 | from openapi_schema_validator import _format as oas_format 13 | from openapi_schema_validator import _keywords as oas_keywords 14 | from openapi_schema_validator import _types as oas_types 15 | from openapi_schema_validator._types import oas31_type_checker 16 | 17 | OAS30Validator = create( 18 | meta_schema=SPECIFICATIONS.contents( 19 | "http://json-schema.org/draft-04/schema#", 20 | ), 21 | validators={ 22 | "multipleOf": _keywords.multipleOf, 23 | # exclusiveMaximum supported inside maximum_draft3_draft4 24 | "maximum": _legacy_keywords.maximum_draft3_draft4, 25 | # exclusiveMinimum supported inside minimum_draft3_draft4 26 | "minimum": _legacy_keywords.minimum_draft3_draft4, 27 | "maxLength": _keywords.maxLength, 28 | "minLength": _keywords.minLength, 29 | "pattern": _keywords.pattern, 30 | "maxItems": _keywords.maxItems, 31 | "minItems": _keywords.minItems, 32 | "uniqueItems": _keywords.uniqueItems, 33 | "maxProperties": _keywords.maxProperties, 34 | "minProperties": _keywords.minProperties, 35 | "enum": _keywords.enum, 36 | # adjusted to OAS 37 | "type": oas_keywords.type, 38 | "allOf": oas_keywords.allOf, 39 | "oneOf": oas_keywords.oneOf, 40 | "anyOf": oas_keywords.anyOf, 41 | "not": _keywords.not_, 42 | "items": oas_keywords.items, 43 | "properties": _keywords.properties, 44 | "required": oas_keywords.required, 45 | "additionalProperties": oas_keywords.additionalProperties, 46 | # TODO: adjust description 47 | "format": oas_keywords.format, 48 | # TODO: adjust default 49 | "$ref": _keywords.ref, 50 | # fixed OAS fields 51 | "discriminator": oas_keywords.not_implemented, 52 | "readOnly": oas_keywords.not_implemented, 53 | "writeOnly": oas_keywords.not_implemented, 54 | "xml": oas_keywords.not_implemented, 55 | "externalDocs": oas_keywords.not_implemented, 56 | "example": oas_keywords.not_implemented, 57 | "deprecated": oas_keywords.not_implemented, 58 | }, 59 | type_checker=oas_types.oas30_type_checker, 60 | format_checker=oas_format.oas30_format_checker, 61 | # NOTE: version causes conflict with global jsonschema validator 62 | # See https://github.com/python-openapi/openapi-schema-validator/pull/12 63 | # version="oas30", 64 | id_of=lambda schema: schema.get("id", ""), 65 | ) 66 | 67 | OAS30ReadValidator = extend( 68 | OAS30Validator, 69 | validators={ 70 | "required": oas_keywords.read_required, 71 | "writeOnly": oas_keywords.read_writeOnly, 72 | }, 73 | ) 74 | OAS30WriteValidator = extend( 75 | OAS30Validator, 76 | validators={ 77 | "required": oas_keywords.write_required, 78 | "readOnly": oas_keywords.write_readOnly, 79 | }, 80 | ) 81 | 82 | OAS31Validator = extend( 83 | Draft202012Validator, 84 | { 85 | # adjusted to OAS 86 | "allOf": oas_keywords.allOf, 87 | "oneOf": oas_keywords.oneOf, 88 | "anyOf": oas_keywords.anyOf, 89 | "description": oas_keywords.not_implemented, 90 | # fixed OAS fields 91 | "discriminator": oas_keywords.not_implemented, 92 | "xml": oas_keywords.not_implemented, 93 | "externalDocs": oas_keywords.not_implemented, 94 | "example": oas_keywords.not_implemented, 95 | }, 96 | type_checker=oas31_type_checker, 97 | format_checker=oas_format.oas31_format_checker, 98 | ) 99 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "alabaster" 5 | version = "0.7.16" 6 | description = "A light, configurable Sphinx theme" 7 | optional = false 8 | python-versions = ">=3.9" 9 | groups = ["docs"] 10 | files = [ 11 | {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, 12 | {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, 13 | ] 14 | 15 | [[package]] 16 | name = "annotated-types" 17 | version = "0.7.0" 18 | description = "Reusable constraint types to use with typing.Annotated" 19 | optional = false 20 | python-versions = ">=3.8" 21 | groups = ["docs"] 22 | files = [ 23 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, 24 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, 25 | ] 26 | 27 | [[package]] 28 | name = "appdirs" 29 | version = "1.4.4" 30 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 31 | optional = false 32 | python-versions = "*" 33 | groups = ["docs"] 34 | files = [ 35 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, 36 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, 37 | ] 38 | 39 | [[package]] 40 | name = "astor" 41 | version = "0.8.1" 42 | description = "Read/rewrite/write Python ASTs" 43 | optional = false 44 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" 45 | groups = ["dev"] 46 | files = [ 47 | {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, 48 | {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, 49 | ] 50 | 51 | [[package]] 52 | name = "attrs" 53 | version = "24.2.0" 54 | description = "Classes Without Boilerplate" 55 | optional = false 56 | python-versions = ">=3.7" 57 | groups = ["main"] 58 | files = [ 59 | {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, 60 | {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, 61 | ] 62 | 63 | [package.extras] 64 | benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 65 | cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 66 | dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 67 | docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] 68 | tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 69 | tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] 70 | 71 | [[package]] 72 | name = "babel" 73 | version = "2.16.0" 74 | description = "Internationalization utilities" 75 | optional = false 76 | python-versions = ">=3.8" 77 | groups = ["docs"] 78 | files = [ 79 | {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, 80 | {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, 81 | ] 82 | 83 | [package.extras] 84 | dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] 85 | 86 | [[package]] 87 | name = "black" 88 | version = "24.10.0" 89 | description = "The uncompromising code formatter." 90 | optional = false 91 | python-versions = ">=3.9" 92 | groups = ["dev"] 93 | files = [ 94 | {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, 95 | {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, 96 | {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, 97 | {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, 98 | {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, 99 | {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, 100 | {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, 101 | {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, 102 | {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, 103 | {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, 104 | {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, 105 | {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, 106 | {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, 107 | {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, 108 | {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, 109 | {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, 110 | {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, 111 | {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, 112 | {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, 113 | {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, 114 | {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, 115 | {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, 116 | ] 117 | 118 | [package.dependencies] 119 | click = ">=8.0.0" 120 | mypy-extensions = ">=0.4.3" 121 | packaging = ">=22.0" 122 | pathspec = ">=0.9.0" 123 | platformdirs = ">=2" 124 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 125 | typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} 126 | 127 | [package.extras] 128 | colorama = ["colorama (>=0.4.3)"] 129 | d = ["aiohttp (>=3.10)"] 130 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 131 | uvloop = ["uvloop (>=0.15.2)"] 132 | 133 | [[package]] 134 | name = "bump2version" 135 | version = "1.0.1" 136 | description = "Version-bump your software with a single command!" 137 | optional = false 138 | python-versions = ">=3.5" 139 | groups = ["dev"] 140 | files = [ 141 | {file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"}, 142 | {file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"}, 143 | ] 144 | 145 | [[package]] 146 | name = "certifi" 147 | version = "2024.8.30" 148 | description = "Python package for providing Mozilla's CA Bundle." 149 | optional = false 150 | python-versions = ">=3.6" 151 | groups = ["docs"] 152 | files = [ 153 | {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, 154 | {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, 155 | ] 156 | 157 | [[package]] 158 | name = "cfgv" 159 | version = "3.4.0" 160 | description = "Validate configuration and produce human readable error messages." 161 | optional = false 162 | python-versions = ">=3.8" 163 | groups = ["dev"] 164 | files = [ 165 | {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, 166 | {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, 167 | ] 168 | 169 | [[package]] 170 | name = "charset-normalizer" 171 | version = "3.4.0" 172 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 173 | optional = false 174 | python-versions = ">=3.7.0" 175 | groups = ["docs"] 176 | files = [ 177 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, 178 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, 179 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, 180 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, 181 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, 182 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, 183 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, 184 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, 185 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, 186 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, 187 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, 188 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, 189 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, 190 | {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, 191 | {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, 192 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, 193 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, 194 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, 195 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, 196 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, 197 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, 198 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, 199 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, 200 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, 201 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, 202 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, 203 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, 204 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, 205 | {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, 206 | {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, 207 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, 208 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, 209 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, 210 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, 211 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, 212 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, 213 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, 214 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, 215 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, 216 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, 217 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, 218 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, 219 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, 220 | {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, 221 | {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, 222 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, 223 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, 224 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, 225 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, 226 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, 227 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, 228 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, 229 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, 230 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, 231 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, 232 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, 233 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, 234 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, 235 | {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, 236 | {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, 237 | {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, 238 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, 239 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, 240 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, 241 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, 242 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, 243 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, 244 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, 245 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, 246 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, 247 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, 248 | {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, 249 | {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, 250 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, 251 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, 252 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, 253 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, 254 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, 255 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, 256 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, 257 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, 258 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, 259 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, 260 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, 261 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, 262 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, 263 | {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, 264 | {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, 265 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, 266 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, 267 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, 268 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, 269 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, 270 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, 271 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, 272 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, 273 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, 274 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, 275 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, 276 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, 277 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, 278 | {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, 279 | {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, 280 | {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, 281 | {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, 282 | ] 283 | 284 | [[package]] 285 | name = "click" 286 | version = "8.1.7" 287 | description = "Composable command line interface toolkit" 288 | optional = false 289 | python-versions = ">=3.7" 290 | groups = ["dev"] 291 | files = [ 292 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 293 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 294 | ] 295 | 296 | [package.dependencies] 297 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 298 | 299 | [[package]] 300 | name = "colorama" 301 | version = "0.4.6" 302 | description = "Cross-platform colored terminal text." 303 | optional = false 304 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 305 | groups = ["dev", "docs"] 306 | files = [ 307 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 308 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 309 | ] 310 | markers = {dev = "platform_system == \"Windows\" or sys_platform == \"win32\"", docs = "sys_platform == \"win32\""} 311 | 312 | [[package]] 313 | name = "coverage" 314 | version = "7.6.3" 315 | description = "Code coverage measurement for Python" 316 | optional = false 317 | python-versions = ">=3.9" 318 | groups = ["dev"] 319 | files = [ 320 | {file = "coverage-7.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6da42bbcec130b188169107ecb6ee7bd7b4c849d24c9370a0c884cf728d8e976"}, 321 | {file = "coverage-7.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c222958f59b0ae091f4535851cbb24eb57fc0baea07ba675af718fb5302dddb2"}, 322 | {file = "coverage-7.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab84a8b698ad5a6c365b08061920138e7a7dd9a04b6feb09ba1bfae68346ce6d"}, 323 | {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70a6756ce66cd6fe8486c775b30889f0dc4cb20c157aa8c35b45fd7868255c5c"}, 324 | {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c2e6fa98032fec8282f6b27e3f3986c6e05702828380618776ad794e938f53a"}, 325 | {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:921fbe13492caf6a69528f09d5d7c7d518c8d0e7b9f6701b7719715f29a71e6e"}, 326 | {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6d99198203f0b9cb0b5d1c0393859555bc26b548223a769baf7e321a627ed4fc"}, 327 | {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:87cd2e29067ea397a47e352efb13f976eb1b03e18c999270bb50589323294c6e"}, 328 | {file = "coverage-7.6.3-cp310-cp310-win32.whl", hash = "sha256:a3328c3e64ea4ab12b85999eb0779e6139295bbf5485f69d42cf794309e3d007"}, 329 | {file = "coverage-7.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bca4c8abc50d38f9773c1ec80d43f3768df2e8576807d1656016b9d3eeaa96fd"}, 330 | {file = "coverage-7.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c51ef82302386d686feea1c44dbeef744585da16fcf97deea2a8d6c1556f519b"}, 331 | {file = "coverage-7.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ca37993206402c6c35dc717f90d4c8f53568a8b80f0bf1a1b2b334f4d488fba"}, 332 | {file = "coverage-7.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c77326300b839c44c3e5a8fe26c15b7e87b2f32dfd2fc9fee1d13604347c9b38"}, 333 | {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e484e479860e00da1f005cd19d1c5d4a813324e5951319ac3f3eefb497cc549"}, 334 | {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c6c0f4d53ef603397fc894a895b960ecd7d44c727df42a8d500031716d4e8d2"}, 335 | {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37be7b5ea3ff5b7c4a9db16074dc94523b5f10dd1f3b362a827af66a55198175"}, 336 | {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:43b32a06c47539fe275106b376658638b418c7cfdfff0e0259fbf877e845f14b"}, 337 | {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee77c7bef0724165e795b6b7bf9c4c22a9b8468a6bdb9c6b4281293c6b22a90f"}, 338 | {file = "coverage-7.6.3-cp311-cp311-win32.whl", hash = "sha256:43517e1f6b19f610a93d8227e47790722c8bf7422e46b365e0469fc3d3563d97"}, 339 | {file = "coverage-7.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:04f2189716e85ec9192df307f7c255f90e78b6e9863a03223c3b998d24a3c6c6"}, 340 | {file = "coverage-7.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27bd5f18d8f2879e45724b0ce74f61811639a846ff0e5c0395b7818fae87aec6"}, 341 | {file = "coverage-7.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d546cfa78844b8b9c1c0533de1851569a13f87449897bbc95d698d1d3cb2a30f"}, 342 | {file = "coverage-7.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9975442f2e7a5cfcf87299c26b5a45266ab0696348420049b9b94b2ad3d40234"}, 343 | {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:583049c63106c0555e3ae3931edab5669668bbef84c15861421b94e121878d3f"}, 344 | {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2341a78ae3a5ed454d524206a3fcb3cec408c2a0c7c2752cd78b606a2ff15af4"}, 345 | {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a4fb91d5f72b7e06a14ff4ae5be625a81cd7e5f869d7a54578fc271d08d58ae3"}, 346 | {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e279f3db904e3b55f520f11f983cc8dc8a4ce9b65f11692d4718ed021ec58b83"}, 347 | {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa23ce39661a3e90eea5f99ec59b763b7d655c2cada10729ed920a38bfc2b167"}, 348 | {file = "coverage-7.6.3-cp312-cp312-win32.whl", hash = "sha256:52ac29cc72ee7e25ace7807249638f94c9b6a862c56b1df015d2b2e388e51dbd"}, 349 | {file = "coverage-7.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:40e8b1983080439d4802d80b951f4a93d991ef3261f69e81095a66f86cf3c3c6"}, 350 | {file = "coverage-7.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9134032f5aa445ae591c2ba6991d10136a1f533b1d2fa8f8c21126468c5025c6"}, 351 | {file = "coverage-7.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99670790f21a96665a35849990b1df447993880bb6463a0a1d757897f30da929"}, 352 | {file = "coverage-7.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc7d6b380ca76f5e817ac9eef0c3686e7834c8346bef30b041a4ad286449990"}, 353 | {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7b26757b22faf88fcf232f5f0e62f6e0fd9e22a8a5d0d5016888cdfe1f6c1c4"}, 354 | {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c59d6a4a4633fad297f943c03d0d2569867bd5372eb5684befdff8df8522e39"}, 355 | {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f263b18692f8ed52c8de7f40a0751e79015983dbd77b16906e5b310a39d3ca21"}, 356 | {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79644f68a6ff23b251cae1c82b01a0b51bc40c8468ca9585c6c4b1aeee570e0b"}, 357 | {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71967c35828c9ff94e8c7d405469a1fb68257f686bca7c1ed85ed34e7c2529c4"}, 358 | {file = "coverage-7.6.3-cp313-cp313-win32.whl", hash = "sha256:e266af4da2c1a4cbc6135a570c64577fd3e6eb204607eaff99d8e9b710003c6f"}, 359 | {file = "coverage-7.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:ea52bd218d4ba260399a8ae4bb6b577d82adfc4518b93566ce1fddd4a49d1dce"}, 360 | {file = "coverage-7.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8d4c6ea0f498c7c79111033a290d060c517853a7bcb2f46516f591dab628ddd3"}, 361 | {file = "coverage-7.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:331b200ad03dbaa44151d74daeb7da2cf382db424ab923574f6ecca7d3b30de3"}, 362 | {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54356a76b67cf8a3085818026bb556545ebb8353951923b88292556dfa9f812d"}, 363 | {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebec65f5068e7df2d49466aab9128510c4867e532e07cb6960075b27658dca38"}, 364 | {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33a785ea8354c480515e781554d3be582a86297e41ccbea627a5c632647f2cd"}, 365 | {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f7ddb920106bbbbcaf2a274d56f46956bf56ecbde210d88061824a95bdd94e92"}, 366 | {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:70d24936ca6c15a3bbc91ee9c7fc661132c6f4c9d42a23b31b6686c05073bde5"}, 367 | {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c30e42ea11badb147f0d2e387115b15e2bd8205a5ad70d6ad79cf37f6ac08c91"}, 368 | {file = "coverage-7.6.3-cp313-cp313t-win32.whl", hash = "sha256:365defc257c687ce3e7d275f39738dcd230777424117a6c76043459db131dd43"}, 369 | {file = "coverage-7.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:23bb63ae3f4c645d2d82fa22697364b0046fbafb6261b258a58587441c5f7bd0"}, 370 | {file = "coverage-7.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da29ceabe3025a1e5a5aeeb331c5b1af686daab4ff0fb4f83df18b1180ea83e2"}, 371 | {file = "coverage-7.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df8c05a0f574d480947cba11b947dc41b1265d721c3777881da2fb8d3a1ddfba"}, 372 | {file = "coverage-7.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1e3b40b82236d100d259854840555469fad4db64f669ab817279eb95cd535c"}, 373 | {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4adeb878a374126f1e5cf03b87f66279f479e01af0e9a654cf6d1509af46c40"}, 374 | {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43d6a66e33b1455b98fc7312b124296dad97a2e191c80320587234a77b1b736e"}, 375 | {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1990b1f4e2c402beb317840030bb9f1b6a363f86e14e21b4212e618acdfce7f6"}, 376 | {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:12f9515d875859faedb4144fd38694a761cd2a61ef9603bf887b13956d0bbfbb"}, 377 | {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99ded130555c021d99729fabd4ddb91a6f4cc0707df4b1daf912c7850c373b13"}, 378 | {file = "coverage-7.6.3-cp39-cp39-win32.whl", hash = "sha256:c3a79f56dee9136084cf84a6c7c4341427ef36e05ae6415bf7d787c96ff5eaa3"}, 379 | {file = "coverage-7.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:aac7501ae73d4a02f4b7ac8fcb9dc55342ca98ffb9ed9f2dfb8a25d53eda0e4d"}, 380 | {file = "coverage-7.6.3-pp39.pp310-none-any.whl", hash = "sha256:b9853509b4bf57ba7b1f99b9d866c422c9c5248799ab20e652bbb8a184a38181"}, 381 | {file = "coverage-7.6.3.tar.gz", hash = "sha256:bb7d5fe92bd0dc235f63ebe9f8c6e0884f7360f88f3411bfed1350c872ef2054"}, 382 | ] 383 | 384 | [package.dependencies] 385 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 386 | 387 | [package.extras] 388 | toml = ["tomli ; python_full_version <= \"3.11.0a6\""] 389 | 390 | [[package]] 391 | name = "deptry" 392 | version = "0.16.2" 393 | description = "A command line utility to check for unused, missing and transitive dependencies in a Python project." 394 | optional = false 395 | python-versions = ">=3.8" 396 | groups = ["dev"] 397 | files = [ 398 | {file = "deptry-0.16.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:24bfbae07bd6533c852c795e8d88d05a8ad0801bec0d3662e1a37db763c52540"}, 399 | {file = "deptry-0.16.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc881688a2eaeafe51c0617d32a6535057bccdb74559cc667109f48f81cd976e"}, 400 | {file = "deptry-0.16.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fed4b692f556e4c80acb42cec93e3b5fdc7fc2323049c2a0cfd9dfc4a9c7033e"}, 401 | {file = "deptry-0.16.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ec508a932d8f06c3bd1aa7a4548d5dbec92c3060d42eedcda3be9729bd7c3b"}, 402 | {file = "deptry-0.16.2-cp38-abi3-win_amd64.whl", hash = "sha256:eb92e9aacde66cfe001d6318eb0851ae0ca26fea441defed4765a47644daf8bb"}, 403 | {file = "deptry-0.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dfdceca2fbc87f4bce04df4207914a5eb37e67fb2107579ad2e88107c22d2456"}, 404 | {file = "deptry-0.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:96ab62dd5f4658735aac72d0e49f6d896eabf50a0e4e2cdecb436a1362aa696b"}, 405 | {file = "deptry-0.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e4408fa5a8d146b55bc40f0829fb875efef33174a2679bd9954ce988b9bc0d7"}, 406 | {file = "deptry-0.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af976afc2a0583f48dc25f616d2566fecd7af5080675c8eccb161def88d93503"}, 407 | {file = "deptry-0.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd86c9d34aa75b91fb72b34110f0660b2277bf9a95fe9cae3ead36d465bc44ac"}, 408 | {file = "deptry-0.16.2.tar.gz", hash = "sha256:f0f752cf6f5e9f7445a79fcf195b772cd2d4b889cd260e23867dd8013caa74c1"}, 409 | ] 410 | 411 | [package.dependencies] 412 | click = ">=8.0.0,<9" 413 | colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} 414 | tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} 415 | 416 | [[package]] 417 | name = "distlib" 418 | version = "0.3.9" 419 | description = "Distribution utilities" 420 | optional = false 421 | python-versions = "*" 422 | groups = ["dev"] 423 | files = [ 424 | {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, 425 | {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, 426 | ] 427 | 428 | [[package]] 429 | name = "docutils" 430 | version = "0.21.2" 431 | description = "Docutils -- Python Documentation Utilities" 432 | optional = false 433 | python-versions = ">=3.9" 434 | groups = ["docs"] 435 | files = [ 436 | {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, 437 | {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, 438 | ] 439 | 440 | [[package]] 441 | name = "exceptiongroup" 442 | version = "1.2.2" 443 | description = "Backport of PEP 654 (exception groups)" 444 | optional = false 445 | python-versions = ">=3.7" 446 | groups = ["dev"] 447 | markers = "python_version < \"3.11\"" 448 | files = [ 449 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 450 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 451 | ] 452 | 453 | [package.extras] 454 | test = ["pytest (>=6)"] 455 | 456 | [[package]] 457 | name = "filelock" 458 | version = "3.16.1" 459 | description = "A platform independent file lock." 460 | optional = false 461 | python-versions = ">=3.8" 462 | groups = ["dev"] 463 | files = [ 464 | {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, 465 | {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, 466 | ] 467 | 468 | [package.extras] 469 | docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] 470 | testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] 471 | typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] 472 | 473 | [[package]] 474 | name = "flake8" 475 | version = "5.0.4" 476 | description = "the modular source code checker: pep8 pyflakes and co" 477 | optional = false 478 | python-versions = ">=3.6.1" 479 | groups = ["dev"] 480 | files = [ 481 | {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, 482 | {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, 483 | ] 484 | 485 | [package.dependencies] 486 | mccabe = ">=0.7.0,<0.8.0" 487 | pycodestyle = ">=2.9.0,<2.10.0" 488 | pyflakes = ">=2.5.0,<2.6.0" 489 | 490 | [[package]] 491 | name = "flynt" 492 | version = "1.0.1" 493 | description = "CLI tool to convert a python project's %-formatted strings to f-strings." 494 | optional = false 495 | python-versions = ">=3.7" 496 | groups = ["dev"] 497 | files = [ 498 | {file = "flynt-1.0.1-py3-none-any.whl", hash = "sha256:65d1c546434827275123222a98408e9561bcd67db832dd58f530ff17b8329ec1"}, 499 | {file = "flynt-1.0.1.tar.gz", hash = "sha256:988aac00672a5469726cc0a17cef7d1178c284a9fe8563458db2475d0aaed965"}, 500 | ] 501 | 502 | [package.dependencies] 503 | astor = "*" 504 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 505 | 506 | [package.extras] 507 | dev = ["build", "pre-commit", "pytest", "pytest-cov", "twine"] 508 | 509 | [[package]] 510 | name = "identify" 511 | version = "2.6.1" 512 | description = "File identification library for Python" 513 | optional = false 514 | python-versions = ">=3.8" 515 | groups = ["dev"] 516 | files = [ 517 | {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, 518 | {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, 519 | ] 520 | 521 | [package.extras] 522 | license = ["ukkonen"] 523 | 524 | [[package]] 525 | name = "idna" 526 | version = "3.10" 527 | description = "Internationalized Domain Names in Applications (IDNA)" 528 | optional = false 529 | python-versions = ">=3.6" 530 | groups = ["docs"] 531 | files = [ 532 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 533 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 534 | ] 535 | 536 | [package.extras] 537 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 538 | 539 | [[package]] 540 | name = "imagesize" 541 | version = "1.4.1" 542 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 543 | optional = false 544 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 545 | groups = ["docs"] 546 | files = [ 547 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 548 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 549 | ] 550 | 551 | [[package]] 552 | name = "importlib-metadata" 553 | version = "8.5.0" 554 | description = "Read metadata from Python packages" 555 | optional = false 556 | python-versions = ">=3.8" 557 | groups = ["docs"] 558 | markers = "python_version < \"3.10\"" 559 | files = [ 560 | {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, 561 | {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, 562 | ] 563 | 564 | [package.dependencies] 565 | zipp = ">=3.20" 566 | 567 | [package.extras] 568 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 569 | cover = ["pytest-cov"] 570 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 571 | enabler = ["pytest-enabler (>=2.2)"] 572 | perf = ["ipython"] 573 | test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] 574 | type = ["pytest-mypy"] 575 | 576 | [[package]] 577 | name = "iniconfig" 578 | version = "2.0.0" 579 | description = "brain-dead simple config-ini parsing" 580 | optional = false 581 | python-versions = ">=3.7" 582 | groups = ["dev"] 583 | files = [ 584 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 585 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 586 | ] 587 | 588 | [[package]] 589 | name = "isort" 590 | version = "5.13.2" 591 | description = "A Python utility / library to sort Python imports." 592 | optional = false 593 | python-versions = ">=3.8.0" 594 | groups = ["dev"] 595 | files = [ 596 | {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, 597 | {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, 598 | ] 599 | 600 | [package.extras] 601 | colors = ["colorama (>=0.4.6)"] 602 | 603 | [[package]] 604 | name = "jinja2" 605 | version = "3.1.6" 606 | description = "A very fast and expressive template engine." 607 | optional = false 608 | python-versions = ">=3.7" 609 | groups = ["dev", "docs"] 610 | files = [ 611 | {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, 612 | {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, 613 | ] 614 | 615 | [package.dependencies] 616 | MarkupSafe = ">=2.0" 617 | 618 | [package.extras] 619 | i18n = ["Babel (>=2.7)"] 620 | 621 | [[package]] 622 | name = "jsonschema" 623 | version = "4.23.0" 624 | description = "An implementation of JSON Schema validation for Python" 625 | optional = false 626 | python-versions = ">=3.8" 627 | groups = ["main"] 628 | files = [ 629 | {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, 630 | {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, 631 | ] 632 | 633 | [package.dependencies] 634 | attrs = ">=22.2.0" 635 | jsonschema-specifications = ">=2023.03.6" 636 | referencing = ">=0.28.4" 637 | rpds-py = ">=0.7.1" 638 | 639 | [package.extras] 640 | format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] 641 | format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] 642 | 643 | [[package]] 644 | name = "jsonschema-specifications" 645 | version = "2024.10.1" 646 | description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" 647 | optional = false 648 | python-versions = ">=3.9" 649 | groups = ["main"] 650 | files = [ 651 | {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, 652 | {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, 653 | ] 654 | 655 | [package.dependencies] 656 | referencing = ">=0.31.0" 657 | 658 | [[package]] 659 | name = "markupsafe" 660 | version = "3.0.1" 661 | description = "Safely add untrusted strings to HTML/XML markup." 662 | optional = false 663 | python-versions = ">=3.9" 664 | groups = ["dev", "docs"] 665 | files = [ 666 | {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, 667 | {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, 668 | {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, 669 | {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, 670 | {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, 671 | {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, 672 | {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, 673 | {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, 674 | {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, 675 | {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, 676 | {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, 677 | {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, 678 | {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, 679 | {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, 680 | {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, 681 | {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, 682 | {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, 683 | {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, 684 | {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, 685 | {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, 686 | {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, 687 | {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, 688 | {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, 689 | {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, 690 | {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, 691 | {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, 692 | {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, 693 | {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, 694 | {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, 695 | {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, 696 | {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, 697 | {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, 698 | {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, 699 | {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, 700 | {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, 701 | {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, 702 | {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, 703 | {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, 704 | {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, 705 | {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, 706 | {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, 707 | {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, 708 | {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, 709 | {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, 710 | {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, 711 | {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, 712 | {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, 713 | {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, 714 | {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, 715 | {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, 716 | {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, 717 | {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, 718 | {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, 719 | {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, 720 | {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, 721 | {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, 722 | {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, 723 | {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, 724 | {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, 725 | {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, 726 | {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, 727 | ] 728 | 729 | [[package]] 730 | name = "mccabe" 731 | version = "0.7.0" 732 | description = "McCabe checker, plugin for flake8" 733 | optional = false 734 | python-versions = ">=3.6" 735 | groups = ["dev"] 736 | files = [ 737 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 738 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 739 | ] 740 | 741 | [[package]] 742 | name = "mypy" 743 | version = "1.14.1" 744 | description = "Optional static typing for Python" 745 | optional = false 746 | python-versions = ">=3.8" 747 | groups = ["dev"] 748 | files = [ 749 | {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, 750 | {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, 751 | {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, 752 | {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, 753 | {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, 754 | {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, 755 | {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, 756 | {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, 757 | {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, 758 | {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, 759 | {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, 760 | {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, 761 | {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, 762 | {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, 763 | {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, 764 | {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, 765 | {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, 766 | {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, 767 | {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, 768 | {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, 769 | {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, 770 | {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, 771 | {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, 772 | {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, 773 | {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, 774 | {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, 775 | {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, 776 | {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, 777 | {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, 778 | {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, 779 | {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, 780 | {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, 781 | {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, 782 | {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, 783 | {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, 784 | {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, 785 | {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, 786 | {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, 787 | ] 788 | 789 | [package.dependencies] 790 | mypy_extensions = ">=1.0.0" 791 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 792 | typing_extensions = ">=4.6.0" 793 | 794 | [package.extras] 795 | dmypy = ["psutil (>=4.0)"] 796 | faster-cache = ["orjson"] 797 | install-types = ["pip"] 798 | mypyc = ["setuptools (>=50)"] 799 | reports = ["lxml"] 800 | 801 | [[package]] 802 | name = "mypy-extensions" 803 | version = "1.0.0" 804 | description = "Type system extensions for programs checked with the mypy type checker." 805 | optional = false 806 | python-versions = ">=3.5" 807 | groups = ["dev"] 808 | files = [ 809 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 810 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 811 | ] 812 | 813 | [[package]] 814 | name = "nodeenv" 815 | version = "1.9.1" 816 | description = "Node.js virtual environment builder" 817 | optional = false 818 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 819 | groups = ["dev"] 820 | files = [ 821 | {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, 822 | {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, 823 | ] 824 | 825 | [[package]] 826 | name = "packaging" 827 | version = "24.1" 828 | description = "Core utilities for Python packages" 829 | optional = false 830 | python-versions = ">=3.8" 831 | groups = ["dev", "docs"] 832 | files = [ 833 | {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, 834 | {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, 835 | ] 836 | 837 | [[package]] 838 | name = "pathspec" 839 | version = "0.12.1" 840 | description = "Utility library for gitignore style pattern matching of file paths." 841 | optional = false 842 | python-versions = ">=3.8" 843 | groups = ["dev"] 844 | files = [ 845 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 846 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 847 | ] 848 | 849 | [[package]] 850 | name = "platformdirs" 851 | version = "4.3.6" 852 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 853 | optional = false 854 | python-versions = ">=3.8" 855 | groups = ["dev"] 856 | files = [ 857 | {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, 858 | {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, 859 | ] 860 | 861 | [package.extras] 862 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] 863 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] 864 | type = ["mypy (>=1.11.2)"] 865 | 866 | [[package]] 867 | name = "pluggy" 868 | version = "1.5.0" 869 | description = "plugin and hook calling mechanisms for python" 870 | optional = false 871 | python-versions = ">=3.8" 872 | groups = ["dev"] 873 | files = [ 874 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 875 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 876 | ] 877 | 878 | [package.extras] 879 | dev = ["pre-commit", "tox"] 880 | testing = ["pytest", "pytest-benchmark"] 881 | 882 | [[package]] 883 | name = "pre-commit" 884 | version = "4.1.0" 885 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 886 | optional = false 887 | python-versions = ">=3.9" 888 | groups = ["dev"] 889 | files = [ 890 | {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, 891 | {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, 892 | ] 893 | 894 | [package.dependencies] 895 | cfgv = ">=2.0.0" 896 | identify = ">=1.0.0" 897 | nodeenv = ">=0.11.1" 898 | pyyaml = ">=5.1" 899 | virtualenv = ">=20.10.0" 900 | 901 | [[package]] 902 | name = "pycodestyle" 903 | version = "2.9.1" 904 | description = "Python style guide checker" 905 | optional = false 906 | python-versions = ">=3.6" 907 | groups = ["dev"] 908 | files = [ 909 | {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, 910 | {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, 911 | ] 912 | 913 | [[package]] 914 | name = "pydantic" 915 | version = "2.9.2" 916 | description = "Data validation using Python type hints" 917 | optional = false 918 | python-versions = ">=3.8" 919 | groups = ["docs"] 920 | files = [ 921 | {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, 922 | {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, 923 | ] 924 | 925 | [package.dependencies] 926 | annotated-types = ">=0.6.0" 927 | pydantic-core = "2.23.4" 928 | typing-extensions = [ 929 | {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, 930 | {version = ">=4.6.1", markers = "python_version < \"3.13\""}, 931 | ] 932 | 933 | [package.extras] 934 | email = ["email-validator (>=2.0.0)"] 935 | timezone = ["tzdata ; python_version >= \"3.9\" and sys_platform == \"win32\""] 936 | 937 | [[package]] 938 | name = "pydantic-core" 939 | version = "2.23.4" 940 | description = "Core functionality for Pydantic validation and serialization" 941 | optional = false 942 | python-versions = ">=3.8" 943 | groups = ["docs"] 944 | files = [ 945 | {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, 946 | {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, 947 | {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, 948 | {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, 949 | {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, 950 | {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, 951 | {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, 952 | {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, 953 | {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, 954 | {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, 955 | {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, 956 | {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, 957 | {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, 958 | {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, 959 | {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, 960 | {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, 961 | {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, 962 | {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, 963 | {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, 964 | {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, 965 | {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, 966 | {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, 967 | {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, 968 | {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, 969 | {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, 970 | {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, 971 | {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, 972 | {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, 973 | {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, 974 | {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, 975 | {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, 976 | {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, 977 | {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, 978 | {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, 979 | {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, 980 | {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, 981 | {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, 982 | {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, 983 | {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, 984 | {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, 985 | {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, 986 | {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, 987 | {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, 988 | {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, 989 | {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, 990 | {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, 991 | {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, 992 | {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, 993 | {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, 994 | {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, 995 | {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, 996 | {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, 997 | {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, 998 | {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, 999 | {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, 1000 | {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, 1001 | {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, 1002 | {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, 1003 | {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, 1004 | {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, 1005 | {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, 1006 | {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, 1007 | {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, 1008 | {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, 1009 | {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, 1010 | {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, 1011 | {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, 1012 | {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, 1013 | {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, 1014 | {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, 1015 | {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, 1016 | {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, 1017 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, 1018 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, 1019 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, 1020 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, 1021 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, 1022 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, 1023 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, 1024 | {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, 1025 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, 1026 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, 1027 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, 1028 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, 1029 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, 1030 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, 1031 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, 1032 | {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, 1033 | {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, 1034 | ] 1035 | 1036 | [package.dependencies] 1037 | typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" 1038 | 1039 | [[package]] 1040 | name = "pydantic-extra-types" 1041 | version = "2.9.0" 1042 | description = "Extra Pydantic types." 1043 | optional = false 1044 | python-versions = ">=3.8" 1045 | groups = ["docs"] 1046 | files = [ 1047 | {file = "pydantic_extra_types-2.9.0-py3-none-any.whl", hash = "sha256:f0bb975508572ba7bf3390b7337807588463b7248587e69f43b1ad7c797530d0"}, 1048 | {file = "pydantic_extra_types-2.9.0.tar.gz", hash = "sha256:e061c01636188743bb69f368dcd391f327b8cfbfede2fe1cbb1211b06601ba3b"}, 1049 | ] 1050 | 1051 | [package.dependencies] 1052 | pydantic = ">=2.5.2" 1053 | 1054 | [package.extras] 1055 | all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2) ; python_version < \"3.9\"", "python-ulid (>=1,<3) ; python_version >= \"3.9\"", "pytz (>=2024.1)", "semver (>=3.0.2)", "tzdata (>=2024.1)"] 1056 | pendulum = ["pendulum (>=3.0.0,<4.0.0)"] 1057 | phonenumbers = ["phonenumbers (>=8,<9)"] 1058 | pycountry = ["pycountry (>=23)"] 1059 | python-ulid = ["python-ulid (>=1,<2) ; python_version < \"3.9\"", "python-ulid (>=1,<3) ; python_version >= \"3.9\""] 1060 | semver = ["semver (>=3.0.2)"] 1061 | 1062 | [[package]] 1063 | name = "pyflakes" 1064 | version = "2.5.0" 1065 | description = "passive checker of Python programs" 1066 | optional = false 1067 | python-versions = ">=3.6" 1068 | groups = ["dev"] 1069 | files = [ 1070 | {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, 1071 | {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, 1072 | ] 1073 | 1074 | [[package]] 1075 | name = "pygments" 1076 | version = "2.18.0" 1077 | description = "Pygments is a syntax highlighting package written in Python." 1078 | optional = false 1079 | python-versions = ">=3.8" 1080 | groups = ["docs"] 1081 | files = [ 1082 | {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, 1083 | {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, 1084 | ] 1085 | 1086 | [package.extras] 1087 | windows-terminal = ["colorama (>=0.4.6)"] 1088 | 1089 | [[package]] 1090 | name = "pytest" 1091 | version = "8.3.5" 1092 | description = "pytest: simple powerful testing with Python" 1093 | optional = false 1094 | python-versions = ">=3.8" 1095 | groups = ["dev"] 1096 | files = [ 1097 | {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, 1098 | {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, 1099 | ] 1100 | 1101 | [package.dependencies] 1102 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 1103 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 1104 | iniconfig = "*" 1105 | packaging = "*" 1106 | pluggy = ">=1.5,<2" 1107 | tomli = {version = ">=1", markers = "python_version < \"3.11\""} 1108 | 1109 | [package.extras] 1110 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 1111 | 1112 | [[package]] 1113 | name = "pytest-cov" 1114 | version = "5.0.0" 1115 | description = "Pytest plugin for measuring coverage." 1116 | optional = false 1117 | python-versions = ">=3.8" 1118 | groups = ["dev"] 1119 | files = [ 1120 | {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, 1121 | {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, 1122 | ] 1123 | 1124 | [package.dependencies] 1125 | coverage = {version = ">=5.2.1", extras = ["toml"]} 1126 | pytest = ">=4.6" 1127 | 1128 | [package.extras] 1129 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] 1130 | 1131 | [[package]] 1132 | name = "pytest-flake8" 1133 | version = "1.2.2" 1134 | description = "pytest plugin to check FLAKE8 requirements" 1135 | optional = false 1136 | python-versions = ">=3.8" 1137 | groups = ["dev"] 1138 | files = [ 1139 | {file = "pytest_flake8-1.2.2-py3-none-any.whl", hash = "sha256:7b8dfa77be42b5cec901b6b67eafb18d47ae6a05c4deac89399dba4c3591ce4d"}, 1140 | {file = "pytest_flake8-1.2.2.tar.gz", hash = "sha256:44a805bd7dce16a0de921a1292df639b70d56ca621a68d4bb515e09858e6e429"}, 1141 | ] 1142 | 1143 | [package.dependencies] 1144 | flake8 = ">=5,<6" 1145 | pytest = ">=7.0" 1146 | 1147 | [package.extras] 1148 | doc = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 1149 | test = ["pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 1150 | 1151 | [[package]] 1152 | name = "pyyaml" 1153 | version = "6.0.2" 1154 | description = "YAML parser and emitter for Python" 1155 | optional = false 1156 | python-versions = ">=3.8" 1157 | groups = ["dev"] 1158 | files = [ 1159 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 1160 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 1161 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 1162 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 1163 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 1164 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 1165 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 1166 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 1167 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 1168 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 1169 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 1170 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 1171 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 1172 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 1173 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 1174 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 1175 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 1176 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 1177 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 1178 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 1179 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 1180 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 1181 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 1182 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 1183 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 1184 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 1185 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 1186 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 1187 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 1188 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 1189 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 1190 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 1191 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 1192 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 1193 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 1194 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 1195 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 1196 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 1197 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 1198 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 1199 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 1200 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 1201 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 1202 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 1203 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 1204 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 1205 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 1206 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 1207 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 1208 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 1209 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 1210 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 1211 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 1212 | ] 1213 | 1214 | [[package]] 1215 | name = "referencing" 1216 | version = "0.35.1" 1217 | description = "JSON Referencing + Python" 1218 | optional = false 1219 | python-versions = ">=3.8" 1220 | groups = ["main"] 1221 | files = [ 1222 | {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, 1223 | {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, 1224 | ] 1225 | 1226 | [package.dependencies] 1227 | attrs = ">=22.2.0" 1228 | rpds-py = ">=0.7.0" 1229 | 1230 | [[package]] 1231 | name = "requests" 1232 | version = "2.32.3" 1233 | description = "Python HTTP for Humans." 1234 | optional = false 1235 | python-versions = ">=3.8" 1236 | groups = ["docs"] 1237 | files = [ 1238 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, 1239 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, 1240 | ] 1241 | 1242 | [package.dependencies] 1243 | certifi = ">=2017.4.17" 1244 | charset-normalizer = ">=2,<4" 1245 | idna = ">=2.5,<4" 1246 | urllib3 = ">=1.21.1,<3" 1247 | 1248 | [package.extras] 1249 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 1250 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 1251 | 1252 | [[package]] 1253 | name = "rfc3339-validator" 1254 | version = "0.1.4" 1255 | description = "A pure python RFC3339 validator" 1256 | optional = false 1257 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 1258 | groups = ["main"] 1259 | files = [ 1260 | {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, 1261 | {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, 1262 | ] 1263 | 1264 | [package.dependencies] 1265 | six = "*" 1266 | 1267 | [[package]] 1268 | name = "rpds-py" 1269 | version = "0.20.0" 1270 | description = "Python bindings to Rust's persistent data structures (rpds)" 1271 | optional = false 1272 | python-versions = ">=3.8" 1273 | groups = ["main"] 1274 | files = [ 1275 | {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, 1276 | {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, 1277 | {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, 1278 | {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, 1279 | {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, 1280 | {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, 1281 | {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, 1282 | {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, 1283 | {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, 1284 | {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, 1285 | {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, 1286 | {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, 1287 | {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, 1288 | {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, 1289 | {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, 1290 | {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, 1291 | {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, 1292 | {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, 1293 | {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, 1294 | {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, 1295 | {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, 1296 | {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, 1297 | {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, 1298 | {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, 1299 | {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, 1300 | {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, 1301 | {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, 1302 | {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, 1303 | {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, 1304 | {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, 1305 | {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, 1306 | {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, 1307 | {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, 1308 | {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, 1309 | {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, 1310 | {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, 1311 | {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, 1312 | {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, 1313 | {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, 1314 | {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, 1315 | {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, 1316 | {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, 1317 | {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, 1318 | {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, 1319 | {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, 1320 | {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, 1321 | {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, 1322 | {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, 1323 | {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, 1324 | {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, 1325 | {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, 1326 | {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, 1327 | {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, 1328 | {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, 1329 | {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, 1330 | {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, 1331 | {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, 1332 | {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, 1333 | {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, 1334 | {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, 1335 | {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, 1336 | {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, 1337 | {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, 1338 | {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, 1339 | {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, 1340 | {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, 1341 | {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, 1342 | {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, 1343 | {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, 1344 | {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, 1345 | {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, 1346 | {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, 1347 | {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, 1348 | {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, 1349 | {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, 1350 | {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, 1351 | {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, 1352 | {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, 1353 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, 1354 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, 1355 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, 1356 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, 1357 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, 1358 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, 1359 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, 1360 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, 1361 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, 1362 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, 1363 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, 1364 | {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, 1365 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, 1366 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, 1367 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, 1368 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, 1369 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, 1370 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, 1371 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, 1372 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, 1373 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, 1374 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, 1375 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, 1376 | {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, 1377 | {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, 1378 | ] 1379 | 1380 | [[package]] 1381 | name = "six" 1382 | version = "1.16.0" 1383 | description = "Python 2 and 3 compatibility utilities" 1384 | optional = false 1385 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1386 | groups = ["main"] 1387 | files = [ 1388 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1389 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1390 | ] 1391 | 1392 | [[package]] 1393 | name = "snowballstemmer" 1394 | version = "2.2.0" 1395 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 1396 | optional = false 1397 | python-versions = "*" 1398 | groups = ["docs"] 1399 | files = [ 1400 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 1401 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "sphinx" 1406 | version = "7.4.7" 1407 | description = "Python documentation generator" 1408 | optional = false 1409 | python-versions = ">=3.9" 1410 | groups = ["docs"] 1411 | files = [ 1412 | {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, 1413 | {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, 1414 | ] 1415 | 1416 | [package.dependencies] 1417 | alabaster = ">=0.7.14,<0.8.0" 1418 | babel = ">=2.13" 1419 | colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} 1420 | docutils = ">=0.20,<0.22" 1421 | imagesize = ">=1.3" 1422 | importlib-metadata = {version = ">=6.0", markers = "python_version < \"3.10\""} 1423 | Jinja2 = ">=3.1" 1424 | packaging = ">=23.0" 1425 | Pygments = ">=2.17" 1426 | requests = ">=2.30.0" 1427 | snowballstemmer = ">=2.2" 1428 | sphinxcontrib-applehelp = "*" 1429 | sphinxcontrib-devhelp = "*" 1430 | sphinxcontrib-htmlhelp = ">=2.0.0" 1431 | sphinxcontrib-jsmath = "*" 1432 | sphinxcontrib-qthelp = "*" 1433 | sphinxcontrib-serializinghtml = ">=1.1.9" 1434 | tomli = {version = ">=2", markers = "python_version < \"3.11\""} 1435 | 1436 | [package.extras] 1437 | docs = ["sphinxcontrib-websupport"] 1438 | lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"] 1439 | test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] 1440 | 1441 | [[package]] 1442 | name = "sphinx-immaterial" 1443 | version = "0.12.4" 1444 | description = "Adaptation of mkdocs-material theme for the Sphinx documentation system" 1445 | optional = false 1446 | python-versions = ">=3.9" 1447 | groups = ["docs"] 1448 | files = [ 1449 | {file = "sphinx_immaterial-0.12.4-py3-none-any.whl", hash = "sha256:09224ad18a39204e0a05168e027d0b6af1b85be33c666c711092ae91229e6ac7"}, 1450 | {file = "sphinx_immaterial-0.12.4.tar.gz", hash = "sha256:3b2128114040e96539cf4eaaa924b80bf4944af363e4e9954a6290fa7549aeaa"}, 1451 | ] 1452 | 1453 | [package.dependencies] 1454 | appdirs = "*" 1455 | markupsafe = "*" 1456 | pydantic = ">=2.4" 1457 | pydantic-extra-types = "*" 1458 | requests = "*" 1459 | sphinx = ">=4.5" 1460 | typing-extensions = "*" 1461 | 1462 | [package.extras] 1463 | black = ["black"] 1464 | clang-format = ["clang-format"] 1465 | cpp = ["libclang"] 1466 | json = ["pyyaml"] 1467 | jsonschema-validation = ["jsonschema"] 1468 | keys = ["pymdown-extensions"] 1469 | 1470 | [[package]] 1471 | name = "sphinxcontrib-applehelp" 1472 | version = "2.0.0" 1473 | description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" 1474 | optional = false 1475 | python-versions = ">=3.9" 1476 | groups = ["docs"] 1477 | files = [ 1478 | {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, 1479 | {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, 1480 | ] 1481 | 1482 | [package.extras] 1483 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1484 | standalone = ["Sphinx (>=5)"] 1485 | test = ["pytest"] 1486 | 1487 | [[package]] 1488 | name = "sphinxcontrib-devhelp" 1489 | version = "2.0.0" 1490 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" 1491 | optional = false 1492 | python-versions = ">=3.9" 1493 | groups = ["docs"] 1494 | files = [ 1495 | {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, 1496 | {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, 1497 | ] 1498 | 1499 | [package.extras] 1500 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1501 | standalone = ["Sphinx (>=5)"] 1502 | test = ["pytest"] 1503 | 1504 | [[package]] 1505 | name = "sphinxcontrib-htmlhelp" 1506 | version = "2.1.0" 1507 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 1508 | optional = false 1509 | python-versions = ">=3.9" 1510 | groups = ["docs"] 1511 | files = [ 1512 | {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, 1513 | {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, 1514 | ] 1515 | 1516 | [package.extras] 1517 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1518 | standalone = ["Sphinx (>=5)"] 1519 | test = ["html5lib", "pytest"] 1520 | 1521 | [[package]] 1522 | name = "sphinxcontrib-jsmath" 1523 | version = "1.0.1" 1524 | description = "A sphinx extension which renders display math in HTML via JavaScript" 1525 | optional = false 1526 | python-versions = ">=3.5" 1527 | groups = ["docs"] 1528 | files = [ 1529 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 1530 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 1531 | ] 1532 | 1533 | [package.extras] 1534 | test = ["flake8", "mypy", "pytest"] 1535 | 1536 | [[package]] 1537 | name = "sphinxcontrib-qthelp" 1538 | version = "2.0.0" 1539 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" 1540 | optional = false 1541 | python-versions = ">=3.9" 1542 | groups = ["docs"] 1543 | files = [ 1544 | {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, 1545 | {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, 1546 | ] 1547 | 1548 | [package.extras] 1549 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1550 | standalone = ["Sphinx (>=5)"] 1551 | test = ["defusedxml (>=0.7.1)", "pytest"] 1552 | 1553 | [[package]] 1554 | name = "sphinxcontrib-serializinghtml" 1555 | version = "2.0.0" 1556 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" 1557 | optional = false 1558 | python-versions = ">=3.9" 1559 | groups = ["docs"] 1560 | files = [ 1561 | {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, 1562 | {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, 1563 | ] 1564 | 1565 | [package.extras] 1566 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1567 | standalone = ["Sphinx (>=5)"] 1568 | test = ["pytest"] 1569 | 1570 | [[package]] 1571 | name = "tomli" 1572 | version = "2.0.2" 1573 | description = "A lil' TOML parser" 1574 | optional = false 1575 | python-versions = ">=3.8" 1576 | groups = ["dev", "docs"] 1577 | files = [ 1578 | {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, 1579 | {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, 1580 | ] 1581 | markers = {dev = "python_full_version <= \"3.11.0a6\"", docs = "python_version < \"3.11\""} 1582 | 1583 | [[package]] 1584 | name = "typing-extensions" 1585 | version = "4.12.2" 1586 | description = "Backported and Experimental Type Hints for Python 3.8+" 1587 | optional = false 1588 | python-versions = ">=3.8" 1589 | groups = ["dev", "docs"] 1590 | files = [ 1591 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 1592 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 1593 | ] 1594 | 1595 | [[package]] 1596 | name = "urllib3" 1597 | version = "2.2.3" 1598 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1599 | optional = false 1600 | python-versions = ">=3.8" 1601 | groups = ["docs"] 1602 | files = [ 1603 | {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, 1604 | {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, 1605 | ] 1606 | 1607 | [package.extras] 1608 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] 1609 | h2 = ["h2 (>=4,<5)"] 1610 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1611 | zstd = ["zstandard (>=0.18.0)"] 1612 | 1613 | [[package]] 1614 | name = "virtualenv" 1615 | version = "20.26.6" 1616 | description = "Virtual Python Environment builder" 1617 | optional = false 1618 | python-versions = ">=3.7" 1619 | groups = ["dev"] 1620 | files = [ 1621 | {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, 1622 | {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, 1623 | ] 1624 | 1625 | [package.dependencies] 1626 | distlib = ">=0.3.7,<1" 1627 | filelock = ">=3.12.2,<4" 1628 | platformdirs = ">=3.9.1,<5" 1629 | 1630 | [package.extras] 1631 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] 1632 | 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) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] 1633 | 1634 | [[package]] 1635 | name = "zipp" 1636 | version = "3.20.2" 1637 | description = "Backport of pathlib-compatible object wrapper for zip files" 1638 | optional = false 1639 | python-versions = ">=3.8" 1640 | groups = ["docs"] 1641 | markers = "python_version < \"3.10\"" 1642 | files = [ 1643 | {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, 1644 | {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, 1645 | ] 1646 | 1647 | [package.extras] 1648 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 1649 | cover = ["pytest-cov"] 1650 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 1651 | enabler = ["pytest-enabler (>=2.2)"] 1652 | test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] 1653 | type = ["pytest-mypy"] 1654 | 1655 | [extras] 1656 | docs = [] 1657 | 1658 | [metadata] 1659 | lock-version = "2.1" 1660 | python-versions = "^3.9.0" 1661 | content-hash = "04e8a4e28d381d83214ac6d31174af00cfd2b099c8b34e9bbfff01e9b4508ccc" 1662 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["poetry-core>=1.0.0"] 3 | build-backend = "poetry.core.masonry.api" 4 | 5 | [tool.coverage.run] 6 | branch = true 7 | source =["openapi_schema_validator"] 8 | 9 | [tool.coverage.xml] 10 | output = "reports/coverage.xml" 11 | 12 | [tool.deptry.per_rule_ignores] 13 | DEP002 = ["rfc3339-validator"] 14 | 15 | [tool.mypy] 16 | files = "openapi_schema_validator" 17 | strict = true 18 | 19 | [[tool.mypy.overrides]] 20 | module = "jsonschema.*" 21 | ignore_missing_imports = true 22 | 23 | [[tool.mypy.overrides]] 24 | module = "jsonschema_specifications" 25 | ignore_missing_imports = true 26 | 27 | [[tool.mypy.overrides]] 28 | module = "rfc3339_validator" 29 | ignore_missing_imports = true 30 | 31 | [tool.poetry] 32 | name = "openapi-schema-validator" 33 | version = "0.6.3" 34 | description = "OpenAPI schema validation for Python" 35 | authors = ["Artur Maciag "] 36 | license = "BSD-3-Clause" 37 | readme = "README.rst" 38 | repository = "https://github.com/python-openapi/openapi-schema-validator" 39 | keywords = ["openapi", "swagger", "schema"] 40 | classifiers = [ 41 | "Development Status :: 4 - Beta", 42 | "Intended Audience :: Developers", 43 | "Topic :: Software Development :: Libraries :: Python Modules", 44 | "Operating System :: OS Independent", 45 | "Programming Language :: Python :: 3", 46 | "Programming Language :: Python :: 3.9", 47 | "Programming Language :: Python :: 3.10", 48 | "Programming Language :: Python :: 3.11", 49 | "Programming Language :: Python :: 3.12", 50 | "Programming Language :: Python :: 3.13", 51 | "Topic :: Software Development :: Libraries", 52 | "Typing :: Typed", 53 | ] 54 | include = [ 55 | {path = "tests", format = "sdist"}, 56 | ] 57 | 58 | [tool.poetry.dependencies] 59 | python = "^3.9.0" 60 | jsonschema = "^4.19.1" 61 | rfc3339-validator = "*" # requred by jsonschema for date-time checker 62 | jsonschema-specifications = ">=2024.10.1" 63 | 64 | [tool.poetry.extras] 65 | docs = ["sphinx", "sphinx-immaterial"] 66 | 67 | [tool.poetry.dev-dependencies] 68 | black = "^24.4.0" 69 | isort = "^5.13.2" 70 | pre-commit = "*" 71 | pytest = "^8" 72 | pytest-flake8 = "*" 73 | pytest-cov = "*" 74 | mypy = "^1.14" 75 | flynt = "^1.0" 76 | deptry = "^0.16.2" 77 | bump2version = "^1.0.1" 78 | 79 | [tool.poetry.group.docs.dependencies] 80 | sphinx = ">=5.3,<8.0" 81 | sphinx-immaterial = ">=0.11,<0.13" 82 | 83 | [tool.pytest.ini_options] 84 | addopts = """ 85 | --capture=no 86 | --verbose 87 | --showlocals 88 | --junitxml=reports/junit.xml 89 | --cov=openapi_schema_validator 90 | --cov-report=term-missing 91 | --cov-report=xml 92 | """ 93 | 94 | [tool.black] 95 | line-length = 79 96 | 97 | [tool.isort] 98 | profile = "black" 99 | line_length = 79 100 | force_single_line = true 101 | -------------------------------------------------------------------------------- /tests/integration/test_validators.py: -------------------------------------------------------------------------------- 1 | from base64 import b64encode 2 | 3 | import pytest 4 | from jsonschema import ValidationError 5 | from referencing import Registry 6 | from referencing import Resource 7 | from referencing.jsonschema import DRAFT202012 8 | 9 | from openapi_schema_validator import OAS30ReadValidator 10 | from openapi_schema_validator import OAS30Validator 11 | from openapi_schema_validator import OAS30WriteValidator 12 | from openapi_schema_validator import OAS31Validator 13 | from openapi_schema_validator import oas30_format_checker 14 | from openapi_schema_validator import oas31_format_checker 15 | 16 | 17 | class TestOAS30ValidatorFormatChecker: 18 | @pytest.fixture 19 | def format_checker(self): 20 | return OAS30Validator.FORMAT_CHECKER 21 | 22 | def test_required_checkers(self, format_checker): 23 | required_formats_set = { 24 | "int32", 25 | "int64", 26 | "float", 27 | "double", 28 | "byte", 29 | "binary", 30 | "date", 31 | "date-time", 32 | "password", 33 | } 34 | assert required_formats_set.issubset( 35 | set(format_checker.checkers.keys()) 36 | ) 37 | 38 | 39 | class BaseTestOASValidatorValidate: 40 | @pytest.mark.parametrize( 41 | "format,value", 42 | [ 43 | ("int32", "test"), 44 | ("int32", True), 45 | ("int32", 3.12), 46 | ("int32", ["test"]), 47 | ("int64", "test"), 48 | ("int64", True), 49 | ("int64", 3.12), 50 | ("int64", ["test"]), 51 | ("float", "test"), 52 | ("float", 3), 53 | ("float", True), 54 | ("float", ["test"]), 55 | ("double", "test"), 56 | ("double", 3), 57 | ("double", True), 58 | ("double", ["test"]), 59 | ("password", 3.12), 60 | ("password", True), 61 | ("password", 3), 62 | ("password", ["test"]), 63 | ], 64 | ) 65 | def test_formats_ignored( 66 | self, format, value, validator_class, format_checker 67 | ): 68 | schema = {"format": format} 69 | validator = validator_class(schema, format_checker=format_checker) 70 | 71 | result = validator.validate(value) 72 | 73 | assert result is None 74 | 75 | @pytest.mark.parametrize("format", ["float", "double"]) 76 | @pytest.mark.parametrize("value", [3, 3.14, 1.0]) 77 | def test_number_float_and_double_valid( 78 | self, format, value, validator_class, format_checker 79 | ): 80 | schema = {"type": "number", "format": format} 81 | validator = validator_class(schema, format_checker=format_checker) 82 | 83 | result = validator.validate(value) 84 | 85 | assert result is None 86 | 87 | @pytest.mark.parametrize("value", ["test"]) 88 | def test_string(self, validator_class, value): 89 | schema = {"type": "string"} 90 | validator = validator_class(schema) 91 | 92 | result = validator.validate(value) 93 | 94 | assert result is None 95 | 96 | @pytest.mark.parametrize("value", [True, 3, 3.12, None]) 97 | def test_string_invalid(self, validator_class, value): 98 | schema = {"type": "string"} 99 | validator = validator_class(schema) 100 | 101 | with pytest.raises(ValidationError): 102 | validator.validate(value) 103 | 104 | def test_referencing(self, validator_class): 105 | name_schema = Resource.from_contents( 106 | { 107 | "$schema": "https://json-schema.org/draft/2020-12/schema", 108 | "type": "string", 109 | } 110 | ) 111 | age_schema = DRAFT202012.create_resource( 112 | { 113 | "type": "integer", 114 | "format": "int32", 115 | "minimum": 0, 116 | "maximum": 120, 117 | } 118 | ) 119 | birth_date_schema = Resource.from_contents( 120 | { 121 | "type": "string", 122 | "format": "date", 123 | }, 124 | default_specification=DRAFT202012, 125 | ) 126 | registry = Registry().with_resources( 127 | [ 128 | ("urn:name-schema", name_schema), 129 | ("urn:age-schema", age_schema), 130 | ("urn:birth-date-schema", birth_date_schema), 131 | ], 132 | ) 133 | schema = { 134 | "type": "object", 135 | "required": ["name"], 136 | "properties": { 137 | "name": {"$ref": "urn:name-schema"}, 138 | "age": {"$ref": "urn:age-schema"}, 139 | "birth-date": {"$ref": "urn:birth-date-schema"}, 140 | }, 141 | "additionalProperties": False, 142 | } 143 | 144 | validator = validator_class(schema, registry=registry) 145 | result = validator.validate({"name": "John", "age": 23}, schema) 146 | 147 | assert result is None 148 | 149 | 150 | class TestOAS30ValidatorValidate(BaseTestOASValidatorValidate): 151 | @pytest.fixture 152 | def validator_class(self): 153 | return OAS30Validator 154 | 155 | @pytest.fixture 156 | def format_checker(self): 157 | return oas30_format_checker 158 | 159 | @pytest.mark.parametrize( 160 | "format,value", 161 | [ 162 | ("binary", True), 163 | ("binary", 3), 164 | ("binary", 3.12), 165 | ("binary", ["test"]), 166 | ("byte", True), 167 | ("byte", 3), 168 | ("byte", 3.12), 169 | ("byte", ["test"]), 170 | ], 171 | ) 172 | def test_oas30_formats_ignored( 173 | self, format, value, validator_class, format_checker 174 | ): 175 | schema = {"format": format} 176 | validator = validator_class(schema, format_checker=format_checker) 177 | 178 | result = validator.validate(value) 179 | 180 | assert result is None 181 | 182 | @pytest.mark.xfail(reason="OAS 3.0 string type checker allows byte") 183 | @pytest.mark.parametrize("value", [b"test"]) 184 | def test_string_disallow_binary(self, validator_class, value): 185 | schema = {"type": "string"} 186 | validator = validator_class(schema) 187 | 188 | with pytest.raises(ValidationError): 189 | validator.validate(value) 190 | 191 | @pytest.mark.parametrize("value", [b"test"]) 192 | def test_string_binary_valid(self, validator_class, format_checker, value): 193 | schema = {"type": "string", "format": "binary"} 194 | validator = validator_class(schema, format_checker=format_checker) 195 | 196 | result = validator.validate(value) 197 | 198 | assert result is None 199 | 200 | @pytest.mark.parametrize("value", ["test", True, 3, 3.12, None]) 201 | def test_string_binary_invalid( 202 | self, validator_class, format_checker, value 203 | ): 204 | schema = {"type": "string", "format": "binary"} 205 | validator = validator_class(schema, format_checker=format_checker) 206 | 207 | with pytest.raises(ValidationError): 208 | validator.validate(value) 209 | 210 | @pytest.mark.parametrize( 211 | "schema_type", 212 | [ 213 | "boolean", 214 | "array", 215 | "integer", 216 | "number", 217 | "string", 218 | ], 219 | ) 220 | def test_null(self, validator_class, schema_type): 221 | schema = {"type": schema_type} 222 | validator = validator_class(schema) 223 | value = None 224 | 225 | with pytest.raises(ValidationError): 226 | validator.validate(value) 227 | 228 | @pytest.mark.parametrize("is_nullable", [True, False]) 229 | def test_nullable_untyped(self, validator_class, is_nullable): 230 | schema = {"nullable": is_nullable} 231 | validator = validator_class(schema) 232 | value = None 233 | 234 | result = validator.validate(value) 235 | 236 | assert result is None 237 | 238 | @pytest.mark.parametrize( 239 | "schema_type", 240 | [ 241 | "boolean", 242 | "array", 243 | "integer", 244 | "number", 245 | "string", 246 | ], 247 | ) 248 | def test_nullable(self, validator_class, schema_type): 249 | schema = {"type": schema_type, "nullable": True} 250 | validator = validator_class(schema) 251 | value = None 252 | 253 | result = validator.validate(value) 254 | 255 | assert result is None 256 | 257 | def test_nullable_enum_without_none(self, validator_class): 258 | schema = {"type": "integer", "nullable": True, "enum": [1, 2, 3]} 259 | validator = validator_class(schema) 260 | value = None 261 | 262 | with pytest.raises(ValidationError): 263 | validator.validate(value) 264 | 265 | def test_nullable_enum_with_none(self, validator_class): 266 | schema = {"type": "integer", "nullable": True, "enum": [1, 2, 3, None]} 267 | validator = validator_class(schema) 268 | value = None 269 | 270 | result = validator.validate(value) 271 | 272 | assert result is None 273 | 274 | @pytest.mark.parametrize( 275 | "value", 276 | [ 277 | b64encode(b"string"), 278 | b64encode(b"string").decode(), 279 | ], 280 | ) 281 | def test_string_format_byte_valid(self, validator_class, value): 282 | schema = {"type": "string", "format": "byte"} 283 | validator = validator_class( 284 | schema, format_checker=oas30_format_checker 285 | ) 286 | 287 | result = validator.validate(value) 288 | 289 | assert result is None 290 | 291 | @pytest.mark.parametrize("value", ["string", b"string"]) 292 | def test_string_format_byte_invalid(self, validator_class, value): 293 | schema = {"type": "string", "format": "byte"} 294 | validator = validator_class( 295 | schema, format_checker=oas30_format_checker 296 | ) 297 | 298 | with pytest.raises(ValidationError, match="is not a 'byte'"): 299 | validator.validate(value) 300 | 301 | def test_allof_required(self, validator_class): 302 | schema = { 303 | "allOf": [ 304 | { 305 | "type": "object", 306 | "properties": {"some_prop": {"type": "string"}}, 307 | }, 308 | {"type": "object", "required": ["some_prop"]}, 309 | ] 310 | } 311 | validator = validator_class( 312 | schema, format_checker=oas30_format_checker 313 | ) 314 | with pytest.raises( 315 | ValidationError, match="'some_prop' is a required property" 316 | ): 317 | validator.validate({"another_prop": "bla"}) 318 | 319 | def test_required(self, validator_class): 320 | schema = { 321 | "type": "object", 322 | "properties": {"some_prop": {"type": "string"}}, 323 | "required": ["some_prop"], 324 | } 325 | 326 | validator = validator_class( 327 | schema, format_checker=oas30_format_checker 328 | ) 329 | with pytest.raises( 330 | ValidationError, match="'some_prop' is a required property" 331 | ): 332 | validator.validate({"another_prop": "bla"}) 333 | assert validator.validate({"some_prop": "hello"}) is None 334 | 335 | def test_oneof_required(self, validator_class): 336 | instance = { 337 | "n3IwfId": "string", 338 | } 339 | schema = { 340 | "type": "object", 341 | "properties": { 342 | "n3IwfId": {"type": "string"}, 343 | "wagfId": {"type": "string"}, 344 | }, 345 | "oneOf": [ 346 | {"required": ["n3IwfId"]}, 347 | {"required": ["wagfId"]}, 348 | ], 349 | } 350 | validator = validator_class( 351 | schema, format_checker=oas30_format_checker 352 | ) 353 | result = validator.validate(instance) 354 | assert result is None 355 | 356 | @pytest.mark.parametrize( 357 | "schema_type", 358 | [ 359 | "oneOf", 360 | "anyOf", 361 | "allOf", 362 | ], 363 | ) 364 | def test_oneof_discriminator(self, validator_class, schema_type): 365 | # We define a few components schemas 366 | components = { 367 | "MountainHiking": { 368 | "type": "object", 369 | "properties": { 370 | "discipline": { 371 | "type": "string", 372 | # we allow both the explicitely matched mountain_hiking discipline 373 | # and the implicitely matched MoutainHiking discipline 374 | "enum": ["mountain_hiking", "MountainHiking"], 375 | }, 376 | "length": { 377 | "type": "integer", 378 | }, 379 | }, 380 | "required": ["discipline", "length"], 381 | }, 382 | "AlpineClimbing": { 383 | "type": "object", 384 | "properties": { 385 | "discipline": { 386 | "type": "string", 387 | "enum": ["alpine_climbing"], 388 | }, 389 | "height": { 390 | "type": "integer", 391 | }, 392 | }, 393 | "required": ["discipline", "height"], 394 | }, 395 | "Route": { 396 | # defined later 397 | }, 398 | } 399 | components["Route"][schema_type] = [ 400 | {"$ref": "#/components/schemas/MountainHiking"}, 401 | {"$ref": "#/components/schemas/AlpineClimbing"}, 402 | ] 403 | 404 | # Add the compoments in a minimalis schema 405 | schema = { 406 | "$ref": "#/components/schemas/Route", 407 | "components": {"schemas": components}, 408 | } 409 | 410 | if schema_type != "allOf": 411 | # use jsonschema validator when no discriminator is defined 412 | validator = validator_class( 413 | schema, format_checker=oas30_format_checker 414 | ) 415 | with pytest.raises( 416 | ValidationError, 417 | match="is not valid under any of the given schemas", 418 | ): 419 | validator.validate( 420 | {"something": "matching_none_of_the_schemas"} 421 | ) 422 | assert False 423 | 424 | if schema_type == "anyOf": 425 | # use jsonschema validator when no discriminator is defined 426 | validator = validator_class( 427 | schema, format_checker=oas30_format_checker 428 | ) 429 | with pytest.raises( 430 | ValidationError, 431 | match="is not valid under any of the given schemas", 432 | ): 433 | validator.validate( 434 | {"something": "matching_none_of_the_schemas"} 435 | ) 436 | assert False 437 | 438 | discriminator = { 439 | "propertyName": "discipline", 440 | "mapping": { 441 | "mountain_hiking": "#/components/schemas/MountainHiking", 442 | "alpine_climbing": "#/components/schemas/AlpineClimbing", 443 | }, 444 | } 445 | schema["components"]["schemas"]["Route"][ 446 | "discriminator" 447 | ] = discriminator 448 | 449 | # Optional: check we return useful result when the schema is wrong 450 | validator = validator_class( 451 | schema, format_checker=oas30_format_checker 452 | ) 453 | with pytest.raises( 454 | ValidationError, match="does not contain discriminating property" 455 | ): 456 | validator.validate({"something": "missing"}) 457 | assert False 458 | 459 | # Check we get a non-generic, somehow usable, error message when a discriminated schema is failing 460 | with pytest.raises( 461 | ValidationError, match="'bad_string' is not of type 'integer'" 462 | ): 463 | validator.validate( 464 | {"discipline": "mountain_hiking", "length": "bad_string"} 465 | ) 466 | assert False 467 | 468 | # Check explicit MountainHiking resolution 469 | validator.validate({"discipline": "mountain_hiking", "length": 10}) 470 | 471 | # Check implicit MountainHiking resolution 472 | validator.validate({"discipline": "MountainHiking", "length": 10}) 473 | 474 | # Check non resolvable implicit schema 475 | with pytest.raises( 476 | ValidationError, 477 | match="reference '#/components/schemas/other' could not be resolved", 478 | ): 479 | result = validator.validate({"discipline": "other"}) 480 | assert False 481 | 482 | @pytest.mark.parametrize("is_nullable", [True, False]) 483 | def test_nullable_ref(self, validator_class, is_nullable): 484 | """ 485 | Tests that a field that points to a schema reference is null checked based on the $ref schema rather than 486 | on this schema 487 | :param is_nullable: if the schema is marked as nullable. If not, validate an exception is raised on None 488 | """ 489 | schema = { 490 | "$ref": "#/$defs/Pet", 491 | "$defs": { 492 | "NullableText": {"type": "string", "nullable": is_nullable}, 493 | "Pet": { 494 | "properties": { 495 | "testfield": {"$ref": "#/$defs/NullableText"}, 496 | }, 497 | }, 498 | }, 499 | } 500 | validator = validator_class( 501 | schema, 502 | format_checker=oas30_format_checker, 503 | ) 504 | 505 | result = validator.validate({"testfield": "John"}) 506 | assert result is None 507 | 508 | if is_nullable: 509 | result = validator.validate({"testfield": None}) 510 | assert result is None 511 | else: 512 | with pytest.raises( 513 | ValidationError, 514 | match="None for not nullable", 515 | ): 516 | validator.validate({"testfield": None}) 517 | assert False 518 | 519 | @pytest.mark.parametrize( 520 | "schema_type, not_nullable_regex", 521 | [ 522 | ("oneOf", "None is not valid under any of the given schemas"), 523 | ("anyOf", "None is not valid under any of the given schemas"), 524 | ("allOf", "None for not nullable"), 525 | ], 526 | ) 527 | @pytest.mark.parametrize("is_nullable", [True, False]) 528 | def test_nullable_schema_combos( 529 | self, validator_class, is_nullable, schema_type, not_nullable_regex 530 | ): 531 | """ 532 | This test ensures that nullablilty semantics are correct for oneOf, anyOf and allOf 533 | Specifically, nullable should checked on the children schemas 534 | :param is_nullable: if the schema is marked as nullable. If not, validate an exception is raised on None 535 | :param schema_type: the schema type to validate 536 | :param not_nullable_regex: the expected raised exception if fields are marked as not nullable 537 | """ 538 | schema = { 539 | "$ref": "#/$defs/Pet", 540 | "$defs": { 541 | "NullableText": { 542 | "type": "string", 543 | "nullable": ( 544 | False if schema_type == "oneOf" else is_nullable 545 | ), 546 | }, 547 | "NullableEnum": { 548 | "type": "string", 549 | "nullable": is_nullable, 550 | "enum": ["John", "Alice", None], 551 | }, 552 | "Pet": { 553 | "properties": { 554 | "testfield": { 555 | schema_type: [ 556 | {"$ref": "#/$defs/NullableText"}, 557 | {"$ref": "#/$defs/NullableEnum"}, 558 | ] 559 | } 560 | }, 561 | }, 562 | }, 563 | } 564 | validator = validator_class( 565 | schema, 566 | format_checker=oas30_format_checker, 567 | ) 568 | 569 | if is_nullable: 570 | result = validator.validate({"testfield": None}) 571 | assert result is None 572 | else: 573 | with pytest.raises(ValidationError, match=not_nullable_regex): 574 | validator.validate({"testfield": None}) 575 | assert False 576 | 577 | 578 | class TestOAS30ReadWriteValidatorValidate: 579 | def test_read_only(self): 580 | schema = { 581 | "type": "object", 582 | "properties": {"some_prop": {"type": "string", "readOnly": True}}, 583 | } 584 | 585 | validator = OAS30WriteValidator( 586 | schema, 587 | format_checker=oas30_format_checker, 588 | ) 589 | with pytest.raises( 590 | ValidationError, 591 | match="Tried to write read-only property with hello", 592 | ): 593 | validator.validate({"some_prop": "hello"}) 594 | validator = OAS30ReadValidator( 595 | schema, 596 | format_checker=oas30_format_checker, 597 | ) 598 | assert validator.validate({"some_prop": "hello"}) is None 599 | validator = OAS30Validator( 600 | schema, 601 | format_checker=oas30_format_checker, 602 | ) 603 | assert validator.validate({"some_prop": "hello"}) is None 604 | 605 | def test_write_only(self): 606 | schema = { 607 | "type": "object", 608 | "properties": {"some_prop": {"type": "string", "writeOnly": True}}, 609 | } 610 | 611 | validator = OAS30ReadValidator( 612 | schema, 613 | format_checker=oas30_format_checker, 614 | ) 615 | with pytest.raises( 616 | ValidationError, 617 | match="Tried to read write-only property with hello", 618 | ): 619 | validator.validate({"some_prop": "hello"}) 620 | validator = OAS30WriteValidator( 621 | schema, 622 | format_checker=oas30_format_checker, 623 | ) 624 | assert validator.validate({"some_prop": "hello"}) is None 625 | validator = OAS30Validator( 626 | schema, 627 | format_checker=oas30_format_checker, 628 | ) 629 | assert validator.validate({"some_prop": "hello"}) is None 630 | 631 | def test_required_read_only(self): 632 | schema = { 633 | "type": "object", 634 | "properties": {"some_prop": {"type": "string", "readOnly": True}}, 635 | "required": ["some_prop"], 636 | } 637 | 638 | validator = OAS30ReadValidator( 639 | schema, 640 | format_checker=oas30_format_checker, 641 | ) 642 | with pytest.raises( 643 | ValidationError, match="'some_prop' is a required property" 644 | ): 645 | validator.validate({"another_prop": "hello"}) 646 | validator = OAS30WriteValidator( 647 | schema, 648 | format_checker=oas30_format_checker, 649 | ) 650 | assert validator.validate({"another_prop": "hello"}) is None 651 | 652 | def test_required_write_only(self): 653 | schema = { 654 | "type": "object", 655 | "properties": {"some_prop": {"type": "string", "writeOnly": True}}, 656 | "required": ["some_prop"], 657 | } 658 | 659 | validator = OAS30WriteValidator( 660 | schema, 661 | format_checker=oas30_format_checker, 662 | ) 663 | with pytest.raises( 664 | ValidationError, match="'some_prop' is a required property" 665 | ): 666 | validator.validate({"another_prop": "hello"}) 667 | validator = OAS30ReadValidator( 668 | schema, 669 | format_checker=oas30_format_checker, 670 | ) 671 | assert validator.validate({"another_prop": "hello"}) is None 672 | 673 | 674 | class TestOAS31ValidatorFormatChecker: 675 | @pytest.fixture 676 | def format_checker(self): 677 | return OAS31Validator.FORMAT_CHECKER 678 | 679 | def test_required_checkers(self, format_checker): 680 | required_formats_set = { 681 | # standard formats 682 | "int32", 683 | "int64", 684 | "float", 685 | "double", 686 | "password", 687 | } 688 | assert required_formats_set.issubset( 689 | set(format_checker.checkers.keys()) 690 | ) 691 | 692 | 693 | class TestOAS31ValidatorValidate(BaseTestOASValidatorValidate): 694 | @pytest.fixture 695 | def validator_class(self): 696 | return OAS31Validator 697 | 698 | @pytest.fixture 699 | def format_checker(self): 700 | return oas31_format_checker 701 | 702 | @pytest.mark.parametrize("value", [b"test"]) 703 | def test_string_disallow_binary(self, validator_class, value): 704 | schema = {"type": "string"} 705 | validator = validator_class(schema) 706 | 707 | with pytest.raises(ValidationError): 708 | validator.validate(value) 709 | 710 | @pytest.mark.parametrize( 711 | "schema_type", 712 | [ 713 | "boolean", 714 | "array", 715 | "integer", 716 | "number", 717 | "string", 718 | ], 719 | ) 720 | def test_null(self, validator_class, schema_type): 721 | schema = {"type": schema_type} 722 | validator = validator_class(schema) 723 | value = None 724 | 725 | with pytest.raises(ValidationError): 726 | validator.validate(value) 727 | 728 | @pytest.mark.parametrize( 729 | "schema_type", 730 | [ 731 | "boolean", 732 | "array", 733 | "integer", 734 | "number", 735 | "string", 736 | ], 737 | ) 738 | def test_nullable(self, validator_class, schema_type): 739 | schema = {"type": [schema_type, "null"]} 740 | validator = validator_class(schema) 741 | value = None 742 | 743 | result = validator.validate(value) 744 | 745 | assert result is None 746 | 747 | def test_schema_validation(self, validator_class, format_checker): 748 | schema = { 749 | "type": "object", 750 | "required": ["name"], 751 | "properties": { 752 | "name": {"type": "string"}, 753 | "age": { 754 | "type": "integer", 755 | "format": "int32", 756 | "minimum": 0, 757 | "nullable": True, 758 | }, 759 | "birth-date": { 760 | "type": "string", 761 | "format": "date", 762 | }, 763 | }, 764 | "additionalProperties": False, 765 | } 766 | validator = validator_class( 767 | schema, 768 | format_checker=format_checker, 769 | ) 770 | 771 | result = validator.validate({"name": "John", "age": 23}) 772 | assert result is None 773 | 774 | with pytest.raises(ValidationError) as excinfo: 775 | validator.validate({"name": "John", "city": "London"}) 776 | 777 | error = "Additional properties are not allowed ('city' was unexpected)" 778 | assert error in str(excinfo.value) 779 | 780 | with pytest.raises(ValidationError) as excinfo: 781 | validator.validate({"name": "John", "birth-date": "-12"}) 782 | 783 | error = "'-12' is not a 'date'" 784 | assert error in str(excinfo.value) 785 | 786 | def test_schema_ref(self, validator_class, format_checker): 787 | schema = { 788 | "$ref": "#/$defs/Pet", 789 | "$defs": { 790 | "Pet": { 791 | "required": ["id", "name"], 792 | "properties": { 793 | "id": {"type": "integer", "format": "int64"}, 794 | "name": {"type": "string"}, 795 | "tag": {"type": "string"}, 796 | }, 797 | } 798 | }, 799 | } 800 | validator = validator_class( 801 | schema, 802 | format_checker=format_checker, 803 | ) 804 | 805 | result = validator.validate({"id": 1, "name": "John"}) 806 | assert result is None 807 | 808 | with pytest.raises(ValidationError) as excinfo: 809 | validator.validate({"name": "John"}) 810 | 811 | error = "'id' is a required property" 812 | assert error in str(excinfo.value) 813 | 814 | @pytest.mark.parametrize( 815 | "value", 816 | [ 817 | [1600, "Pennsylvania", "Avenue", "NW"], 818 | [1600, "Pennsylvania", "Avenue"], 819 | ], 820 | ) 821 | def test_array_prefixitems(self, validator_class, format_checker, value): 822 | schema = { 823 | "type": "array", 824 | "prefixItems": [ 825 | {"type": "number"}, 826 | {"type": "string"}, 827 | {"enum": ["Street", "Avenue", "Boulevard"]}, 828 | {"enum": ["NW", "NE", "SW", "SE"]}, 829 | ], 830 | "items": False, 831 | } 832 | validator = validator_class( 833 | schema, 834 | format_checker=format_checker, 835 | ) 836 | 837 | result = validator.validate(value) 838 | 839 | assert result is None 840 | 841 | @pytest.mark.parametrize( 842 | "value", 843 | [ 844 | [1600, "Pennsylvania", "Avenue", "NW", "Washington"], 845 | ], 846 | ) 847 | def test_array_prefixitems_invalid(self, validator_class, value): 848 | schema = { 849 | "type": "array", 850 | "prefixItems": [ 851 | {"type": "number"}, 852 | {"type": "string"}, 853 | {"enum": ["Street", "Avenue", "Boulevard"]}, 854 | {"enum": ["NW", "NE", "SW", "SE"]}, 855 | ], 856 | "items": False, 857 | } 858 | validator = validator_class( 859 | schema, 860 | format_checker=oas31_format_checker, 861 | ) 862 | 863 | with pytest.raises(ValidationError) as excinfo: 864 | validator.validate(value) 865 | 866 | errors = [ 867 | # jsonschema < 4.20.0 868 | "Expected at most 4 items, but found 5", 869 | # jsonschema >= 4.20.0 870 | "Expected at most 4 items but found 1 extra", 871 | ] 872 | assert any(error in str(excinfo.value) for error in errors) 873 | -------------------------------------------------------------------------------- /tests/unit/test_shortcut.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from openapi_schema_validator import validate 4 | 5 | 6 | class ValidateTest(TestCase): 7 | def test_validate_does_not_mutate_schema_adding_nullable_key(self): 8 | schema = { 9 | "type": "object", 10 | "properties": { 11 | "email": {"type": "string"}, 12 | "enabled": { 13 | "type": "boolean", 14 | }, 15 | }, 16 | "example": {"enabled": False, "email": "foo@bar.com"}, 17 | } 18 | 19 | validate({"email": "foo@bar.com"}, schema) 20 | 21 | self.assertTrue("nullable" not in schema["properties"]["email"].keys()) 22 | --------------------------------------------------------------------------------