├── tests ├── __init__.py ├── test_serializers_v1.py └── test_serializers.py ├── .github ├── workflows │ ├── commitlint.config.js │ ├── publish.yml │ └── pr.yml ├── templates │ ├── template.hbs │ └── commit-template.hbs └── dependabot.yml ├── AUTHORS ├── apiclient_pydantic ├── v1 │ ├── utils.py │ ├── __init__.py │ └── serializers.py ├── __init__.py └── serializers.py ├── .editorconfig ├── .pre-commit-config.yaml ├── LICENSE ├── Makefile ├── CODE_OF_CONDUCT.md ├── release.config.js ├── README.md ├── pyproject.toml ├── .gitignore ├── CHANGELOG.md └── poetry.lock /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ['gitmoji']}; 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alphabetical list of contributors: 2 | 3 | * Stolpasov Maksim (MaxST) 4 | -------------------------------------------------------------------------------- /.github/templates/template.hbs: -------------------------------------------------------------------------------- 1 | ## v{{nextRelease.version}} ({{datetime "UTC:yyyy-mm-dd"}}) 2 | 3 | {{#each (sections commits) as |section| }} 4 | ### {{section.label}} 5 | {{#each section.commits}} 6 | {{> commitTemplate is_dep=section.is_dep}} 7 | {{/each}} 8 | {{/each}} 9 | -------------------------------------------------------------------------------- /apiclient_pydantic/v1/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from pydantic.v1 import BaseModel 4 | 5 | 6 | def is_pydantic_model(cls: Any) -> bool: 7 | try: 8 | return issubclass(cls, BaseModel) 9 | except TypeError: 10 | return False 11 | -------------------------------------------------------------------------------- /apiclient_pydantic/__init__.py: -------------------------------------------------------------------------------- 1 | from .serializers import ( 2 | ModelDumped, 3 | TModel, 4 | serialize, 5 | serialize_all_methods, 6 | ) 7 | 8 | __all__ = ( 9 | 'ModelDumped', 10 | 'TModel', 11 | 'serialize', 12 | 'serialize_all_methods', 13 | ) 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_size = 2 9 | indent_style = space 10 | 11 | [*.{py,pyi}] 12 | indent_size = 4 13 | 14 | [Makefile] 15 | indent_style = tab 16 | -------------------------------------------------------------------------------- /.github/templates/commit-template.hbs: -------------------------------------------------------------------------------- 1 | - {{subject}}{{#if issues}} (Issues:{{#each issues}} [`{{text}}`]({{link}}){{/each}}){{/if}} 2 | {{#if body}}{{#unless is_dep}} 3 | {{# each (split_by_line body) as |body_line|}} 4 | {{#if body_line}}{{body_line}}{{/if}} 5 | {{/each}} 6 | {{/unless}}{{/if}} 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: pip 5 | directory: '/' 6 | schedule: 7 | interval: daily 8 | time: '02:00' 9 | open-pull-requests-limit: 10 10 | ignore: 11 | - dependency-name: pydantic 12 | versions: 13 | - '1.8' 14 | -------------------------------------------------------------------------------- /apiclient_pydantic/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from .serializers import ( 2 | ParamsSerializer, 3 | ResponseSerializer, 4 | params_serializer, 5 | response_serializer, 6 | serialize, 7 | serialize_all_methods, 8 | serialize_request, 9 | serialize_response, 10 | ) 11 | 12 | __all__ = ( 13 | 'params_serializer', 14 | 'ParamsSerializer', 15 | 'response_serializer', 16 | 'ResponseSerializer', 17 | 'serialize', 18 | 'serialize_all_methods', 19 | 'serialize_request', 20 | 'serialize_response', 21 | ) 22 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.1.0 5 | hooks: 6 | - id: check-merge-conflict 7 | - id: check-toml 8 | - id: check-yaml 9 | args: 10 | - --allow-multiple-documents 11 | - id: debug-statements 12 | - id: double-quote-string-fixer 13 | - id: end-of-file-fixer 14 | exclude: LICENSE 15 | - repo: https://github.com/myint/docformatter 16 | rev: v1.4 17 | hooks: 18 | - id: docformatter 19 | args: [--in-place, --wrap-summaries, "119", --wrap-descriptions, "119"] 20 | - repo: local 21 | hooks: 22 | - id: py-format 23 | name: Formatting python 24 | entry: make format 25 | language: system 26 | require_serial: true 27 | files: \.py$ 28 | - id: py-lint 29 | name: Linting python 30 | entry: make lint 31 | language: system 32 | files: \.pyi?$ 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 MaxST 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Publish 3 | 4 | "on": 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build-and-publish: 11 | if: github.repository == 'mom1/api-client-pydantic' 12 | name: Build and publish Python 🐍 distributions 📦 to PyPI 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: '19' 19 | - uses: actions/setup-python@v3 20 | with: 21 | python-version: 3.8 22 | - name: Set up cache 23 | uses: actions/cache@v2 24 | with: 25 | path: ~/.cache/pypoetry/virtualenvs 26 | key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} 27 | - name: Install poetry 28 | run: make poetry-download 29 | - name: Install Semantic Release 30 | run: >- 31 | npm install --location=global 32 | semantic-release@21.1.2 33 | @semantic-release/changelog 34 | @semantic-release/git 35 | @semantic-release/exec 36 | @semantic-release/github 37 | semantic-release-gitmoji@1.5.0 38 | - name: Release package 39 | env: 40 | POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | run: semantic-release 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # https://stackoverflow.com/a/26339924 2 | .PHONY: list 3 | list: 4 | @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' 5 | 6 | %: 7 | @: 8 | 9 | #* Poetry 10 | .PHONY: poetry-download 11 | poetry-download: 12 | curl -sSL https://install.python-poetry.org | python 13 | 14 | .PHONY: poetry-remove 15 | poetry-remove: 16 | curl -sSL https://install.python-poetry.org | python --uninstall 17 | 18 | #* Installation 19 | .PHONY: install 20 | install: 21 | poetry install -n --sync 22 | 23 | .PHONY: pre-commit-install 24 | pre-commit-install: 25 | poetry run pre-commit install 26 | 27 | #* Formatters 28 | .PHONY: format 29 | fmt format: 30 | poetry run ruff format $(filter-out $@,$(MAKECMDGOALS)) 31 | 32 | #* Linting 33 | .PHONY: test 34 | test: 35 | poetry run pytest -c pyproject.toml tests 36 | 37 | .PHONY: check-codestyle 38 | check-codestyle: 39 | poetry run ruff check --exit-non-zero-on-fix $(filter-out $@,$(MAKECMDGOALS)) 40 | 41 | .PHONY: mypy 42 | mypy: 43 | poetry run mypy --config-file pyproject.toml $(filter-out $@,$(MAKECMDGOALS)) 44 | 45 | .PHONY: lint 46 | lint: 47 | @$(MAKE) -s check-codestyle $(filter-out $@,$(MAKECMDGOALS)) 48 | @$(MAKE) -s mypy $(filter-out $@,$(MAKECMDGOALS)) 49 | 50 | #* Cleaning 51 | .PHONY: clean 52 | clean: 53 | find . | grep -E '(.DS_Store|.mypy_cache|__pycache__|\.hypothesis|htmlcov|\.pytest_cache|\.coverage|\.perm|\.cache|\.static|\.py[cod]$$)' | xargs rm -rf 54 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | 4 | "on": [pull_request, workflow_dispatch] 5 | 6 | jobs: 7 | test: 8 | if: "! contains(toJSON(github.event.head_commit.message), ':memo: Update CHANGELOG.md')" 9 | name: Python ${{ matrix.python-version }} tests 10 | runs-on: ubuntu-latest 11 | strategy: 12 | max-parallel: 4 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12"] 15 | fail-fast: false 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | 23 | - name: Install poetry 24 | run: make poetry-download 25 | - name: Set up cache 26 | uses: actions/cache@v2 27 | with: 28 | path: ~/.cache/pypoetry/virtualenvs 29 | key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} 30 | - name: Install dependencies 31 | run: make install 32 | - name: Install dependencies ci 33 | run: poetry run pip install pytest-github-actions-annotate-failures 34 | - name: Run tests 35 | run: make test 36 | lint: 37 | if: "! contains(toJSON(github.event.head_commit.message), ':memo: Update CHANGELOG.md')" 38 | name: Python lint 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | - name: Set up Python 3.8 43 | uses: actions/setup-python@v2 44 | with: 45 | python-version: 3.8 46 | - name: Install poetry 47 | run: make poetry-download 48 | - name: Set up cache 49 | uses: actions/cache@v2 50 | with: 51 | path: ~/.cache/pypoetry/virtualenvs 52 | key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} 53 | - name: Install dependencies 54 | run: make install 55 | - name: Run tests 56 | run: make lint apiclient_pydantic 57 | dependabot-auto-merge: 58 | name: Dependabot auto-merge 59 | runs-on: ubuntu-latest 60 | needs: [test, lint] 61 | if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} 62 | permissions: 63 | contents: write 64 | pull-requests: write 65 | steps: 66 | - name: Dependabot metadata 67 | id: dependabot-metadata 68 | uses: dependabot/fetch-metadata@v1.2.1 69 | with: 70 | github-token: "${{ secrets.GITHUB_TOKEN }}" 71 | - name: Enable auto-merge for Dependabot PRs 72 | if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-minor' || steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' }} 73 | run: gh pr merge --auto --rebase "$PR_URL" 74 | env: 75 | PR_URL: ${{github.event.pull_request.html_url}} 76 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 77 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /release.config.js: -------------------------------------------------------------------------------- 1 | const {promisify} = require('util') 2 | const readFileAsync = promisify(require('fs').readFile) 3 | 4 | const template = readFileAsync('.github/templates/template.hbs') 5 | const commitTemplate = readFileAsync('.github/templates/commit-template.hbs') 6 | 7 | const sections = [ 8 | { 9 | group: 'breaking_changes', 10 | label: ':boom: Breaking Changes', 11 | emojis: ['💥'], 12 | }, 13 | { 14 | group: 'sparkles', 15 | label: ':sparkles: New', 16 | emojis: ['✨', '🎉'], 17 | }, 18 | { 19 | group: 'changed', 20 | label: ':recycle: Changes', 21 | emojis: ['🎨', '✏️', '⚡️', '♻️', '🔧', '👽️', '🚚', '🍱', '♿️', '💬', '🗃️', '🚸', '🏗️', '📱', '🔥', '🏷️'], 22 | }, 23 | { 24 | group: 'fixed', 25 | label: ':bug: Bugs', 26 | emojis: ['🐛', '🚑️'], 27 | }, 28 | { 29 | group: 'dependencies', 30 | label: ':arrow_up: Dependencies', 31 | emojis: ['⬆️', '⬇️', '➕', '➖', '📌'], 32 | }, 33 | { 34 | group: 'docs', 35 | label: ':memo: Documentation', 36 | emojis: ['📝', '🔇'], 37 | }, 38 | { 39 | group: 'other', 40 | label: ':seedling: Other', 41 | emojis: ['🔒️', '🔐', '👷', '💄'], 42 | }, 43 | ]; 44 | 45 | function makeGroups(commits) { 46 | if (!commits.length) return [] 47 | 48 | function mapCommits(groups) { 49 | return groups 50 | .map(({group, emojis, label}) => ({ 51 | group, 52 | label, 53 | is_dep: group === 'dependencies', 54 | commits: commits 55 | .filter((commit) => emojis.indexOf(commit.gitmoji) >= 0) 56 | .sort((first, second) => new Date(second.committerDate) - new Date(first.committerDate)), 57 | })) 58 | .filter(group => group.commits.length); 59 | } 60 | 61 | return mapCommits(sections) 62 | } 63 | 64 | module.exports = { 65 | branches: ["main", {name: "develop", prerelease: "rc"}], 66 | tagFormat: "v${version}", 67 | plugins: [ 68 | [ 69 | 'semantic-release-gitmoji', 70 | { 71 | releaseRules: { 72 | patch: { 73 | include: [...sections[2].emojis, ...sections[3].emojis, ...sections[4].emojis, ...sections[5].emojis, ...sections[6].emojis], 74 | exclude: ['⬆️', '📝'], 75 | }, 76 | }, 77 | releaseNotes: { 78 | template, 79 | partials: {commitTemplate}, 80 | helpers: { 81 | sections: (commits) => { 82 | let flat_commits = []; 83 | for (const [, value] of Object.entries(commits)) { 84 | flat_commits.push(...value); 85 | } 86 | return makeGroups(flat_commits); 87 | }, 88 | split_by_line: (text) => text.split('\n'), 89 | }, 90 | } 91 | } 92 | ], 93 | [ 94 | "@semantic-release/changelog", 95 | { 96 | changelogFile: "CHANGELOG.md", 97 | changelogTitle: '# Changelog', 98 | }, 99 | ], 100 | [ 101 | "@semantic-release/exec", 102 | { 103 | prepareCmd: 104 | "poetry version ${nextRelease.version} && " + 105 | "poetry build", 106 | publishCmd: "poetry publish", 107 | }, 108 | ], 109 | [ 110 | "@semantic-release/git", 111 | { 112 | assets: ["CHANGELOG.md", "pyproject.toml"], 113 | message: [ 114 | ':bookmark: v${nextRelease.version} [skip ci]', 115 | '', 116 | 'https://github.com/mom1/api-client-pydantic/releases/tag/${nextRelease.gitTag}' 117 | ].join('\n') 118 | }, 119 | ], 120 | [ 121 | "@semantic-release/github", 122 | { 123 | assets: [{path: "dist/*.whl"}, {path: "dist/*.tar.gz"}], 124 | }, 125 | ], 126 | ], 127 | }; 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub issues](https://img.shields.io/github/issues/mom1/api-client-pydantic.svg) 2 | ![GitHub stars](https://img.shields.io/github/stars/mom1/api-client-pydantic.svg) 3 | ![GitHub Release Date](https://img.shields.io/github/release-date/mom1/api-client-pydantic.svg) 4 | ![GitHub commits since latest release](https://img.shields.io/github/commits-since/mom1/api-client-pydantic/latest.svg) 5 | ![GitHub last commit](https://img.shields.io/github/last-commit/mom1/api-client-pydantic.svg) 6 | [![GitHub license](https://img.shields.io/github/license/mom1/api-client-pydantic)](https://github.com/mom1/api-client-pydantic/blob/master/LICENSE) 7 | 8 | [![PyPI](https://img.shields.io/pypi/v/api-client-pydantic.svg)](https://pypi.python.org/pypi/api-client-pydantic) 9 | [![PyPI](https://img.shields.io/pypi/pyversions/api-client-pydantic.svg)]() 10 | ![PyPI - Downloads](https://img.shields.io/pypi/dm/api-client-pydantic.svg?label=pip%20installs&logo=python) 11 | 12 | Gitmoji 13 | [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) 14 | 15 | # Python API Client Pydantic Extension 16 | 17 | ## Installation 18 | 19 | ```bash 20 | pip install api-client-pydantic 21 | ``` 22 | 23 | ## Usage 24 | 25 | The following decorators have been provided to validate request data and converting json straight to pydantic class. 26 | 27 | ```python 28 | from apiclient_pydantic import serialize, serialize_all_methods 29 | 30 | 31 | # serialize request and response data 32 | @serialize(config: Optional[ConfigDict] = None, validate_return: bool = True, response: Optional[Type[BaseModel]] = None) 33 | 34 | # wraps all local methods of a class with a decorator 'serialize'. 35 | @serialize_all_methods(config: Optional[ConfigDict] = None) 36 | ``` 37 | 38 | Usage: 39 | 40 | 1. Define the schema for your api in pydantic classes. 41 | ```python 42 | from pydantic import BaseModel, Field 43 | 44 | 45 | class Account(BaseModel): 46 | account_number: int = Field(alias='accountNumber') 47 | sort_code: int = Field(alias='sortCode') 48 | date_opened: datetime = Field(alias='dateOpened') 49 | ``` 50 | 51 | 2. Add the `@serialize` decorator to the api client method to transform the response 52 | directly into your defined schema. 53 | ```python 54 | @serialize(response=List[Account]) 55 | def get_accounts(): 56 | ... 57 | # or 58 | @serialize 59 | def get_accounts() -> List[Account]: 60 | ... 61 | ``` 62 | 3. Add the `@serialize` decorator to the api client method to translate the incoming kwargs 63 | into the required dict or instance for the endpoint: 64 | ```python 65 | from apiclient_pydantic import ModelDumped 66 | 67 | @serialize 68 | def create_account(data: AccountHolder): 69 | # data will be AccountHolder instance 70 | ... 71 | 72 | create_account(data={'last_name' : 'Smith','first_name' : 'John'}) 73 | # data will be a AccountHolder(last_name="Smith", first_name="John") 74 | 75 | @serialize 76 | def create_account(data: ModelDumped[AccountHolder]): 77 | # data will be exactly a dict 78 | ... 79 | 80 | create_account(data={'last_name' : 'Smith','first_name' : 'John'}) 81 | # data will be a dict {"last_name": "Smith", "first_name": "John"} 82 | ``` 83 | 4. For more convenient use, you can wrap all APIClient methods with `@serialize_all_methods`. 84 | ```python 85 | from apiclient import APIClient 86 | from apiclient_pydantic import serialize_all_methods 87 | from typing import List 88 | 89 | from .models import Account, AccountHolder 90 | 91 | 92 | @serialize_all_methods 93 | class MyApiClient(APIClient): 94 | def decorated_func(self, data: Account) -> Account: 95 | ... 96 | 97 | def decorated_func_holder(self, data: AccountHolder) -> List[Account]: 98 | ... 99 | ``` 100 | 101 | ## Related projects 102 | 103 | ### apiclient-pydantic-generator - Now deprecated. 104 | 105 | This code generator creates a [ApiClient](https://github.com/MikeWooster/api-client) app from an openapi file. 106 | 107 | [apiclient-pydantic-generator](https://github.com/mom1/apiclient-pydantic-generator) 108 | -------------------------------------------------------------------------------- /apiclient_pydantic/serializers.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import inspect 4 | from functools import partial 5 | from typing import TYPE_CHECKING, Annotated, Any, Callable, TypeVar, cast 6 | 7 | from apiclient import APIClient 8 | from pydantic import AfterValidator, BaseModel, ConfigDict 9 | from pydantic._internal import _generate_schema, _typing_extra, _validate_call 10 | from pydantic._internal._config import ConfigWrapper 11 | from pydantic._internal._generate_schema import GenerateSchema, ValidateCallSupportedTypes 12 | from pydantic._internal._namespace_utils import MappingNamespace, NsResolver, ns_for_function 13 | from pydantic._internal._validate_call import ( 14 | ValidateCallWrapper as PydanticValidateCallWrapper, 15 | extract_function_qualname, 16 | ) 17 | from pydantic.plugin._schema_validator import create_schema_validator 18 | from pydantic.validate_call_decorator import _check_function_type 19 | 20 | if TYPE_CHECKING: 21 | from collections.abc import Awaitable 22 | 23 | T = TypeVar('T', bound=APIClient) 24 | AnyCallableT = TypeVar('AnyCallableT', bound=Callable[..., Any]) 25 | TModel = TypeVar('TModel', bound=BaseModel) 26 | ModelDumped = Annotated[TModel, AfterValidator(lambda v: v.model_dump(exclude_none=True, by_alias=True))] 27 | 28 | 29 | class ValidateCallWrapper(PydanticValidateCallWrapper): 30 | __slots__ = () 31 | 32 | def __init__( 33 | self, 34 | function: ValidateCallSupportedTypes, 35 | config: ConfigDict | None, 36 | validate_return: bool, 37 | parent_namespace: MappingNamespace | None, 38 | response: type[BaseModel] | None = None, 39 | ) -> None: 40 | super().__init__(function, config, validate_return, parent_namespace) 41 | if response: 42 | if isinstance(function, partial): # pragma: no cover 43 | schema_type = function.func 44 | module = function.func.__module__ 45 | else: 46 | schema_type = function 47 | module = function.__module__ 48 | qualname = extract_function_qualname(function) 49 | 50 | ns_resolver = NsResolver(namespaces_tuple=ns_for_function(schema_type, parent_namespace=parent_namespace)) 51 | config_wrapper = ConfigWrapper(config) 52 | core_config = config_wrapper.core_config(title=qualname) 53 | 54 | gen_schema = GenerateSchema(config_wrapper, ns_resolver) 55 | schema = gen_schema.clean_schema(gen_schema.generate_schema(response)) 56 | validator = create_schema_validator( 57 | schema, 58 | schema_type, 59 | module, 60 | qualname, 61 | 'validate_call', 62 | core_config, 63 | config_wrapper.plugin_settings, 64 | ) 65 | if inspect.iscoroutinefunction(function): # pragma: no cover 66 | 67 | async def return_val_wrapper(aw: Awaitable[Any]) -> None: 68 | return validator.validate_python(await aw) 69 | 70 | self.__return_pydantic_validator__ = return_val_wrapper 71 | else: 72 | self.__return_pydantic_validator__ = validator.validate_python 73 | 74 | 75 | APICLIENT_METHODS: set[str] = {i[0] for i in inspect.getmembers(APIClient, predicate=inspect.isfunction)} 76 | 77 | 78 | def serialize( 79 | __func: AnyCallableT | None = None, 80 | /, 81 | *, 82 | config: ConfigDict | None = None, 83 | validate_return: bool = True, 84 | response: type[BaseModel] | None = None, 85 | ) -> AnyCallableT | Callable[[AnyCallableT], AnyCallableT]: 86 | parent_namespace = _typing_extra.parent_frame_namespace() 87 | 88 | def validate(function: AnyCallableT) -> AnyCallableT: 89 | _check_function_type(function) 90 | validate_call_wrapper = ValidateCallWrapper( 91 | cast(_generate_schema.ValidateCallSupportedTypes, function), 92 | config, 93 | validate_return, 94 | parent_namespace, 95 | response, 96 | ) 97 | return _validate_call.update_wrapper_attributes(function, validate_call_wrapper.__call__) # type:ignore[arg-type] 98 | 99 | if __func: 100 | return validate(__func) 101 | return validate 102 | 103 | 104 | def serialize_all_methods( 105 | __cls: type[T] | None = None, /, *, config: ConfigDict | None = None 106 | ) -> AnyCallableT | Callable[[AnyCallableT], AnyCallableT] | Callable[[type[T]], type[T]]: 107 | def decorate(cls: type[T]) -> type[T]: 108 | for attr, value in vars(cls).items(): 109 | if not attr.startswith('_') and inspect.isfunction(value) and attr not in APICLIENT_METHODS: 110 | setattr(cls, attr, serialize(value, config=config)) 111 | return cls 112 | 113 | if __cls: 114 | return decorate(__cls) 115 | return decorate 116 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "api-client-pydantic" 3 | version = "3.1.0" 4 | description = "API Client extension for validate and transform requests / responses using pydantic." 5 | authors = ["MaxST "] 6 | license = "MIT" 7 | repository = 'https://github.com/mom1/api-client-pydantic' 8 | homepage = 'https://github.com/mom1/api-client-pydantic' 9 | keywords = ['api-client', 'api-client-extension'] 10 | readme = 'README.md' 11 | classifiers = [ 12 | 'Development Status :: 5 - Production/Stable', 13 | 'Environment :: Web Environment', 14 | 'Intended Audience :: Developers', 15 | 'License :: OSI Approved :: MIT License', 16 | 'Operating System :: OS Independent', 17 | 'Programming Language :: Python', 18 | 'Programming Language :: Python :: 3.8', 19 | 'Programming Language :: Python :: 3.9', 20 | 'Programming Language :: Python :: 3.10', 21 | 'Programming Language :: Python :: 3.11', 22 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 23 | ] 24 | include = ['AUTHORS'] 25 | packages = [ 26 | { include = 'apiclient_pydantic' }, 27 | ] 28 | 29 | [tool.poetry.dependencies] 30 | python = ">=3.9,<4" 31 | api-client = ">1.2.1" 32 | pydantic = ">=2.10,<3.0" 33 | 34 | [tool.poetry.dev-dependencies] 35 | # tests 36 | coverage = "^7.6" 37 | pytest = "^8.3" 38 | pytest-cov = "^6.0" 39 | pytest-asyncio = "^0.25" 40 | # formating 41 | ruff = "^0.8" 42 | mypy = "^1.10" 43 | types-setuptools = "^75.6" 44 | types-toml = "^0.10" 45 | # checks 46 | pre-commit = "^4.0" 47 | 48 | [build-system] 49 | requires = ["poetry-core>=1.8"] 50 | build-backend = "poetry.core.masonry.api" 51 | 52 | [tool.ruff] 53 | # ruff configuration: 54 | # https://docs.astral.sh/ruff/settings/#top-level 55 | target-version = "py39" 56 | fix = true 57 | unsafe-fixes = true 58 | line-length = 120 59 | extend-exclude = ["apiclient_pydantic/v1"] 60 | 61 | [tool.ruff.lint] 62 | select = ["ALL"] 63 | fixable = ["ALL"] 64 | extend-ignore = [ 65 | "PGH003", 66 | "FBT", # flake8-boolean-trap 67 | "FA", # flake8-future-annotations 68 | "D100", # Missing docstring in public module 69 | "D101", # Missing docstring in public class 70 | "D102", # Missing docstring in public method 71 | "D103", # Missing docstring in public function 72 | "D104", # Missing docstring in public package 73 | "D105", # Missing docstring in magic method 74 | "D106", # Missing docstring in public nested class 75 | "D107", # Missing docstring in __init__ 76 | "D203", # 1 blank line required before class docstring 77 | "D213", # Multi-line docstring summary should start at the second line 78 | "D301", # Use r""" if any backslashes in a docstring 79 | "D401", # The First line of docstring should be in imperative mood 80 | "D413", # Missing blank line after last section ("{name}") 81 | "DJ012", # django-unordered-body-content-in-model 82 | "E203", # Whitespace around slice operators 83 | "G004", # logging-f-string 84 | "PLR0913", # too-many-arguments 85 | "RET501", # Do not explicitly `return None` in function if it is the only possible return value 86 | "RET502", # Do not implicitly `return None` in function able to return non-`None` value 87 | "RET503", # Missing explicit `return` at the end of function able to return non-`None` value 88 | "RUF001", # ambiguous-unicode-character-string 89 | "RUF002", # ambiguous-unicode-character-docstring 90 | "RUF003", # ambiguous-unicode-character-comment 91 | "RUF012", # Mutable class attributes should be annotated with typing.ClassVar 92 | "S105", # hardcoded-password-string 93 | "S608", # hardcoded-sql-expression 94 | "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass 95 | # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules 96 | "COM812", # missing-trailing-comma 97 | "COM819", # prohibited-trailing-comma 98 | "D206", # indent-with-spaces 99 | "D300", # triple-single-quotes 100 | "E111", # indentation-with-invalid-multiple 101 | "E114", # indentation-with-invalid-multiple-comment 102 | "E117", # over-indented 103 | "ISC001", # single-line-implicit-string-concatenation 104 | "ISC002", # multi-line-implicit-string-concatenation 105 | "Q000", # bad-quotes-inline-string 106 | "Q001", # bad-quotes-multiline-string 107 | "Q002", # bad-quotes-docstring 108 | "Q003", # avoidable-escaped-quote 109 | "W191", # tab-indentation 110 | ] 111 | flake8-quotes = { inline-quotes = "single" } 112 | 113 | [tool.ruff.lint.per-file-ignores] 114 | "tests/**/*.py" = [ 115 | "S101", # assert 116 | "S106", # hardcoded-password-func-arg 117 | "S311", # suspicious-non-cryptographic-random-usage 118 | "PLR2004", # magic-value-comparison 119 | "ANN", # flake8-annotations 120 | ] 121 | 122 | [tool.ruff.format] 123 | # https://docs.astral.sh/ruff/settings/#format 124 | quote-style = "single" 125 | 126 | [tool.ruff.lint.isort] 127 | # https://docs.astral.sh/ruff/settings/#isort 128 | combine-as-imports = true 129 | known-first-party = ["apiclient_pydantic", "tests"] 130 | known-local-folder = ["."] 131 | 132 | [tool.mypy] 133 | # https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file 134 | pretty = true 135 | show_traceback = true 136 | color_output = true 137 | exclude = ["apiclient_pydantic/v1"] 138 | 139 | plugins = ["pydantic.mypy"] 140 | 141 | follow_imports = "silent" 142 | strict_optional = true 143 | warn_redundant_casts = true 144 | warn_unused_ignores = true 145 | disallow_any_generics = true 146 | check_untyped_defs = true 147 | no_implicit_reexport = true 148 | disallow_untyped_defs = true 149 | ignore_missing_imports = true 150 | 151 | [tool.pydantic-mypy] 152 | init_forbid_extra = true 153 | init_typed = true 154 | warn_required_dynamic_aliases = true 155 | 156 | [[tool.mypy.overrides]] 157 | module = [ 158 | 'pydantic_core.*', 159 | 'apiclient.*', 160 | ] 161 | follow_imports = "skip" 162 | 163 | 164 | [tool.coverage.run] 165 | # Coverage configuration: 166 | # https://coverage.readthedocs.io/en/latest/config.html 167 | # https://coverage.readthedocs.io/en/latest/config.html#run 168 | branch = true 169 | source = ["apiclient_pydantic"] 170 | omit = ["tests", ".github"] 171 | 172 | [tool.coverage.report] 173 | # https://coverage.readthedocs.io/en/latest/config.html#report 174 | fail_under = 100 175 | show_missing = true 176 | exclude_lines = [ 177 | "pragma: no cover", 178 | "if TYPE_CHECKING:", 179 | "raise AssertionError", 180 | "raise NotImplementedError", 181 | "if __name__ == .__main__.:", 182 | ] 183 | 184 | [tool.pytest.ini_options] 185 | # pytest configuration: 186 | # https://docs.pytest.org/en/stable/customize.html 187 | python_files = ["tests.py", "test_*.py", "*_tests.py"] 188 | 189 | asyncio_mode = 'auto' 190 | asyncio_default_fixture_loop_scope = 'function' 191 | 192 | # Directories that are not visited by pytest collector: 193 | norecursedirs = [ 194 | "*.egg", 195 | ".eggs", 196 | "dist", 197 | "build", 198 | "docs", 199 | ".tox", 200 | ".git", 201 | "__pycache__", 202 | ] 203 | addopts = [ 204 | "-v", 205 | "--strict-markers", 206 | "--strict-config", 207 | "--tb=short", 208 | "--cov=apiclient_pydantic", 209 | "--cov-report=term-missing:skip-covered", 210 | "--cov-report=html", 211 | "--no-cov-on-fail", 212 | ] 213 | filterwarnings = ["ignore:A private pytest class or function was used.:_pytest.warning_types.PytestDeprecationWarning"] 214 | -------------------------------------------------------------------------------- /tests/test_serializers_v1.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | import pytest 4 | from apiclient import APIClient 5 | from pydantic.v1 import BaseModel, ValidationError 6 | from pydantic.v1.config import Extra 7 | from pydantic.v1.fields import Field 8 | 9 | from apiclient_pydantic.v1 import params_serializer, serialize_all_methods 10 | 11 | 12 | class SimpleModel(BaseModel): 13 | test_attr: str = 'Param' 14 | 15 | 16 | class SimpleConfigModel(BaseModel): 17 | test_attr: str = Field(alias='TestAttr') 18 | 19 | class Config: 20 | allow_population_by_field_name = True 21 | 22 | 23 | class SimpleTestModel(BaseModel): 24 | test: str 25 | 26 | 27 | class ForwardrefModel(BaseModel): 28 | test_attr: str = 'Param' 29 | this: Optional['ForwardrefModel'] = None 30 | 31 | 32 | class ForbidModel(BaseModel): 33 | test_attr: str = 'Param' 34 | 35 | class Config: 36 | extra = Extra.forbid 37 | 38 | 39 | @serialize_all_methods() 40 | class Client(APIClient): 41 | def function_without_all(self): 42 | return ['test'] 43 | 44 | def function_simple_arg(self, pk: int): 45 | return pk 46 | 47 | def function_simple_response(self) -> int: 48 | return '1' # type: ignore 49 | 50 | def function_simple_response_and_arg(self, pk: int) -> bool: 51 | assert isinstance(pk, int) 52 | return pk # type: ignore 53 | 54 | def function_simple_auto_type_str(self, pk): 55 | return pk 56 | 57 | def function_simple_args(self, *args): 58 | return args 59 | 60 | def function_simple_kwargs(self, **kwargs): 61 | return kwargs 62 | 63 | def function_simple_model(self, param: SimpleModel): 64 | return param 65 | 66 | def function_simple_model_args(self, param: SimpleModel, test_attr: str): 67 | return test_attr, param 68 | 69 | def function_forbid_model(self, param: ForbidModel): 70 | return param 71 | 72 | def function_type_from_default(self, pk=1): 73 | return pk 74 | 75 | def function_special_type(self, pk: None.__class__, name: ....__class__): # type: ignore 76 | return pk, name 77 | 78 | def function_return_none(self, pk=1) -> None: 79 | pass 80 | 81 | def function_forwardref(self, data: 'ForwardrefModel'): 82 | return data 83 | 84 | def function_union(self, data: Union[SimpleTestModel, SimpleModel]): 85 | return data 86 | 87 | def function_list_response(self, data: SimpleModel) -> list[SimpleModel]: 88 | return [data] 89 | 90 | def function_config_test(self, data: SimpleConfigModel): 91 | return data 92 | 93 | def function_same_name_test(self, test_attr: SimpleModel): 94 | return test_attr 95 | 96 | async def async_function_simple_model(self, param: SimpleModel) -> SimpleTestModel: 97 | return {'test': param['test_attr']} # type: ignore 98 | 99 | async def async_function_return_none(self, param: SimpleModel) -> None: 100 | pass 101 | 102 | 103 | @pytest.fixture 104 | def client(): 105 | return Client() 106 | 107 | 108 | def test_function_without_all(client): 109 | # not wrapped 110 | assert '__wrapped__' not in vars(client.function_without_all) 111 | 112 | assert client.function_without_all() == ['test'] 113 | 114 | 115 | def test_function_simple_arg(client): 116 | # wrapped once 117 | assert '__wrapped__' in vars(client.function_simple_arg) 118 | assert '__wrapped__' not in vars(vars(client.function_simple_arg)['__wrapped__']) 119 | 120 | assert client.function_simple_arg(pk='1') == 1 # type: ignore 121 | 122 | 123 | def test_send_args(client): 124 | assert client.function_simple_arg('1') == 1 # type: ignore 125 | 126 | 127 | def test_function_simple_response(client): 128 | # wrapped once 129 | assert '__wrapped__' in vars(client.function_simple_response) 130 | assert '__wrapped__' not in vars(vars(client.function_simple_response)['__wrapped__']) 131 | 132 | assert client.function_simple_response() == 1 # type: ignore 133 | 134 | 135 | def test_function_simple_response_and_arg(client): 136 | # wrapped 137 | assert '__wrapped__' in vars(client.function_simple_response_and_arg) 138 | assert '__wrapped__' in vars(vars(client.function_simple_response_and_arg)['__wrapped__']) 139 | assert '__wrapped__' not in vars(vars(vars(client.function_simple_response_and_arg)['__wrapped__'])['__wrapped__']) 140 | 141 | assert client.function_simple_response_and_arg(pk='0') is False # type: ignore 142 | 143 | 144 | def test_function_simple_auto_type_str(client): 145 | assert client.function_simple_auto_type_str(pk=0) == '0' 146 | 147 | 148 | def test_function_simple_args(client): 149 | assert client.function_simple_args('test') == ('test',) 150 | 151 | 152 | def test_function_simple_kwargs(client): 153 | assert client.function_simple_kwargs(test='test') == {'test': 'test'} 154 | 155 | 156 | def test_function_simple_model(client): 157 | assert client.function_simple_model() == {'test_attr': 'Param'} # type: ignore 158 | assert client.function_simple_model(test_attr='test') == {'test_attr': 'test'} # type: ignore 159 | 160 | 161 | def test_function_simple_model_args(client): 162 | assert client.function_simple_model_args(test_attr='test') == ('test', {'test_attr': 'test'}) # type: ignore 163 | 164 | 165 | def test_function_forbid_model(client): 166 | assert client.function_forbid_model(test_attr='test') == {'test_attr': 'test'} # type: ignore 167 | assert client.function_forbid_model(test_attr='test', test='bla') == {'test_attr': 'test'} # type: ignore 168 | 169 | 170 | def test_function_type_from_default(client): 171 | assert client.function_type_from_default(pk='2') == 2 # type: ignore 172 | 173 | 174 | def test_function_special_type(client): 175 | assert client.function_special_type(pk=2, name=True) == ('2', 'True') # type: ignore 176 | 177 | 178 | def test_function_return_none(client): 179 | assert client.function_return_none(pk=2) is None # type: ignore 180 | 181 | 182 | def test_function_forwardref(client): 183 | assert client.function_forwardref(test_attr='123', this={'test_attr': 456}) == { # type: ignore 184 | 'test_attr': '123', 185 | 'this': {'test_attr': '456'}, 186 | } 187 | 188 | 189 | def test_function_union(client): 190 | assert client.function_union(test='bla') == {'test': 'bla'} # type: ignore 191 | assert client.function_union(test_attr='bla') == {'test_attr': 'bla'} # type: ignore 192 | 193 | 194 | def test_function_list_response(client): 195 | assert client.function_list_response(test_attr='bla') == [{'test_attr': 'bla'}] # type: ignore 196 | 197 | 198 | def test_function_config_test(client): 199 | assert client.function_config_test(test_attr='bla') == {'TestAttr': 'bla'} # type: ignore 200 | 201 | 202 | @pytest.mark.asyncio 203 | async def test_async_function_return_none(client): 204 | response = await client.async_function_return_none(test_attr='test') # type: ignore 205 | assert response is None 206 | 207 | 208 | @pytest.mark.asyncio 209 | async def test_async_function_simple_model(client): 210 | response = await client.async_function_simple_model() # type: ignore 211 | assert isinstance(response, SimpleTestModel) 212 | assert response.dict() == {'test': 'Param'} 213 | 214 | response = await client.async_function_simple_model(test_attr='test') # type: ignore 215 | assert isinstance(response, SimpleTestModel) 216 | assert response.dict() == {'test': 'test'} 217 | 218 | 219 | @pytest.mark.xfail 220 | def test_function_same_name_test(client): 221 | with pytest.raises(ValidationError): 222 | assert client.function_same_name_test(test_attr='bla') == {'test_attr': 'bla'} # type: ignore 223 | 224 | 225 | def test_param_for_model(): 226 | class MyModel(BaseModel): 227 | test: str = Field(alias='TesT') 228 | 229 | @params_serializer() 230 | def function_by_alias(data: MyModel): 231 | return data 232 | 233 | @params_serializer(by_alias=False) 234 | def function(data: MyModel): 235 | return data 236 | 237 | assert function_by_alias(TesT='bla') == {'TesT': 'bla'} 238 | assert function(TesT='bla') == {'test': 'bla'} 239 | -------------------------------------------------------------------------------- /apiclient_pydantic/v1/serializers.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import inspect 3 | from functools import wraps 4 | from typing import Any, Callable, Dict, ForwardRef, Optional, Set, Tuple, Type, get_type_hints 5 | 6 | from pydantic.v1 import BaseModel, create_model, parse_obj_as 7 | from pydantic.v1.config import BaseConfig as PydanticBaseConfig, Extra 8 | from pydantic.v1.typing import evaluate_forwardref 9 | 10 | from .utils import is_pydantic_model 11 | 12 | DictStrAny = Dict[str, Any] 13 | 14 | 15 | class BaseConfig(PydanticBaseConfig): 16 | orm_mode = True 17 | 18 | 19 | class ParamsObject: 20 | def __init__(self, **kwargs: DictStrAny) -> None: 21 | for attr, param in kwargs.items(): 22 | setattr(self, attr, param) 23 | 24 | 25 | def get_typed_signature(call: Callable) -> inspect.Signature: 26 | """Finds call signature and resolves all forwardrefs.""" 27 | signature = inspect.signature(call) 28 | globalns = getattr(call, '__globals__', {}) 29 | typed_params = [ 30 | inspect.Parameter( 31 | name=param.name, 32 | kind=param.kind, 33 | default=param.default, 34 | annotation=get_typed_annotation(param, globalns), 35 | ) 36 | for param in signature.parameters.values() 37 | ] 38 | return inspect.Signature(typed_params) 39 | 40 | 41 | def get_typed_annotation(param: inspect.Parameter, globalns: DictStrAny) -> Any: 42 | annotation = param.annotation 43 | if isinstance(annotation, str): 44 | annotation = make_forwardref(annotation, globalns) 45 | return annotation 46 | 47 | 48 | def make_forwardref(annotation: str, globalns: DictStrAny) -> Any: 49 | forward_ref = ForwardRef(annotation) 50 | return evaluate_forwardref(forward_ref, globalns, globalns) 51 | 52 | 53 | class ParamsSerializer: 54 | __slot__ = ( 55 | 'signature', 56 | 'by_alias', 57 | 'exclude_unset', 58 | 'exclude_defaults', 59 | 'exclude_none', 60 | 'has_kwargs', 61 | 'model_param', 62 | 'params_object_class', 63 | 'used_args_index', 64 | ) 65 | 66 | params_object_class = ParamsObject 67 | used_args_index: Set = set() 68 | 69 | def __init__( 70 | self, 71 | by_alias: bool = True, 72 | exclude_unset: bool = False, 73 | exclude_defaults: bool = False, 74 | exclude_none: bool = True, 75 | ) -> None: 76 | self.by_alias = by_alias 77 | self.exclude_unset = exclude_unset 78 | self.exclude_defaults = exclude_defaults 79 | self.exclude_none = exclude_none 80 | self.has_self = False 81 | self.has_kwargs = False 82 | 83 | def __call__(self, func: Callable) -> Callable: 84 | attrs, self.signature = {}, get_typed_signature(func) 85 | new_signature_parameters: DictStrAny = {} 86 | forbid_attrs = set() 87 | 88 | for name, arg in self.signature.parameters.items(): 89 | if name == 'self': 90 | new_signature_parameters.setdefault(arg.name, arg) 91 | self.has_self = True 92 | continue 93 | 94 | if arg.kind == arg.VAR_KEYWORD: 95 | # Skipping **kwargs 96 | self.has_kwargs = True 97 | continue 98 | 99 | if arg.kind == arg.VAR_POSITIONAL: 100 | # Skipping *args 101 | continue 102 | 103 | arg_type = self._get_param_type(arg) 104 | 105 | if name not in new_signature_parameters: 106 | if is_pydantic_model(arg_type): 107 | new_signature_parameters.update( 108 | {argument.name: argument for argument in inspect.signature(arg_type).parameters.values()} 109 | ) 110 | else: 111 | new_signature_parameters.setdefault(arg.name, arg) 112 | 113 | attrs[name] = (arg_type, ...) 114 | if is_pydantic_model(arg_type) and getattr(arg_type.Config, 'extra', None) == Extra.forbid: 115 | forbid_attrs.add(name) 116 | if attrs: 117 | config_cls = type(f'{func.__name__}Config', (BaseConfig,), {'forbid_attrs': forbid_attrs}) 118 | self.model_param = create_model(f'{func.__name__}Params', __config__=config_cls, **attrs) # type: ignore 119 | 120 | if asyncio.iscoroutinefunction(func): 121 | 122 | @wraps(func) 123 | async def wrap(*args, **kwargs): 124 | params_object = self.make_object_params(args, kwargs) 125 | result = self.make_result(params_object) 126 | 127 | return await func( 128 | *tuple(v for i, v in enumerate(args) if i not in self.used_args_index), 129 | **result, 130 | ) 131 | 132 | else: 133 | 134 | @wraps(func) 135 | def wrap(*args, **kwargs): 136 | params_object = self.make_object_params(args, kwargs) 137 | result = self.make_result(params_object) 138 | 139 | return func( 140 | *tuple(v for i, v in enumerate(args) if i not in self.used_args_index), 141 | **result, 142 | ) 143 | 144 | # Override signature 145 | if new_signature_parameters and attrs: 146 | sig = inspect.signature(func) 147 | sig = sig.replace(parameters=tuple(sorted(new_signature_parameters.values(), key=lambda x: x.kind))) 148 | wrap.__signature__ = sig # type: ignore 149 | 150 | return wrap if attrs else func 151 | 152 | def make_object_params(self, args: Tuple, kwargs: DictStrAny) -> ParamsObject: 153 | object_params = {} 154 | used_args_index = self.used_args_index 155 | forbid_attrs = getattr(self.model_param.Config, 'forbid_attrs', {}) 156 | len_args = len(args) 157 | 158 | for index, (name, fld) in enumerate(self.model_param.__fields__.items(), start=int(self.has_self)): 159 | kw = kwargs 160 | if name in kwargs: 161 | kw = kwargs[name] 162 | elif len_args > index: 163 | kw = args[index] 164 | used_args_index.add(index) 165 | 166 | object_params[name] = kw 167 | if is_pydantic_model(fld.type_) and name in forbid_attrs and isinstance(kw, dict): 168 | object_params[name] = {k: v for k, v in kw.items() if k in fld.type_.__fields__} 169 | 170 | return self.params_object_class(**object_params) 171 | 172 | def make_result(self, params_object: ParamsObject) -> DictStrAny: 173 | return self.model_param.from_orm(params_object).dict( 174 | by_alias=self.by_alias, 175 | exclude_unset=self.exclude_unset, 176 | exclude_defaults=self.exclude_defaults, 177 | exclude_none=self.exclude_none, 178 | ) 179 | 180 | def _get_param_type(self, arg: inspect.Parameter) -> Any: 181 | annotation = arg.annotation 182 | 183 | if annotation == self.signature.empty: 184 | annotation = str if arg.default == self.signature.empty else type(arg.default) 185 | 186 | if annotation is type(None) or annotation is type(Ellipsis): 187 | annotation = str 188 | 189 | return annotation 190 | 191 | 192 | serialize_request = params_serializer = ParamsSerializer 193 | 194 | 195 | class ResponseSerializer: 196 | __slot__ = ('response',) 197 | 198 | def __init__(self, response: Optional[Type[BaseModel]] = None) -> None: 199 | self.response = response 200 | 201 | def __call__(self, func: Callable) -> Callable: 202 | self.response = self.response or get_type_hints(func).get('return') 203 | if asyncio.iscoroutinefunction(func): 204 | 205 | @wraps(func) 206 | async def wrap(*args, **kwargs): 207 | result = await func(*args, **kwargs) 208 | if result is not None: 209 | return parse_obj_as(self.response, result) 210 | return result 211 | 212 | else: 213 | 214 | @wraps(func) 215 | def wrap(*args, **kwargs): 216 | result = func(*args, **kwargs) 217 | if result is not None: 218 | return parse_obj_as(self.response, result) 219 | return result 220 | 221 | return wrap if self.response else func 222 | 223 | 224 | serialize_response = response_serializer = ResponseSerializer 225 | 226 | 227 | def serialize( 228 | response: Optional[Type[BaseModel]] = None, 229 | by_alias: bool = True, 230 | exclude_unset: bool = False, 231 | exclude_defaults: bool = False, 232 | exclude_none: bool = True, 233 | ) -> Callable: 234 | def decorator(func: Callable) -> Callable: 235 | result_func = ParamsSerializer( 236 | by_alias=by_alias, 237 | exclude_unset=exclude_unset, 238 | exclude_defaults=exclude_defaults, 239 | exclude_none=exclude_none, 240 | )(func) 241 | return serialize_response(response=response)(result_func) 242 | 243 | return decorator 244 | 245 | 246 | def serialize_all_methods(decorator=serialize): 247 | def decorate(cls): 248 | for attr, value in vars(cls).items(): 249 | if not attr.startswith('_') and (inspect.ismethod(value) or inspect.isfunction(value)): 250 | setattr(cls, attr, decorator()(value)) 251 | return cls 252 | 253 | return decorate 254 | -------------------------------------------------------------------------------- /tests/test_serializers.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | from typing import Annotated, Union 3 | 4 | import pytest 5 | from apiclient import APIClient 6 | from pydantic import AfterValidator, BaseModel, BeforeValidator, ConfigDict, Field, ValidationError 7 | 8 | from apiclient_pydantic import ModelDumped, TModel, serialize, serialize_all_methods 9 | 10 | ModelDumpedNotAlias = Annotated[TModel, AfterValidator(lambda v: v.model_dump(exclude_none=True, by_alias=False))] 11 | 12 | 13 | class SimpleModel(BaseModel): 14 | test_attr: str = 'Param' 15 | 16 | 17 | class SimpleConfigModel(BaseModel): 18 | test_attr: str = Field(alias='TestAttr') 19 | model_config = ConfigDict(populate_by_name=True) 20 | 21 | 22 | class SimpleTestModel(BaseModel): 23 | test: str 24 | 25 | 26 | class ForwardrefModel(BaseModel): 27 | test_attr: Annotated[str, BeforeValidator(str)] = 'Param' 28 | this: 'ForwardrefModel' = None 29 | 30 | 31 | class ForbidModel(BaseModel): 32 | test_attr: str = 'Param' 33 | model_config = ConfigDict(extra='forbid') 34 | 35 | 36 | @serialize_all_methods 37 | class Client(APIClient): 38 | def function_without_all(self): 39 | return ['test'] 40 | 41 | def function_simple_arg(self, pk: int): 42 | return pk 43 | 44 | def function_simple_response(self) -> int: 45 | return '1' 46 | 47 | def function_simple_response_and_arg(self, pk: int) -> bool: 48 | assert isinstance(pk, int) 49 | return pk 50 | 51 | def function_simple_auto_type_str(self, pk): 52 | return pk 53 | 54 | def function_simple_args(self, *args): 55 | return args 56 | 57 | def function_simple_kwargs(self, **kwargs): 58 | return kwargs 59 | 60 | def function_simple_model( 61 | self, 62 | param: Annotated[ModelDumped[SimpleModel], Field(validate_default=True, default_factory=SimpleModel)] = None, 63 | ): 64 | return param 65 | 66 | def function_simple_model_args(self, param: ModelDumped[SimpleModel], test_attr: str): 67 | return test_attr, param 68 | 69 | def function_forbid_model(self, param: ModelDumped[ForbidModel]): 70 | return param 71 | 72 | @serialize(config=ConfigDict(arbitrary_types_allowed=True)) 73 | def function_special_type(self, pk: None.__class__): 74 | return pk 75 | 76 | @serialize(validate_return=False) 77 | def function_return_none(self, pk=1) -> None: 78 | pass 79 | 80 | def function_forwardref(self, data: ModelDumped[ForwardrefModel]): 81 | return data 82 | 83 | def function_union(self, data: ModelDumped[Union[SimpleTestModel, SimpleModel]]): 84 | return data 85 | 86 | def function_list_response(self, data: ModelDumped[SimpleModel]) -> list[dict[str, str]]: 87 | return [data] 88 | 89 | def function_config_test(self, data: ModelDumped[SimpleConfigModel]): 90 | return data 91 | 92 | def function_same_name_test(self, test_attr: ModelDumped[SimpleModel]): 93 | return test_attr 94 | 95 | async def async_function_simple_model( 96 | self, 97 | param: Annotated[ModelDumped[SimpleModel], Field(validate_default=True, default_factory=SimpleModel)] = None, 98 | ) -> SimpleTestModel: 99 | return {'test': param['test_attr']} 100 | 101 | async def async_function_return_none(self, param: SimpleModel) -> None: 102 | pass 103 | 104 | 105 | @pytest.fixture 106 | def client() -> Client: 107 | return Client() 108 | 109 | 110 | def test_function_without_all(client): 111 | assert client.function_without_all() == ['test'] 112 | 113 | 114 | def test_function_simple_arg(client): 115 | assert client.function_simple_arg(pk='1') == 1 116 | 117 | 118 | def test_send_args(client): 119 | assert client.function_simple_arg('1') == 1 120 | 121 | 122 | def test_function_simple_response(client): 123 | assert client.function_simple_response() == 1 124 | 125 | 126 | def test_function_simple_response_and_arg(client): 127 | assert client.function_simple_response_and_arg(pk='0') is False 128 | 129 | 130 | def test_function_simple_auto_type_str(client): 131 | assert client.function_simple_auto_type_str(pk='0') == '0' 132 | assert client.function_simple_auto_type_str(pk=0) == 0 133 | 134 | 135 | def test_function_simple_args(client): 136 | assert client.function_simple_args('test') == ('test',) 137 | assert client.function_simple_args(111) == (111,) 138 | 139 | 140 | def test_function_simple_kwargs(client): 141 | assert client.function_simple_kwargs(test='test') == {'test': 'test'} 142 | 143 | 144 | def test_function_simple_model(client): 145 | assert client.function_simple_model() == {'test_attr': 'Param'} 146 | assert client.function_simple_model(param={'test_attr': 'test'}) == {'test_attr': 'test'} 147 | 148 | 149 | def test_function_simple_model_args(client): 150 | assert client.function_simple_model_args(param={'test_attr': 'test'}, test_attr='test') == ( 151 | 'test', 152 | {'test_attr': 'test'}, 153 | ) 154 | 155 | 156 | def test_function_forbid_model(client): 157 | assert client.function_forbid_model(param={'test_attr': 'test'}) == {'test_attr': 'test'} 158 | with pytest.raises(ValidationError): 159 | assert client.function_forbid_model(param={'test_attr': 'test', 'test': 'bla'}) 160 | 161 | 162 | def test_function_special_type(client): 163 | assert client.function_special_type(pk=None) is None 164 | with pytest.raises(ValidationError): 165 | assert client.function_special_type(pk=2) 166 | 167 | 168 | def test_function_return_none(client): 169 | assert client.function_return_none(pk=2) is None 170 | 171 | 172 | def test_function_forwardref(client): 173 | assert client.function_forwardref(data={'test_attr': '123', 'this': {'test_attr': 456}}) == { 174 | 'test_attr': '123', 175 | 'this': {'test_attr': '456'}, 176 | } 177 | 178 | 179 | def test_function_union(client): 180 | assert client.function_union(data={'test': 'bla'}) == {'test': 'bla'} 181 | assert client.function_union(data={'test_attr': 'bla'}) == {'test_attr': 'bla'} 182 | 183 | 184 | def test_function_list_response(client): 185 | assert client.function_list_response(data={'test_attr': 'bla'}) == [{'test_attr': 'bla'}] 186 | 187 | 188 | def test_function_config_test(client): 189 | assert client.function_config_test(data={'test_attr': 'bla'}) == {'TestAttr': 'bla'} 190 | 191 | 192 | @pytest.mark.asyncio 193 | async def test_async_function_return_none(client): 194 | response = await client.async_function_return_none(param={'test_attr': 'test'}) 195 | assert response is None 196 | 197 | 198 | @pytest.mark.asyncio 199 | async def test_async_function_simple_model(client): 200 | response = await client.async_function_simple_model() 201 | assert isinstance(response, SimpleTestModel) 202 | assert response.model_dump(by_alias=True, exclude_none=True) == {'test': 'Param'} 203 | 204 | response = await client.async_function_simple_model(param={'test_attr': 'test'}) 205 | assert isinstance(response, SimpleTestModel) 206 | assert response.model_dump(by_alias=True, exclude_none=True) == {'test': 'test'} 207 | 208 | 209 | @pytest.mark.xfail 210 | def test_function_same_name_test(client): 211 | with pytest.raises(ValidationError): 212 | assert client.function_same_name_test(test_attr='bla') == {'test_attr': 'bla'} 213 | 214 | 215 | def test_param_for_model(): 216 | class MyModel(BaseModel): 217 | model_config = ConfigDict(populate_by_name=True) 218 | test: str = Field(alias='TesT') 219 | 220 | @serialize_all_methods() 221 | class Client1(APIClient): 222 | @serialize 223 | def function_by_alias(self, data: ModelDumped[MyModel]): 224 | return data 225 | 226 | @serialize 227 | def function(self, data: ModelDumpedNotAlias[MyModel]): 228 | return data 229 | 230 | client = Client1() 231 | assert client.function_by_alias(data={'TesT': 'bla'}) == {'TesT': 'bla'} 232 | assert client.function(data={'TesT': 'bla'}) == {'test': 'bla'} 233 | 234 | 235 | def test_classmethod_error(): 236 | with pytest.raises(TypeError, match='The `@classmethod` decorator should be applied after'): 237 | 238 | class Client2(APIClient): 239 | @serialize 240 | @classmethod 241 | def function(cls, data: ModelDumped[SimpleModel]): 242 | return data 243 | 244 | 245 | def test_staticmethod_error(): 246 | with pytest.raises(TypeError, match='The `@staticmethod` decorator should be applied after'): 247 | 248 | class Client2(APIClient): 249 | @serialize 250 | @staticmethod 251 | def function(_, data: ModelDumped[SimpleModel]): 252 | return data 253 | 254 | 255 | def test_classmethod(): 256 | class Client2(APIClient): 257 | @classmethod 258 | @serialize 259 | def function(cls, data: ModelDumped[SimpleModel]): 260 | return data 261 | 262 | client = Client2() 263 | assert client.function(data={'TesT': 'bla'}) == {'test_attr': 'Param'} 264 | 265 | 266 | def test_partial_func(): 267 | def function(data: ModelDumped[SimpleModel], test: str): 268 | return test, data 269 | 270 | func = partial(function, test='321') 271 | f = serialize(func) 272 | assert f({'test_attr': 'bla'}) == ('321', {'test_attr': 'bla'}) 273 | 274 | 275 | def test_response_model(): 276 | class Client2(APIClient): 277 | @serialize(response=SimpleModel) 278 | def function_response_model(self): 279 | return {'test_attr': 'test1'} 280 | 281 | client = Client2() 282 | assert isinstance(client.function_response_model(), SimpleModel) 283 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,visualstudio,pycharm+all,sublimetext,macos,windows,linux 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,visualstudio,pycharm+all,sublimetext,macos,windows,linux 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | 30 | # Thumbnails 31 | ._* 32 | 33 | # Files that might appear in the root of a volume 34 | .DocumentRevisions-V100 35 | .fseventsd 36 | .Spotlight-V100 37 | .TemporaryItems 38 | .Trashes 39 | .VolumeIcon.icns 40 | .com.apple.timemachine.donotpresent 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | ### PyCharm+all ### 50 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 51 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 52 | 53 | # User-specific stuff 54 | .idea/**/workspace.xml 55 | .idea/**/tasks.xml 56 | .idea/**/usage.statistics.xml 57 | .idea/**/dictionaries 58 | .idea/**/shelf 59 | 60 | # AWS User-specific 61 | .idea/**/aws.xml 62 | 63 | # Generated files 64 | .idea/**/contentModel.xml 65 | 66 | # Sensitive or high-churn files 67 | .idea/**/dataSources/ 68 | .idea/**/dataSources.ids 69 | .idea/**/dataSources.local.xml 70 | .idea/**/sqlDataSources.xml 71 | .idea/**/dynamic.xml 72 | .idea/**/uiDesigner.xml 73 | .idea/**/dbnavigator.xml 74 | 75 | # Gradle 76 | .idea/**/gradle.xml 77 | .idea/**/libraries 78 | 79 | # Gradle and Maven with auto-import 80 | # When using Gradle or Maven with auto-import, you should exclude module files, 81 | # since they will be recreated, and may cause churn. Uncomment if using 82 | # auto-import. 83 | # .idea/artifacts 84 | # .idea/compiler.xml 85 | # .idea/jarRepositories.xml 86 | # .idea/modules.xml 87 | # .idea/*.iml 88 | # .idea/modules 89 | # *.iml 90 | # *.ipr 91 | 92 | # CMake 93 | cmake-build-*/ 94 | 95 | # Mongo Explorer plugin 96 | .idea/**/mongoSettings.xml 97 | 98 | # File-based project format 99 | *.iws 100 | 101 | # IntelliJ 102 | out/ 103 | 104 | # mpeltonen/sbt-idea plugin 105 | .idea_modules/ 106 | 107 | # JIRA plugin 108 | atlassian-ide-plugin.xml 109 | 110 | # Cursive Clojure plugin 111 | .idea/replstate.xml 112 | 113 | # SonarLint plugin 114 | .idea/sonarlint/ 115 | 116 | # Crashlytics plugin (for Android Studio and IntelliJ) 117 | com_crashlytics_export_strings.xml 118 | crashlytics.properties 119 | crashlytics-build.properties 120 | fabric.properties 121 | 122 | # Editor-based Rest Client 123 | .idea/httpRequests 124 | 125 | # Android studio 3.1+ serialized cache file 126 | .idea/caches/build_file_checksums.ser 127 | 128 | ### PyCharm+all Patch ### 129 | # Ignores the whole .idea folder and all .iml files 130 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 131 | 132 | .idea/* 133 | 134 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 135 | 136 | *.iml 137 | modules.xml 138 | .idea/misc.xml 139 | *.ipr 140 | 141 | # Sonarlint plugin 142 | .idea/sonarlint 143 | 144 | # Fleet 145 | .fleet/* 146 | ### Python ### 147 | # Byte-compiled / optimized / DLL files 148 | __pycache__/ 149 | *.py[cod] 150 | *$py.class 151 | 152 | # C extensions 153 | *.so 154 | 155 | # Distribution / packaging 156 | .Python 157 | build/ 158 | develop-eggs/ 159 | dist/ 160 | downloads/ 161 | eggs/ 162 | .eggs/ 163 | lib/ 164 | lib64/ 165 | parts/ 166 | sdist/ 167 | var/ 168 | wheels/ 169 | share/python-wheels/ 170 | *.egg-info/ 171 | .installed.cfg 172 | *.egg 173 | MANIFEST 174 | 175 | # PyInstaller 176 | # Usually these files are written by a python script from a template 177 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 178 | *.manifest 179 | *.spec 180 | 181 | # Installer logs 182 | pip-log.txt 183 | pip-delete-this-directory.txt 184 | 185 | # Unit test / coverage reports 186 | htmlcov/ 187 | .tox/ 188 | .nox/ 189 | .coverage 190 | .coverage.* 191 | .cache 192 | nosetests.xml 193 | coverage.xml 194 | *.cover 195 | *.py,cover 196 | .hypothesis/ 197 | .pytest_cache/ 198 | cover/ 199 | 200 | # Translations 201 | *.mo 202 | *.pot 203 | 204 | # Django stuff: 205 | *.log 206 | local_settings.py 207 | db.sqlite3 208 | db.sqlite3-journal 209 | 210 | # Flask stuff: 211 | instance/ 212 | .webassets-cache 213 | 214 | # Scrapy stuff: 215 | .scrapy 216 | 217 | # Sphinx documentation 218 | docs/_build/ 219 | 220 | # PyBuilder 221 | .pybuilder/ 222 | target/ 223 | 224 | # Jupyter Notebook 225 | .ipynb_checkpoints 226 | 227 | # IPython 228 | profile_default/ 229 | ipython_config.py 230 | 231 | # pyenv 232 | # For a library or package, you might want to ignore these files since the code is 233 | # intended to run in multiple environments; otherwise, check them in: 234 | # .python-version 235 | 236 | # pipenv 237 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 238 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 239 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 240 | # install all needed dependencies. 241 | #Pipfile.lock 242 | 243 | # poetry 244 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 245 | # This is especially recommended for binary packages to ensure reproducibility, and is more 246 | # commonly ignored for libraries. 247 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 248 | #poetry.lock 249 | 250 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 251 | __pypackages__/ 252 | 253 | # Celery stuff 254 | celerybeat-schedule 255 | celerybeat.pid 256 | 257 | # SageMath parsed files 258 | *.sage.py 259 | 260 | # Environments 261 | .env 262 | .venv 263 | env/ 264 | venv/ 265 | ENV/ 266 | env.bak/ 267 | venv.bak/ 268 | 269 | # Spyder project settings 270 | .spyderproject 271 | .spyproject 272 | 273 | # Rope project settings 274 | .ropeproject 275 | 276 | # mkdocs documentation 277 | /site 278 | 279 | # mypy 280 | .mypy_cache/ 281 | .dmypy.json 282 | dmypy.json 283 | 284 | # Pyre type checker 285 | .pyre/ 286 | 287 | # pytype static type analyzer 288 | .pytype/ 289 | 290 | # Cython debug symbols 291 | cython_debug/ 292 | 293 | # PyCharm 294 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 295 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 296 | # and can be added to the global gitignore or merged into this file. For a more nuclear 297 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 298 | #.idea/ 299 | 300 | ### SublimeText ### 301 | # Cache files for Sublime Text 302 | *.tmlanguage.cache 303 | *.tmPreferences.cache 304 | *.stTheme.cache 305 | 306 | # Workspace files are user-specific 307 | *.sublime-workspace 308 | 309 | # Project files should be checked into the repository, unless a significant 310 | # proportion of contributors will probably not be using Sublime Text 311 | # *.sublime-project 312 | 313 | # SFTP configuration file 314 | sftp-config.json 315 | sftp-config-alt*.json 316 | 317 | # Package control specific files 318 | Package Control.last-run 319 | Package Control.ca-list 320 | Package Control.ca-bundle 321 | Package Control.system-ca-bundle 322 | Package Control.cache/ 323 | Package Control.ca-certs/ 324 | Package Control.merged-ca-bundle 325 | Package Control.user-ca-bundle 326 | oscrypto-ca-bundle.crt 327 | bh_unicode_properties.cache 328 | 329 | # Sublime-github package stores a github token in this file 330 | # https://packagecontrol.io/packages/sublime-github 331 | GitHub.sublime-settings 332 | 333 | ### VisualStudioCode ### 334 | .vscode/* 335 | !.vscode/settings.json 336 | !.vscode/tasks.json 337 | !.vscode/launch.json 338 | !.vscode/extensions.json 339 | !.vscode/*.code-snippets 340 | 341 | # Local History for Visual Studio Code 342 | .history/ 343 | 344 | # Built Visual Studio Code Extensions 345 | *.vsix 346 | 347 | ### VisualStudioCode Patch ### 348 | # Ignore all local history of files 349 | .history 350 | .ionide 351 | 352 | # Support for Project snippet scope 353 | 354 | ### Windows ### 355 | # Windows thumbnail cache files 356 | Thumbs.db 357 | Thumbs.db:encryptable 358 | ehthumbs.db 359 | ehthumbs_vista.db 360 | 361 | # Dump file 362 | *.stackdump 363 | 364 | # Folder config file 365 | [Dd]esktop.ini 366 | 367 | # Recycle Bin used on file shares 368 | $RECYCLE.BIN/ 369 | 370 | # Windows Installer files 371 | *.cab 372 | *.msi 373 | *.msix 374 | *.msm 375 | *.msp 376 | 377 | # Windows shortcuts 378 | *.lnk 379 | 380 | ### VisualStudio ### 381 | ## Ignore Visual Studio temporary files, build results, and 382 | ## files generated by popular Visual Studio add-ons. 383 | ## 384 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 385 | 386 | # User-specific files 387 | *.rsuser 388 | *.suo 389 | *.user 390 | *.userosscache 391 | *.sln.docstates 392 | 393 | # User-specific files (MonoDevelop/Xamarin Studio) 394 | *.userprefs 395 | 396 | # Mono auto generated files 397 | mono_crash.* 398 | 399 | # Build results 400 | [Dd]ebug/ 401 | [Dd]ebugPublic/ 402 | [Rr]elease/ 403 | [Rr]eleases/ 404 | x64/ 405 | x86/ 406 | [Ww][Ii][Nn]32/ 407 | [Aa][Rr][Mm]/ 408 | [Aa][Rr][Mm]64/ 409 | bld/ 410 | [Bb]in/ 411 | [Oo]bj/ 412 | [Ll]og/ 413 | [Ll]ogs/ 414 | 415 | # Visual Studio 2015/2017 cache/options directory 416 | .vs/ 417 | # Uncomment if you have tasks that create the project's static files in wwwroot 418 | #wwwroot/ 419 | 420 | # Visual Studio 2017 auto generated files 421 | Generated\ Files/ 422 | 423 | # MSTest test Results 424 | [Tt]est[Rr]esult*/ 425 | [Bb]uild[Ll]og.* 426 | 427 | # NUnit 428 | *.VisualState.xml 429 | TestResult.xml 430 | nunit-*.xml 431 | 432 | # Build Results of an ATL Project 433 | [Dd]ebugPS/ 434 | [Rr]eleasePS/ 435 | dlldata.c 436 | 437 | # Benchmark Results 438 | BenchmarkDotNet.Artifacts/ 439 | 440 | # .NET Core 441 | project.lock.json 442 | project.fragment.lock.json 443 | artifacts/ 444 | 445 | # ASP.NET Scaffolding 446 | ScaffoldingReadMe.txt 447 | 448 | # StyleCop 449 | StyleCopReport.xml 450 | 451 | # Files built by Visual Studio 452 | *_i.c 453 | *_p.c 454 | *_h.h 455 | *.ilk 456 | *.meta 457 | *.obj 458 | *.iobj 459 | *.pch 460 | *.pdb 461 | *.ipdb 462 | *.pgc 463 | *.pgd 464 | *.rsp 465 | *.sbr 466 | *.tlb 467 | *.tli 468 | *.tlh 469 | *.tmp 470 | *.tmp_proj 471 | *_wpftmp.csproj 472 | *.tlog 473 | *.vspscc 474 | *.vssscc 475 | .builds 476 | *.pidb 477 | *.svclog 478 | *.scc 479 | 480 | # Chutzpah Test files 481 | _Chutzpah* 482 | 483 | # Visual C++ cache files 484 | ipch/ 485 | *.aps 486 | *.ncb 487 | *.opendb 488 | *.opensdf 489 | *.sdf 490 | *.cachefile 491 | *.VC.db 492 | *.VC.VC.opendb 493 | 494 | # Visual Studio profiler 495 | *.psess 496 | *.vsp 497 | *.vspx 498 | *.sap 499 | 500 | # Visual Studio Trace Files 501 | *.e2e 502 | 503 | # TFS 2012 Local Workspace 504 | $tf/ 505 | 506 | # Guidance Automation Toolkit 507 | *.gpState 508 | 509 | # ReSharper is a .NET coding add-in 510 | _ReSharper*/ 511 | *.[Rr]e[Ss]harper 512 | *.DotSettings.user 513 | 514 | # TeamCity is a build add-in 515 | _TeamCity* 516 | 517 | # DotCover is a Code Coverage Tool 518 | *.dotCover 519 | 520 | # AxoCover is a Code Coverage Tool 521 | .axoCover/* 522 | !.axoCover/settings.json 523 | 524 | # Coverlet is a free, cross platform Code Coverage Tool 525 | coverage*.json 526 | coverage*.xml 527 | coverage*.info 528 | 529 | # Visual Studio code coverage results 530 | *.coverage 531 | *.coveragexml 532 | 533 | # NCrunch 534 | _NCrunch_* 535 | .*crunch*.local.xml 536 | nCrunchTemp_* 537 | 538 | # MightyMoose 539 | *.mm.* 540 | AutoTest.Net/ 541 | 542 | # Web workbench (sass) 543 | .sass-cache/ 544 | 545 | # Installshield output folder 546 | [Ee]xpress/ 547 | 548 | # DocProject is a documentation generator add-in 549 | DocProject/buildhelp/ 550 | DocProject/Help/*.HxT 551 | DocProject/Help/*.HxC 552 | DocProject/Help/*.hhc 553 | DocProject/Help/*.hhk 554 | DocProject/Help/*.hhp 555 | DocProject/Help/Html2 556 | DocProject/Help/html 557 | 558 | # Click-Once directory 559 | publish/ 560 | 561 | # Publish Web Output 562 | *.[Pp]ublish.xml 563 | *.azurePubxml 564 | # Note: Comment the next line if you want to checkin your web deploy settings, 565 | # but database connection strings (with potential passwords) will be unencrypted 566 | *.pubxml 567 | *.publishproj 568 | 569 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 570 | # checkin your Azure Web App publish settings, but sensitive information contained 571 | # in these scripts will be unencrypted 572 | PublishScripts/ 573 | 574 | # NuGet Packages 575 | *.nupkg 576 | # NuGet Symbol Packages 577 | *.snupkg 578 | # The packages folder can be ignored because of Package Restore 579 | **/[Pp]ackages/* 580 | # except build/, which is used as an MSBuild target. 581 | !**/[Pp]ackages/build/ 582 | # Uncomment if necessary however generally it will be regenerated when needed 583 | #!**/[Pp]ackages/repositories.config 584 | # NuGet v3's project.json files produces more ignorable files 585 | *.nuget.props 586 | *.nuget.targets 587 | 588 | # Microsoft Azure Build Output 589 | csx/ 590 | *.build.csdef 591 | 592 | # Microsoft Azure Emulator 593 | ecf/ 594 | rcf/ 595 | 596 | # Windows Store app package directories and files 597 | AppPackages/ 598 | BundleArtifacts/ 599 | Package.StoreAssociation.xml 600 | _pkginfo.txt 601 | *.appx 602 | *.appxbundle 603 | *.appxupload 604 | 605 | # Visual Studio cache files 606 | # files ending in .cache can be ignored 607 | *.[Cc]ache 608 | # but keep track of directories ending in .cache 609 | !?*.[Cc]ache/ 610 | 611 | # Others 612 | ClientBin/ 613 | ~$* 614 | *.dbmdl 615 | *.dbproj.schemaview 616 | *.jfm 617 | *.pfx 618 | *.publishsettings 619 | orleans.codegen.cs 620 | 621 | # Including strong name files can present a security risk 622 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 623 | #*.snk 624 | 625 | # Since there are multiple workflows, uncomment next line to ignore bower_components 626 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 627 | #bower_components/ 628 | 629 | # RIA/Silverlight projects 630 | Generated_Code/ 631 | 632 | # Backup & report files from converting an old project file 633 | # to a newer Visual Studio version. Backup files are not needed, 634 | # because we have git ;-) 635 | _UpgradeReport_Files/ 636 | Backup*/ 637 | UpgradeLog*.XML 638 | UpgradeLog*.htm 639 | ServiceFabricBackup/ 640 | *.rptproj.bak 641 | 642 | # SQL Server files 643 | *.mdf 644 | *.ldf 645 | *.ndf 646 | 647 | # Business Intelligence projects 648 | *.rdl.data 649 | *.bim.layout 650 | *.bim_*.settings 651 | *.rptproj.rsuser 652 | *- [Bb]ackup.rdl 653 | *- [Bb]ackup ([0-9]).rdl 654 | *- [Bb]ackup ([0-9][0-9]).rdl 655 | 656 | # Microsoft Fakes 657 | FakesAssemblies/ 658 | 659 | # GhostDoc plugin setting file 660 | *.GhostDoc.xml 661 | 662 | # Node.js Tools for Visual Studio 663 | .ntvs_analysis.dat 664 | node_modules/ 665 | 666 | # Visual Studio 6 build log 667 | *.plg 668 | 669 | # Visual Studio 6 workspace options file 670 | *.opt 671 | 672 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 673 | *.vbw 674 | 675 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 676 | *.vbp 677 | 678 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 679 | *.dsw 680 | *.dsp 681 | 682 | # Visual Studio 6 technical files 683 | 684 | # Visual Studio LightSwitch build output 685 | **/*.HTMLClient/GeneratedArtifacts 686 | **/*.DesktopClient/GeneratedArtifacts 687 | **/*.DesktopClient/ModelManifest.xml 688 | **/*.Server/GeneratedArtifacts 689 | **/*.Server/ModelManifest.xml 690 | _Pvt_Extensions 691 | 692 | # Paket dependency manager 693 | .paket/paket.exe 694 | paket-files/ 695 | 696 | # FAKE - F# Make 697 | .fake/ 698 | 699 | # CodeRush personal settings 700 | .cr/personal 701 | 702 | # Python Tools for Visual Studio (PTVS) 703 | *.pyc 704 | 705 | # Cake - Uncomment if you are using it 706 | # tools/** 707 | # !tools/packages.config 708 | 709 | # Tabs Studio 710 | *.tss 711 | 712 | # Telerik's JustMock configuration file 713 | *.jmconfig 714 | 715 | # BizTalk build output 716 | *.btp.cs 717 | *.btm.cs 718 | *.odx.cs 719 | *.xsd.cs 720 | 721 | # OpenCover UI analysis results 722 | OpenCover/ 723 | 724 | # Azure Stream Analytics local run output 725 | ASALocalRun/ 726 | 727 | # MSBuild Binary and Structured Log 728 | *.binlog 729 | 730 | # NVidia Nsight GPU debugger configuration file 731 | *.nvuser 732 | 733 | # MFractors (Xamarin productivity tool) working folder 734 | .mfractor/ 735 | 736 | # Local History for Visual Studio 737 | .localhistory/ 738 | 739 | # Visual Studio History (VSHistory) files 740 | .vshistory/ 741 | 742 | # BeatPulse healthcheck temp database 743 | healthchecksdb 744 | 745 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 746 | MigrationBackup/ 747 | 748 | # Ionide (cross platform F# VS Code tools) working folder 749 | .ionide/ 750 | 751 | # Fody - auto-generated XML schema 752 | FodyWeavers.xsd 753 | 754 | # VS Code files for those working on multiple tools 755 | *.code-workspace 756 | 757 | # Local History for Visual Studio Code 758 | 759 | # Windows Installer files from build outputs 760 | 761 | # JetBrains Rider 762 | *.sln.iml 763 | 764 | ### VisualStudio Patch ### 765 | # Additional files built by Visual Studio 766 | 767 | # End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,visualstudio,pycharm+all,sublimetext,macos,windows,linux 768 | 769 | /local 770 | /.vscode 771 | cspell.json 772 | .python-version 773 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v3.1.0 (2024-12-15) 4 | 5 | ### :sparkles: New 6 | - Now only pydantic 2.10+ and python 3.9+ are supported 7 | 8 | ### :arrow_up: Dependencies 9 | - Remove safety. 10 | 11 | - Update dependencies. 12 | 13 | ## v3.0.2 (2024-12-15) 14 | 15 | ### :arrow_up: Dependencies 16 | - Pin pydantic less 2.10. 17 | 18 | ## v3.0.1 (2024-10-20) 19 | 20 | ### :bug: Bugs 21 | - fix new pydantic imports fix #406 (Issues: [`#406`](https://github.com/mom1/api-client-pydantic/issues/406)) 22 | 23 | ### :arrow_up: Dependencies 24 | - Update dependencies. 25 | 26 | - Bump ruff from 0.4.3 to 0.4.4 27 | 28 | - Bump ruff from 0.4.2 to 0.4.3 29 | 30 | - Bump coverage from 7.5.0 to 7.5.1 31 | 32 | - Bump pytest-cov from 4.1.0 to 5.0.0 33 | 34 | - Bump pytest from 7.4.4 to 8.2.0 35 | 36 | - Bump ruff from 0.4.1 to 0.4.2 37 | 38 | - Bump mypy from 1.9.0 to 1.10.0 39 | 40 | - Bump coverage from 7.4.4 to 7.5.0 41 | 42 | - Bump types-setuptools from 69.5.0.20240415 to 69.5.0.20240423 43 | 44 | - Bump pydantic from 2.7.0 to 2.7.1 45 | 46 | - Bump ruff from 0.4.0 to 0.4.1 47 | 48 | - Bump ruff from 0.3.7 to 0.4.0 49 | 50 | - Bump types-setuptools from 69.2.0.20240317 to 69.5.0.20240415 51 | 52 | - Bump ruff from 0.3.6 to 0.3.7 53 | 54 | - Bump ruff from 0.3.5 to 0.3.6 55 | 56 | - Bump pydantic from 2.6.4 to 2.7.0 57 | 58 | - Bump ruff from 0.3.4 to 0.3.5 59 | 60 | - Bump ruff from 0.3.3 to 0.3.4 61 | 62 | - Bump pytest-asyncio from 0.23.5.post1 to 0.23.6 63 | 64 | - Bump ruff from 0.3.2 to 0.3.3 65 | 66 | - Bump types-setuptools from 69.1.0.20240310 to 69.2.0.20240317 67 | 68 | - Bump coverage from 7.4.3 to 7.4.4 69 | 70 | - Bump pydantic from 2.6.3 to 2.6.4 71 | 72 | - Bump ruff from 0.3.1 to 0.3.2 73 | 74 | - Bump mypy from 1.8.0 to 1.9.0 75 | 76 | - Bump types-setuptools from 69.1.0.20240308 to 69.1.0.20240310 77 | 78 | - Bump types-toml from 0.10.8.7 to 0.10.8.20240310 79 | 80 | - Bump pytest-asyncio from 0.23.5 to 0.23.5.post1 81 | 82 | - Bump types-setuptools from 69.1.0.20240302 to 69.1.0.20240308 83 | 84 | - Bump ruff from 0.3.0 to 0.3.1 85 | 86 | - Bump types-setuptools from 69.1.0.20240301 to 69.1.0.20240302 87 | 88 | - Bump ruff from 0.2.2 to 0.3.0 89 | 90 | - Bump types-setuptools from 69.1.0.20240229 to 69.1.0.20240301 91 | 92 | - Bump types-setuptools from 69.1.0.20240223 to 69.1.0.20240229 93 | 94 | - Bump pydantic from 2.6.2 to 2.6.3 95 | 96 | - Bump coverage from 7.4.2 to 7.4.3 97 | 98 | - Bump pydantic from 2.6.1 to 2.6.2 99 | 100 | - Bump types-setuptools from 69.1.0.20240217 to 69.1.0.20240223 101 | 102 | - Bump coverage from 7.4.1 to 7.4.2 103 | 104 | - Bump types-setuptools from 69.1.0.20240215 to 69.1.0.20240217 105 | 106 | - Bump ruff from 0.2.1 to 0.2.2 107 | 108 | - Bump types-setuptools from 69.0.0.20240125 to 69.1.0.20240215 109 | 110 | - Bump pytest-asyncio from 0.23.4 to 0.23.5 111 | 112 | - Bump pydantic from 2.6.0 to 2.6.1 113 | 114 | - Bump ruff from 0.2.0 to 0.2.1 115 | 116 | - Bump ruff from 0.1.15 to 0.2.0 117 | 118 | - Bump ruff from 0.1.14 to 0.1.15 119 | 120 | - Bump pydantic from 2.5.3 to 2.6.0 121 | 122 | - Bump pytest-asyncio from 0.23.3 to 0.23.4 123 | 124 | - Bump coverage from 7.4.0 to 7.4.1 125 | 126 | - Bump types-setuptools from 69.0.0.20240115 to 69.0.0.20240125 127 | 128 | - Bump ruff from 0.1.13 to 0.1.14 129 | 130 | - Bump types-setuptools from 69.0.0.20240106 to 69.0.0.20240115 131 | 132 | - Bump ruff from 0.1.12 to 0.1.13 133 | 134 | - Bump ruff from 0.1.11 to 0.1.12 135 | 136 | - Bump ruff from 0.1.9 to 0.1.11 137 | 138 | - Bump types-setuptools from 69.0.0.0 to 69.0.0.20240106 139 | 140 | - Bump types-setuptools from 67.8.0.0 to 69.0.0.0 141 | 142 | - Bump pytest-asyncio from 0.21.1 to 0.23.3 143 | 144 | ## v3.0.0 (2024-01-05) 145 | 146 | ### :boom: Breaking Changes 147 | - Pydantic V2 and new signature. 148 | 149 | ### :recycle: Changes 150 | - Old logic moved to v1 namespace. 151 | 152 | ### :arrow_up: Dependencies 153 | - Upgrade dependencies. 154 | 155 | - Bump pytest from 7.3.2 to 7.4.0 156 | 157 | - Bump mypy from 1.3.0 to 1.4.0 158 | 159 | - Bump flake8-comprehensions from 3.12.0 to 3.13.0 160 | 161 | - Bump flake8-tidy-imports from 4.8.0 to 4.9.0 162 | 163 | - Bump pytest from 7.3.1 to 7.3.2 164 | 165 | - Bump pydantic from 1.10.8 to 1.10.9 166 | 167 | - Bump coverage from 7.2.6 to 7.2.7 168 | 169 | - Bump pytest-cov from 4.0.0 to 4.1.0 170 | 171 | - Bump pydantic from 1.10.7 to 1.10.8 172 | 173 | - Bump coverage from 7.2.5 to 7.2.6 174 | 175 | - Bump types-setuptools from 67.7.0.2 to 67.8.0.0 176 | 177 | - Bump types-setuptools from 67.7.0.1 to 67.7.0.2 178 | 179 | - Bump mypy from 1.2.0 to 1.3.0 180 | 181 | - Bump types-setuptools from 67.7.0.0 to 67.7.0.1 182 | 183 | - Bump coverage from 7.2.3 to 7.2.5 184 | 185 | - Bump types-setuptools from 67.6.0.8 to 67.7.0.0 186 | 187 | - Bump mypy from 0.991 to 1.2.0 188 | 189 | - Bump pre-commit from 2.20.0 to 2.21.0 190 | 191 | - Bump flake8-comprehensions from 3.10.1 to 3.12.0 192 | 193 | - Bump types-setuptools from 67.6.0.7 to 67.6.0.8 194 | 195 | - Bump pydantic from 1.10.2 to 1.10.7 196 | 197 | - Bump types-toml from 0.10.8.1 to 0.10.8.6 198 | 199 | - Bump pytest from 7.2.0 to 7.3.1 200 | 201 | ## v2.2.1 (2023-04-14) 202 | 203 | ### :arrow_up: Dependencies 204 | - Bump actions 205 | 206 | - Bump safety from 2.3.1 to 2.3.5 207 | 208 | - Bump certifi from 2021.10.8 to 2022.12.7 209 | 210 | - Bump setuptools from 65.3.0 to 67.6.1 211 | 212 | - Bump ipython from 7.32.0 to 7.34.0 213 | 214 | - Bump ipdb from 0.13.9 to 0.13.13 215 | 216 | - Bump types-setuptools from 65.5.0.3 to 67.6.0.7 217 | 218 | - Bump pytest-asyncio from 0.20.2 to 0.21.0 219 | 220 | - Bump isort from 5.10.1 to 5.11.5 221 | 222 | - Bump flake8-quotes from 3.3.1 to 3.3.2 223 | 224 | - Bump flake8-builtins from 2.0.1 to 2.1.0 225 | 226 | - Bump flake8-bugbear from 22.10.27 to 23.3.12 227 | 228 | - Bump coverage from 6.5.0 to 7.2.3 229 | 230 | - Bump black from 22.10.0 to 23.3.0 231 | 232 | - Remove dependency ipdb. 233 | 234 | - Bump pytest-asyncio from 0.20.1 to 0.20.2 235 | 236 | - Bump types-setuptools from 65.5.0.2 to 65.5.0.3 237 | 238 | - Bump mypy from 0.990 to 0.991 239 | 240 | - Bump types-toml from 0.10.8 to 0.10.8.1 241 | 242 | - Bump pytest-cov from 3.0.0 to 4.0.0 243 | 244 | - Bump mypy from 0.982 to 0.990 245 | 246 | - Bump flake8-builtins from 2.0.0 to 2.0.1 247 | 248 | - Bump flake8-comprehensions from 3.10.0 to 3.10.1 249 | 250 | - Bump flake8-bugbear from 22.10.25 to 22.10.27 251 | 252 | - Bump flake8-bugbear from 22.9.23 to 22.10.25 253 | 254 | - Bump pytest from 7.1.3 to 7.2.0 255 | 256 | - Bump types-setuptools from 65.5.0.1 to 65.5.0.2 257 | 258 | - Bump pytest-asyncio from 0.19.0 to 0.20.1 259 | 260 | - Bump flake8-builtins from 1.5.3 to 2.0.0 261 | 262 | - Bump types-setuptools from 65.4.0.0 to 65.5.0.1 263 | 264 | - Bump black from 22.8.0 to 22.10.0 265 | 266 | - Bump safety from 2.2.1 to 2.3.1 267 | 268 | - Bump safety from 2.2.0 to 2.2.1 269 | 270 | - Bump mypy from 0.981 to 0.982 271 | 272 | - Bump flake8-broken-line from 0.5.0 to 0.6.0 273 | 274 | - Bump types-setuptools from 65.3.0 to 65.4.0.0 275 | 276 | - Bump coverage from 6.4.4 to 6.5.0 277 | 278 | - Bump mypy from 0.971 to 0.981 279 | 280 | - Bump flake8-bugbear from 22.9.11 to 22.9.23 281 | 282 | - Bump flake8-eradicate from 1.3.0 to 1.4.0 283 | 284 | - Bump safety from 2.1.1 to 2.2.0 285 | 286 | - Bump flake8-bugbear from 22.8.23 to 22.9.11 287 | 288 | - Bump pydantic from 1.10.1 to 1.10.2 289 | 290 | - Bump black from 22.6.0 to 22.8.0 291 | 292 | - Bump pytest from 7.1.2 to 7.1.3 293 | 294 | - Bump pydantic from 1.10.0 to 1.10.1 295 | 296 | - Bump types-setuptools from 64.0.1 to 65.3.0 297 | 298 | - Bump pydantic from 1.9.2 to 1.10.0 299 | 300 | - Bump flake8-bugbear from 22.8.22 to 22.8.23 301 | 302 | - Bump flake8-bugbear from 22.7.1 to 22.8.22 303 | 304 | - Bump pep8-naming from 0.13.1 to 0.13.2 305 | 306 | - Bump coverage from 6.4.3 to 6.4.4 307 | 308 | - Bump flake8-broken-line from 0.4.0 to 0.5.0 309 | 310 | - Bump types-setuptools from 63.4.0 to 64.0.1 311 | 312 | - Bump pydantic from 1.9.1 to 1.9.2 313 | 314 | - Bump flake8-eradicate from 1.2.1 to 1.3.0 315 | 316 | - Bump coverage from 6.4.2 to 6.4.3 317 | 318 | - Bump types-setuptools from 63.2.3 to 63.4.0 319 | 320 | - Bump types-setuptools from 63.2.2 to 63.2.3 321 | 322 | - Bump types-setuptools from 63.2.1 to 63.2.2 323 | 324 | - Bump types-setuptools from 57.4.18 to 63.2.1 325 | 326 | - Bump safety from 1.10.3 to 2.1.1 327 | 328 | - Bump mypy from 0.961 to 0.971 329 | 330 | - Bump pep8-naming from 0.13.0 to 0.13.1 331 | 332 | - Bump coverage from 6.4.1 to 6.4.2 333 | 334 | - Bump pytest-asyncio from 0.18.3 to 0.19.0 335 | 336 | - Bump types-toml from 0.10.7 to 0.10.8 337 | 338 | - Bump pre-commit from 2.19.0 to 2.20.0 339 | 340 | - Bump flake8-bugbear from 22.6.22 to 22.7.1 341 | 342 | - Bump black from 22.3.0 to 22.6.0 343 | 344 | - Bump types-setuptools from 57.4.17 to 57.4.18 345 | 346 | - Bump flake8-bugbear from 22.4.25 to 22.6.22 347 | 348 | - Bump mypy from 0.960 to 0.961 349 | 350 | - Bump mypy from 0.950 to 0.960 351 | 352 | - Bump coverage from 6.4 to 6.4.1 353 | 354 | - Bump pep8-naming from 0.12.1 to 0.13.0 355 | 356 | - Bump types-setuptools from 57.4.16 to 57.4.17 357 | 358 | - Bump types-setuptools from 57.4.15 to 57.4.16 359 | 360 | - Bump coverage from 6.3.3 to 6.4 361 | 362 | - Bump types-setuptools from 57.4.14 to 57.4.15 363 | 364 | - Bump pydantic from 1.9.0 to 1.9.1 365 | 366 | - Bump flake8-comprehensions from 3.9.0 to 3.10.0 367 | 368 | ## v2.2.0 (2022-05-16) 369 | 370 | ### :sparkles: New 371 | - Support for async. 372 | 373 | ### :arrow_up: Dependencies 374 | - Add dep pytest-asyncio for async testing. 375 | 376 | - Bump coverage from 6.3.2 to 6.3.3 377 | 378 | - Bump flake8-comprehensions from 3.8.0 to 3.9.0 379 | 380 | - Bump flake8-tidy-imports from 4.7.0 to 4.8.0 381 | 382 | - Bump types-toml from 0.10.6 to 0.10.7 383 | 384 | - Bump pre-commit from 2.18.1 to 2.19.0 385 | 386 | - Bump flake8-tidy-imports from 4.6.0 to 4.7.0 387 | 388 | - Bump types-toml from 0.10.5 to 0.10.6 389 | 390 | - Bump mypy from 0.942 to 0.950 391 | 392 | - Bump flake8-eradicate from 1.2.0 to 1.2.1 393 | 394 | - Bump flake8-bugbear from 22.3.23 to 22.4.25 395 | 396 | - Bump pytest from 7.1.1 to 7.1.2 397 | 398 | ### :memo: Documentation 399 | - Added "Mentions" in README.md 400 | 401 | ## v2.1.0 (2022-04-24) 402 | 403 | ### :sparkles: New 404 | - Now the parameters passed through "args" are also serialized. 405 | 406 | ```python 407 | def func(i: int): 408 | return i 409 | 410 | assert func('1') == 1 411 | ``` 412 | 413 | 414 | ### :recycle: Changes 415 | - Small changes for release config 416 | 417 | - no need `cfg` settings in editorconfig. 418 | 419 | ### :arrow_up: Dependencies 420 | - Bump types-toml from 0.10.4 to 0.10.5 421 | 422 | - Upgrade dependency black -> black[d] 423 | 424 | - Bump types-setuptools from 57.4.12 to 57.4.14 425 | 426 | - Bump types-setuptools from 57.4.11 to 57.4.12 427 | 428 | - Bump pre-commit from 2.17.0 to 2.18.1 429 | 430 | - Bump black from 22.1.0 to 22.3.0 431 | 432 | - Bump mypy from 0.941 to 0.942 433 | 434 | - Bump flake8-bugbear from 22.3.20 to 22.3.23 435 | 436 | - Bump flake8-bugbear from 22.1.11 to 22.3.20 437 | 438 | - Bump pytest from 7.1.0 to 7.1.1 439 | 440 | - Bump types-setuptools from 57.4.10 to 57.4.11 441 | 442 | - Bump mypy from 0.940 to 0.941 443 | 444 | - Bump mypy from 0.931 to 0.940 445 | 446 | - Bump pytest from 7.0.1 to 7.1.0 447 | 448 | ## v2.0.1 (2022-03-09) 449 | 450 | ### :bug: Bugs 451 | - Fixed a bug with working with the config of a model that does not have extra. 452 | 453 | ### :arrow_up: Dependencies 454 | - Bump types-setuptools from 57.4.9 to 57.4.10 455 | 456 | ## v2.0.0 (2022-03-08) 457 | 458 | ### :boom: Breaking Changes 459 | - Global refactoring. (Issues: [`#72`](https://github.com/mom1/api-client-pydantic/issues/72)) 460 | 461 | - support for more use cases (fix #72). 462 | - `serialize_response` and `serialize_request` and `serialize` call signature changed. 463 | - `serialize_response` and `serialize_request` names are left for compatibility, 464 | it is better to use `params_serializer` and `response_serializer` instead. 465 | - Removed unnecessary dependencies. 466 | - Tests completely rewritten. 467 | - Decorating will only be done if necessary, which will positively affect performance. 468 | 469 | 470 | ### :arrow_up: Dependencies 471 | - Bump pycln from 1.2.0 to 1.2.4 472 | 473 | # [v1.2.2](https://github.com/mom1/api-client-pydantic/compare/1.2.1...1.2.2) (2022-02-24) 474 | 475 | ## :arrow_up: Dependencies 476 | - Bump pytest from 6.2.5 to 7.0.1 477 | Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.5 to 7.0.1. 478 | - [Release notes](https://github.com/pytest-dev/pytest/releases) 479 | - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) 480 | - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.5...7.0.1) 481 | 482 | --- 483 | updated-dependencies: 484 | - dependency-name: pytest 485 | dependency-type: direct:development 486 | update-type: version-update:semver-major 487 | ... 488 | 489 | Signed-off-by: dependabot[bot] <support@github.com> 490 | - Bump pre-commit from 2.15.0 to 2.17.0 491 | Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.15.0 to 2.17.0. 492 | - [Release notes](https://github.com/pre-commit/pre-commit/releases) 493 | - [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md) 494 | - [Commits](https://github.com/pre-commit/pre-commit/compare/v2.15.0...v2.17.0) 495 | 496 | --- 497 | updated-dependencies: 498 | - dependency-name: pre-commit 499 | dependency-type: direct:development 500 | update-type: version-update:semver-minor 501 | ... 502 | 503 | Signed-off-by: dependabot[bot] <support@github.com> 504 | - Bump flake8-comprehensions from 3.7.0 to 3.8.0 505 | Bumps [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) from 3.7.0 to 3.8.0. 506 | - [Release notes](https://github.com/adamchainz/flake8-comprehensions/releases) 507 | - [Changelog](https://github.com/adamchainz/flake8-comprehensions/blob/main/HISTORY.rst) 508 | - [Commits](https://github.com/adamchainz/flake8-comprehensions/compare/3.7.0...3.8.0) 509 | 510 | --- 511 | updated-dependencies: 512 | - dependency-name: flake8-comprehensions 513 | dependency-type: direct:development 514 | update-type: version-update:semver-minor 515 | ... 516 | 517 | Signed-off-by: dependabot[bot] <support@github.com> 518 | - Bump flake8-bugbear from 21.9.2 to 22.1.11 519 | Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 21.9.2 to 22.1.11. 520 | - [Release notes](https://github.com/PyCQA/flake8-bugbear/releases) 521 | - [Commits](https://github.com/PyCQA/flake8-bugbear/compare/21.9.2...22.1.11) 522 | 523 | --- 524 | updated-dependencies: 525 | - dependency-name: flake8-bugbear 526 | dependency-type: direct:development 527 | update-type: version-update:semver-major 528 | ... 529 | 530 | Signed-off-by: dependabot[bot] <support@github.com> 531 | - Bump isort from 5.9.3 to 5.10.1 532 | Bumps [isort](https://github.com/pycqa/isort) from 5.9.3 to 5.10.1. 533 | - [Release notes](https://github.com/pycqa/isort/releases) 534 | - [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) 535 | - [Commits](https://github.com/pycqa/isort/compare/5.9.3...5.10.1) 536 | 537 | --- 538 | updated-dependencies: 539 | - dependency-name: isort 540 | dependency-type: direct:development 541 | update-type: version-update:semver-minor 542 | ... 543 | 544 | Signed-off-by: dependabot[bot] <support@github.com> 545 | ## :memo: Documentation 546 | - Update README.md 547 | 548 | # [v1.2.1](https://github.com/mom1/api-client-pydantic/compare/v1.2.0...1.2.1) (2022-02-24) 549 | 550 | ## :recycle: Changes 551 | - fix release template 552 | - Improve `.gitignore` 553 | - Improved all dev things 554 | - Changed formater to black 555 | - Reformat code 556 | - Added checks 557 | - pre-commit autoupdate 558 | 559 | - commitlint is off, need research 560 | - Upgrading a dependency doesn't increase the version 561 | - Small changes due to coverage analysis 562 | - remove walrus operator for python 3.7 compatibility (#73) (Issues: [`#73`](https://github.com/mom1/api-client-pydantic/issues/73)) 563 | - Changelog generation job because now pre-commit generate changelog 564 | ## :memo: Documentation 565 | - Update CHANGELOG.md 566 | 567 | #### api-client-pydantic `v1.2.0` (2021-10-24) 568 | 569 | ##### ✨ New 570 | 571 | - Set a new signature for function. 572 | 573 | ##### ♻️ Changes 574 | 575 | - New changelog generation. 576 | - New configs and pre-commit. 577 | 578 | ##### ⬆️ Dependencies 579 | 580 | - ⬆️ Bumps [emoji](https://github.com/carpedm20/emoji) from 1.5.0 to 1.6.1. 581 | - [Release notes](https://github.com/carpedm20/emoji/releases) 582 | - [Changelog](https://github.com/carpedm20/emoji/blob/master/CHANGES.md) 583 | - [Commits](carpedm20/emoji@v1.5.0...v1.6.1) 584 | - ⬆️ Bump all for flake8 4. 585 | 586 | ##### 📝 Docs 587 | 588 | - Update README.md. 589 | 590 | 591 | #### api-client-pydantic `v1.1.1` (2021-10-13) 592 | 593 | ##### 🐛 Bugs 594 | 595 | - Fix case with typing. 596 | `def function(q: Optional[str]):` 597 | 598 | ##### ⬆️ Dependencies 599 | 600 | - ⬆️ Bump pytest-cov from 2.12.1 to 3.0.0 (#54) 601 | Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.12.1 to 3.0.0. 602 | - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) 603 | - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) 604 | - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.12.1...v3.0.0) 605 | - ⬆️ Bump flake8-bugbear from 21.4.3 to 21.9.2 (#53) 606 | Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 21.4.3 to 21.9.2. 607 | - [Release notes](https://github.com/PyCQA/flake8-bugbear/releases) 608 | - [Commits](https://github.com/PyCQA/flake8-bugbear/compare/21.4.3...21.9.2) 609 | - ⬆️ Bump emoji from 1.4.2 to 1.5.0 (#52) 610 | Bumps [emoji](https://github.com/carpedm20/emoji) from 1.4.2 to 1.5.0. 611 | - [Release notes](https://github.com/carpedm20/emoji/releases) 612 | - [Changelog](https://github.com/carpedm20/emoji/blob/master/CHANGES.md) 613 | - [Commits](https://github.com/carpedm20/emoji/compare/v.1.4.2...v1.5.0) 614 | - ⬆️ Bump pytest from 6.2.4 to 6.2.5 (#50) 615 | Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.4 to 6.2.5. 616 | - [Release notes](https://github.com/pytest-dev/pytest/releases) 617 | - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) 618 | - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.4...6.2.5) 619 | - ⬆️ Bump flake8-quotes from 3.2.0 to 3.3.0 (#49) 620 | Bumps [flake8-quotes](https://github.com/zheller/flake8-quotes) from 3.2.0 to 3.3.0. 621 | - [Release notes](https://github.com/zheller/flake8-quotes/releases) 622 | - [Commits](https://github.com/zheller/flake8-quotes/compare/3.2.0...3.3.0) 623 | - ⬆️ Bump flake8-comprehensions from 3.5.0 to 3.6.1 (#48) 624 | Bumps [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) from 3.5.0 to 3.6.1. 625 | - [Release notes](https://github.com/adamchainz/flake8-comprehensions/releases) 626 | - [Changelog](https://github.com/adamchainz/flake8-comprehensions/blob/main/HISTORY.rst) 627 | - [Commits](https://github.com/adamchainz/flake8-comprehensions/compare/3.5.0...3.6.1) 628 | 629 | ##### 🌱 Other 630 | 631 | - Release 1.1.1. 632 | 633 | 634 | #### api-client-pydantic `v1.1.0` (2021-08-09) 635 | 636 | ##### ♻️ Changes 637 | 638 | - Correct recognition of functions (#46) 639 | 640 | ##### ⬆️ Dependencies 641 | 642 | - ⬆️ Bump pep8-naming from 0.12.0 to 0.12.1 (#45) 643 | Bumps [pep8-naming](https://github.com/PyCQA/pep8-naming) from 0.12.0 to 0.12.1. 644 | - [Release notes](https://github.com/PyCQA/pep8-naming/releases) 645 | - [Changelog](https://github.com/PyCQA/pep8-naming/blob/master/CHANGELOG.rst) 646 | - [Commits](https://github.com/PyCQA/pep8-naming/compare/0.12.0...0.12.1) 647 | 648 | --- 649 | updated-dependencies: 650 | - dependency-name: pep8-naming 651 | dependency-type: direct:development 652 | update-type: version-update:semver-patch 653 | ... 654 | - ⬆️ Bump emoji from 1.2.0 to 1.4.2 (#44) 655 | Bumps [emoji](https://github.com/carpedm20/emoji) from 1.2.0 to 1.4.2. 656 | - [Release notes](https://github.com/carpedm20/emoji/releases) 657 | - [Changelog](https://github.com/carpedm20/emoji/blob/master/CHANGES.md) 658 | - [Commits](https://github.com/carpedm20/emoji/compare/v.1.2.0...v.1.4.2) 659 | 660 | --- 661 | updated-dependencies: 662 | - dependency-name: emoji 663 | dependency-type: direct:development 664 | update-type: version-update:semver-minor 665 | ... 666 | - ⬆️ Bump isort from 5.9.1 to 5.9.3 (#43) 667 | Bumps [isort](https://github.com/pycqa/isort) from 5.9.1 to 5.9.3. 668 | - [Release notes](https://github.com/pycqa/isort/releases) 669 | - [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) 670 | - [Commits](https://github.com/pycqa/isort/compare/5.9.1...5.9.3) 671 | 672 | --- 673 | updated-dependencies: 674 | - dependency-name: isort 675 | dependency-type: direct:development 676 | update-type: version-update:semver-patch 677 | ... 678 | - ⬆️ Bump pep8-naming from 0.11.1 to 0.12.0 (#40) 679 | Bumps [pep8-naming](https://github.com/PyCQA/pep8-naming) from 0.11.1 to 0.12.0. 680 | - [Release notes](https://github.com/PyCQA/pep8-naming/releases) 681 | - [Changelog](https://github.com/PyCQA/pep8-naming/blob/master/CHANGELOG.rst) 682 | - [Commits](https://github.com/PyCQA/pep8-naming/compare/0.11.1...0.12.0) 683 | 684 | --- 685 | updated-dependencies: 686 | - dependency-name: pep8-naming 687 | dependency-type: direct:development 688 | update-type: version-update:semver-minor 689 | ... 690 | - ⬆️ Bump flake8-eradicate from 1.0.0 to 1.1.0 (#39) 691 | Bumps [flake8-eradicate](https://github.com/wemake-services/flake8-eradicate) from 1.0.0 to 1.1.0. 692 | - [Release notes](https://github.com/wemake-services/flake8-eradicate/releases) 693 | - [Changelog](https://github.com/wemake-services/flake8-eradicate/blob/master/CHANGELOG.md) 694 | - [Commits](https://github.com/wemake-services/flake8-eradicate/compare/1.0.0...1.1.0) 695 | - ⬆️ Bump isort from 5.8.0 to 5.9.1 (#38) 696 | Bumps [isort](https://github.com/pycqa/isort) from 5.8.0 to 5.9.1. 697 | - [Release notes](https://github.com/pycqa/isort/releases) 698 | - [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) 699 | - [Commits](https://github.com/pycqa/isort/compare/5.8.0...5.9.1) 700 | 701 | --- 702 | updated-dependencies: 703 | - dependency-name: isort 704 | dependency-type: direct:development 705 | update-type: version-update:semver-minor 706 | ... 707 | - ⬆️ Bump ipdb from 0.13.8 to 0.13.9 (#37) 708 | Bumps [ipdb](https://github.com/gotcha/ipdb) from 0.13.8 to 0.13.9. 709 | - [Release notes](https://github.com/gotcha/ipdb/releases) 710 | - [Changelog](https://github.com/gotcha/ipdb/blob/master/HISTORY.txt) 711 | - [Commits](https://github.com/gotcha/ipdb/compare/0.13.8...0.13.9) 712 | 713 | --- 714 | updated-dependencies: 715 | - dependency-name: ipdb 716 | dependency-type: direct:development 717 | update-type: version-update:semver-patch 718 | ... 719 | - ⬆️ Bump pytest-cov from 2.12.0 to 2.12.1 (#36) 720 | Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.12.0 to 2.12.1. 721 | - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) 722 | - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) 723 | - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.12.0...v2.12.1) 724 | 725 | --- 726 | updated-dependencies: 727 | - dependency-name: pytest-cov 728 | dependency-type: direct:development 729 | update-type: version-update:semver-patch 730 | ... 731 | - ⬆️ Bump requests-mock from 1.9.2 to 1.9.3 (#35) 732 | Bumps [requests-mock](https://github.com/jamielennox/requests-mock) from 1.9.2 to 1.9.3. 733 | - [Release notes](https://github.com/jamielennox/requests-mock/releases) 734 | - [Commits](https://github.com/jamielennox/requests-mock/compare/1.9.2...1.9.3) 735 | - ⬆️ Bump ipdb from 0.13.7 to 0.13.8 (#34) 736 | Bumps [ipdb](https://github.com/gotcha/ipdb) from 0.13.7 to 0.13.8. 737 | - [Release notes](https://github.com/gotcha/ipdb/releases) 738 | - [Changelog](https://github.com/gotcha/ipdb/blob/master/HISTORY.txt) 739 | - [Commits](https://github.com/gotcha/ipdb/compare/0.13.7...0.13.8) 740 | - ⬆️ Bump pytest-cov from 2.11.1 to 2.12.0 (#33) 741 | Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.11.1 to 2.12.0. 742 | - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) 743 | - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) 744 | - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.11.1...v2.12.0) 745 | - ⬆️ Bump pydantic from 1.8.1 to 1.8.2. 746 | Bumps [pydantic](https://github.com/samuelcolvin/pydantic) from 1.8.1 to 1.8.2. 747 | - [Release notes](https://github.com/samuelcolvin/pydantic/releases) 748 | - [Changelog](https://github.com/samuelcolvin/pydantic/blob/master/HISTORY.md) 749 | - [Commits](https://github.com/samuelcolvin/pydantic/compare/v1.8.1...v1.8.2) 750 | - ⬆️ Bump flake8-comprehensions from 3.4.0 to 3.5.0 (#31) 751 | Bumps [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) from 3.4.0 to 3.5.0. 752 | - [Release notes](https://github.com/adamchainz/flake8-comprehensions/releases) 753 | - [Changelog](https://github.com/adamchainz/flake8-comprehensions/blob/main/HISTORY.rst) 754 | - [Commits](https://github.com/adamchainz/flake8-comprehensions/compare/3.4.0...3.5.0) 755 | - ⬆️ Bump flake8 from 3.9.1 to 3.9.2 (#30) 756 | Bumps [flake8](https://gitlab.com/pycqa/flake8) from 3.9.1 to 3.9.2. 757 | - [Release notes](https://gitlab.com/pycqa/flake8/tags) 758 | - [Commits](https://gitlab.com/pycqa/flake8/compare/3.9.1...3.9.2) 759 | - ⬆️ Bump pytest from 6.2.3 to 6.2.4 (#29) 760 | Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.3 to 6.2.4. 761 | - [Release notes](https://github.com/pytest-dev/pytest/releases) 762 | - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) 763 | - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.3...6.2.4) 764 | - ⬆️ Bump requests-mock from 1.9.1 to 1.9.2 (#28) 765 | Bumps [requests-mock](https://github.com/jamielennox/requests-mock) from 1.9.1 to 1.9.2. 766 | - [Release notes](https://github.com/jamielennox/requests-mock/releases) 767 | - [Commits](https://github.com/jamielennox/requests-mock/compare/1.9.1...1.9.2) 768 | - ⬆️ Bump requests-mock from 1.8.0 to 1.9.1 (#27) 769 | Bumps [requests-mock](https://github.com/jamielennox/requests-mock) from 1.8.0 to 1.9.1. 770 | - [Release notes](https://github.com/jamielennox/requests-mock/releases) 771 | - [Commits](https://github.com/jamielennox/requests-mock/compare/1.8.0...1.9.1) 772 | - ⬆️ Bump requests-mock from 1.8.0 to 1.9.1 (#25) 773 | Bumps [requests-mock](https://github.com/jamielennox/requests-mock) from 1.8.0 to 1.9.1. 774 | - [Release notes](https://github.com/jamielennox/requests-mock/releases) 775 | - [Commits](https://github.com/jamielennox/requests-mock/compare/1.8.0...1.9.1) 776 | - ⬆️ Bump flake8 from 3.9.0 to 3.9.1 (#24) 777 | Bumps [flake8](https://gitlab.com/pycqa/flake8) from 3.9.0 to 3.9.1. 778 | - [Release notes](https://gitlab.com/pycqa/flake8/tags) 779 | - [Commits](https://gitlab.com/pycqa/flake8/compare/3.9.0...3.9.1) 780 | - ⬆️ Bump pytest from 6.2.2 to 6.2.3 (#22) 781 | Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.2 to 6.2.3. 782 | - [Release notes](https://github.com/pytest-dev/pytest/releases) 783 | - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) 784 | - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.2...6.2.3) 785 | - ⬆️ Bump flake8-bugbear from 21.3.2 to 21.4.3 (#21) 786 | Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 21.3.2 to 21.4.3. 787 | - [Release notes](https://github.com/PyCQA/flake8-bugbear/releases) 788 | - [Commits](https://github.com/PyCQA/flake8-bugbear/compare/21.3.2...21.4.3) 789 | - ⬆️:lock: Bump urllib3 from 1.26.3 to 1.26.4 (#23) 790 | Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.3 to 1.26.4. **This update includes a security fix.** 791 | - [Release notes](https://github.com/urllib3/urllib3/releases) 792 | - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) 793 | - [Commits](https://github.com/urllib3/urllib3/compare/1.26.3...1.26.4) 794 | - ⬆️ Bump api-client from 1.3.0 to 1.3.1 (#20) 795 | Bumps [api-client](https://github.com/MikeWooster/api-client) from 1.3.0 to 1.3.1. 796 | - [Release notes](https://github.com/MikeWooster/api-client/releases) 797 | - [Commits](https://github.com/MikeWooster/api-client/compare/v1.3.0...v1.3.1) 798 | - ⬆️ Bump isort from 5.7.0 to 5.8.0 (#19) 799 | Bumps [isort](https://github.com/pycqa/isort) from 5.7.0 to 5.8.0. 800 | - [Release notes](https://github.com/pycqa/isort/releases) 801 | - [Changelog](https://github.com/PyCQA/isort/blob/develop/CHANGELOG.md) 802 | - [Commits](https://github.com/pycqa/isort/compare/5.7.0...5.8.0) 803 | - ⬆️:lock: Bump urllib3 from 1.26.2 to 1.26.3 (#18) 804 | Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.2 to 1.26.3. **This update includes a security fix.** 805 | - [Release notes](https://github.com/urllib3/urllib3/releases) 806 | - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) 807 | - [Commits](https://github.com/urllib3/urllib3/compare/1.26.2...1.26.3) 808 | - ⬆️ Bump flake8-comprehensions from 3.3.1 to 3.4.0 (#17) 809 | Bumps [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) from 3.3.1 to 3.4.0. 810 | - [Release notes](https://github.com/adamchainz/flake8-comprehensions/releases) 811 | - [Changelog](https://github.com/adamchainz/flake8-comprehensions/blob/main/HISTORY.rst) 812 | - [Commits](https://github.com/adamchainz/flake8-comprehensions/compare/3.3.1...3.4.0) 813 | - ⬆️ Bump yapf from 0.30.0 to 0.31.0 (#15) 814 | Bumps [yapf](https://github.com/google/yapf) from 0.30.0 to 0.31.0. 815 | - [Release notes](https://github.com/google/yapf/releases) 816 | - [Changelog](https://github.com/google/yapf/blob/main/CHANGELOG) 817 | - [Commits](https://github.com/google/yapf/compare/v0.30.0...v0.31.0) 818 | - ⬆️ Bump ipdb from 0.13.6 to 0.13.7 (#16) 819 | Bumps [ipdb](https://github.com/gotcha/ipdb) from 0.13.6 to 0.13.7. 820 | - [Release notes](https://github.com/gotcha/ipdb/releases) 821 | - [Changelog](https://github.com/gotcha/ipdb/blob/master/HISTORY.txt) 822 | - [Commits](https://github.com/gotcha/ipdb/compare/0.13.6...0.13.7) 823 | - ⬆️ Bump flake8 from 3.8.4 to 3.9.0 (#14) 824 | Bumps [flake8](https://gitlab.com/pycqa/flake8) from 3.8.4 to 3.9.0. 825 | - [Release notes](https://gitlab.com/pycqa/flake8/tags) 826 | - [Commits](https://gitlab.com/pycqa/flake8/compare/3.8.4...3.9.0) 827 | - ⬆️ Bump flake8-bugbear from 21.3.1 to 21.3.2 (#12) 828 | Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 21.3.1 to 21.3.2. 829 | - [Release notes](https://github.com/PyCQA/flake8-bugbear/releases) 830 | - [Commits](https://github.com/PyCQA/flake8-bugbear/commits) 831 | - ⬆️ Bump ipdb from 0.13.4 to 0.13.6. 832 | Bumps [ipdb](https://github.com/gotcha/ipdb) from 0.13.4 to 0.13.6. 833 | - [Release notes](https://github.com/gotcha/ipdb/releases) 834 | - [Changelog](https://github.com/gotcha/ipdb/blob/master/HISTORY.txt) 835 | - [Commits](https://github.com/gotcha/ipdb/compare/0.13.4...0.13.6) 836 | - ⬆️ Bump pydantic from 1.7.3 to 1.8.1. 837 | Bumps [pydantic](https://github.com/samuelcolvin/pydantic) from 1.7.3 to 1.8.1. 838 | - [Release notes](https://github.com/samuelcolvin/pydantic/releases) 839 | - [Changelog](https://github.com/samuelcolvin/pydantic/blob/master/HISTORY.md) 840 | - [Commits](https://github.com/samuelcolvin/pydantic/compare/v1.7.3...v1.8.1) 841 | - ⬆️ Bump flake8-bugbear from 20.11.1 to 21.3.1. 842 | Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 20.11.1 to 21.3.1. 843 | - [Release notes](https://github.com/PyCQA/flake8-bugbear/releases) 844 | - [Commits](https://github.com/PyCQA/flake8-bugbear/commits) 845 | - ⬆️ Bump api-client from 1.2.2 to 1.3.0. 846 | Bumps [api-client](https://github.com/MikeWooster/api-client) from 1.2.2 to 1.3.0. 847 | - [Release notes](https://github.com/MikeWooster/api-client/releases) 848 | - [Commits](https://github.com/MikeWooster/api-client/compare/v1.2.2...v1.3.0) 849 | - ⬆️ Bump emoji from 1.1.0 to 1.2.0. 850 | Bumps [emoji](https://github.com/carpedm20/emoji) from 1.1.0 to 1.2.0. 851 | - [Release notes](https://github.com/carpedm20/emoji/releases) 852 | - [Changelog](https://github.com/carpedm20/emoji/blob/master/CHANGES.md) 853 | - [Commits](https://github.com/carpedm20/emoji/compare/v.1.1.0...v.1.2.0) 854 | - ⬆️ Bump pytest from 6.2.1 to 6.2.2. 855 | Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.1 to 6.2.2. 856 | - [Release notes](https://github.com/pytest-dev/pytest/releases) 857 | - [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst) 858 | - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.1...6.2.2) 859 | - ⬆️ Bump emoji from 0.6.0 to 1.1.0. 860 | Bumps [emoji](https://github.com/carpedm20/emoji) from 0.6.0 to 1.1.0. 861 | - [Release notes](https://github.com/carpedm20/emoji/releases) 862 | - [Changelog](https://github.com/carpedm20/emoji/blob/master/CHANGES.md) 863 | - [Commits](https://github.com/carpedm20/emoji/commits/v.1.1.0) 864 | 865 | ##### 🌱 Other 866 | 867 | - Release 1.1.0. 868 | - Upgrade to GitHub-native Dependabot (#26) 869 | 870 | 871 | #### api-client-pydantic `v1.0.2` (2021-01-21) 872 | 873 | ##### ♻️ Changes 874 | 875 | - Refactoring use args and kwargs. 876 | 877 | ##### ⬆️ Dependencies 878 | 879 | - ⬆️ Bump pytest-cov from 2.11.0 to 2.11.1. 880 | Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.11.0 to 2.11.1. 881 | - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) 882 | - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) 883 | - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.11.0...v2.11.1) 884 | - ⬆️ Bump pytest-cov from 2.10.1 to 2.11.0. 885 | Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.10.1 to 2.11.0. 886 | - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) 887 | - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) 888 | - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.10.1...v2.11.0) 889 | 890 | ##### 🌱 Other 891 | 892 | - Bumping version from 1.0.0 to 1.0.1. 893 | 894 | 895 | #### api-client-pydantic `v1.0.0` (2021-01-17) 896 | 897 | ##### ♻️ Changes 898 | 899 | - Publish job. 900 | 901 | ##### 🌱 Other 902 | 903 | - Bumping version from 0.1.0 to 1.0.0. 904 | - Move api_client_pydantic to apiclient_pydantic. 905 | now import `from apiclient_pydantic import *` 906 | 907 | 908 | #### api-client-pydantic `v0.1.1` (2021-01-16) 909 | 910 | ##### ♻️ Changes 911 | 912 | - Auto generate changelog. 913 | - Fix Changelog template. 914 | 915 | ##### 📝 Docs 916 | 917 | - Update README.md. 918 | - Prepare for generate CHANGELOG.md. 919 | 920 | 921 | #### api-client-pydantic `v0.1.0` (2021-01-15) 922 | 923 | ##### New ✨ 924 | 925 | - Basic functionality. 926 | 927 | ##### ♻️ Changes 928 | 929 | - Configs. 930 | 931 | ##### ⬆️ Dependencies 932 | 933 | - ➕ dependencies and pyproject. 934 | 935 | ##### 📝 Documentation 936 | 937 | - Common files. 938 | - Init readme. 939 | 940 | ##### 🌱 Other 941 | 942 | - CI Actions. 943 | - Add tests. 944 | - Initial commit. 945 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "annotated-types" 5 | version = "0.7.0" 6 | description = "Reusable constraint types to use with typing.Annotated" 7 | optional = false 8 | python-versions = ">=3.8" 9 | files = [ 10 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, 11 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, 12 | ] 13 | 14 | [[package]] 15 | name = "api-client" 16 | version = "1.3.1" 17 | description = "Separate the high level client implementation from the underlying CRUD." 18 | optional = false 19 | python-versions = ">=3.6" 20 | files = [ 21 | {file = "api-client-1.3.1.tar.gz", hash = "sha256:194e5c8f2b5200540464462a68ea9d06ad85d6f374f03d384f098711572ab946"}, 22 | ] 23 | 24 | [package.dependencies] 25 | requests = ">=2.16" 26 | tenacity = ">=5.1.0" 27 | 28 | [package.extras] 29 | deploy = ["requests", "twine"] 30 | dev = ["black", "flake8", "flake8-docstrings", "ipdb", "isort", "pytest", "pytest-cov", "pytest-env", "requests-mock", "vcrpy"] 31 | docs = ["black", "flake8", "flake8-docstrings", "ipdb", "isort", "pytest", "pytest-cov", "pytest-env", "requests-mock", "vcrpy"] 32 | lint = ["black", "flake8", "flake8-docstrings", "isort"] 33 | test = ["pytest", "pytest-cov", "pytest-env", "requests-mock", "vcrpy"] 34 | 35 | [[package]] 36 | name = "certifi" 37 | version = "2024.12.14" 38 | description = "Python package for providing Mozilla's CA Bundle." 39 | optional = false 40 | python-versions = ">=3.6" 41 | files = [ 42 | {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, 43 | {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, 44 | ] 45 | 46 | [[package]] 47 | name = "cfgv" 48 | version = "3.4.0" 49 | description = "Validate configuration and produce human readable error messages." 50 | optional = false 51 | python-versions = ">=3.8" 52 | files = [ 53 | {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, 54 | {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, 55 | ] 56 | 57 | [[package]] 58 | name = "charset-normalizer" 59 | version = "3.4.0" 60 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 61 | optional = false 62 | python-versions = ">=3.7.0" 63 | files = [ 64 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, 65 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, 66 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, 67 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, 68 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, 69 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, 70 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, 71 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, 72 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, 73 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, 74 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, 75 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, 76 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, 77 | {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, 78 | {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, 79 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, 80 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, 81 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, 82 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, 83 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, 84 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, 85 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, 86 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, 87 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, 88 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, 89 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, 90 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, 91 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, 92 | {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, 93 | {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, 94 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, 95 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, 96 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, 97 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, 98 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, 99 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, 100 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, 101 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, 102 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, 103 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, 104 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, 105 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, 106 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, 107 | {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, 108 | {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, 109 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, 110 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, 111 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, 112 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, 113 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, 114 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, 115 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, 116 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, 117 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, 118 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, 119 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, 120 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, 121 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, 122 | {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, 123 | {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, 124 | {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, 125 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, 126 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, 127 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, 128 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, 129 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, 130 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, 131 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, 132 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, 133 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, 134 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, 135 | {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, 136 | {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, 137 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, 138 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, 139 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, 140 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, 141 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, 142 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, 143 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, 144 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, 145 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, 146 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, 147 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, 148 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, 149 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, 150 | {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, 151 | {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, 152 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, 153 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, 154 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, 155 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, 156 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, 157 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, 158 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, 159 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, 160 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, 161 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, 162 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, 163 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, 164 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, 165 | {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, 166 | {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, 167 | {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, 168 | {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, 169 | ] 170 | 171 | [[package]] 172 | name = "colorama" 173 | version = "0.4.6" 174 | description = "Cross-platform colored terminal text." 175 | optional = false 176 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 177 | files = [ 178 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 179 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 180 | ] 181 | 182 | [[package]] 183 | name = "coverage" 184 | version = "7.6.9" 185 | description = "Code coverage measurement for Python" 186 | optional = false 187 | python-versions = ">=3.9" 188 | files = [ 189 | {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, 190 | {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, 191 | {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, 192 | {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, 193 | {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, 194 | {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, 195 | {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, 196 | {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, 197 | {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, 198 | {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, 199 | {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, 200 | {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, 201 | {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, 202 | {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, 203 | {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, 204 | {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, 205 | {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, 206 | {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, 207 | {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, 208 | {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, 209 | {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, 210 | {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, 211 | {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, 212 | {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, 213 | {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, 214 | {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, 215 | {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, 216 | {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, 217 | {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, 218 | {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, 219 | {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, 220 | {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, 221 | {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, 222 | {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, 223 | {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, 224 | {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, 225 | {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, 226 | {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, 227 | {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, 228 | {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, 229 | {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, 230 | {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, 231 | {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, 232 | {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, 233 | {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, 234 | {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, 235 | {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, 236 | {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, 237 | {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, 238 | {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, 239 | {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, 240 | {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, 241 | {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, 242 | {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, 243 | {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, 244 | {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, 245 | {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, 246 | {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, 247 | {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, 248 | {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, 249 | {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, 250 | {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, 251 | ] 252 | 253 | [package.dependencies] 254 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 255 | 256 | [package.extras] 257 | toml = ["tomli"] 258 | 259 | [[package]] 260 | name = "distlib" 261 | version = "0.3.9" 262 | description = "Distribution utilities" 263 | optional = false 264 | python-versions = "*" 265 | files = [ 266 | {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, 267 | {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, 268 | ] 269 | 270 | [[package]] 271 | name = "exceptiongroup" 272 | version = "1.2.2" 273 | description = "Backport of PEP 654 (exception groups)" 274 | optional = false 275 | python-versions = ">=3.7" 276 | files = [ 277 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 278 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 279 | ] 280 | 281 | [package.extras] 282 | test = ["pytest (>=6)"] 283 | 284 | [[package]] 285 | name = "filelock" 286 | version = "3.12.4" 287 | description = "A platform independent file lock." 288 | optional = false 289 | python-versions = ">=3.8" 290 | files = [ 291 | {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, 292 | {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, 293 | ] 294 | 295 | [package.extras] 296 | docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] 297 | testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] 298 | typing = ["typing-extensions (>=4.7.1)"] 299 | 300 | [[package]] 301 | name = "identify" 302 | version = "2.6.3" 303 | description = "File identification library for Python" 304 | optional = false 305 | python-versions = ">=3.9" 306 | files = [ 307 | {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, 308 | {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, 309 | ] 310 | 311 | [package.extras] 312 | license = ["ukkonen"] 313 | 314 | [[package]] 315 | name = "idna" 316 | version = "3.10" 317 | description = "Internationalized Domain Names in Applications (IDNA)" 318 | optional = false 319 | python-versions = ">=3.6" 320 | files = [ 321 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 322 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 323 | ] 324 | 325 | [package.extras] 326 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 327 | 328 | [[package]] 329 | name = "iniconfig" 330 | version = "2.0.0" 331 | description = "brain-dead simple config-ini parsing" 332 | optional = false 333 | python-versions = ">=3.7" 334 | files = [ 335 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 336 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 337 | ] 338 | 339 | [[package]] 340 | name = "mypy" 341 | version = "1.13.0" 342 | description = "Optional static typing for Python" 343 | optional = false 344 | python-versions = ">=3.8" 345 | files = [ 346 | {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, 347 | {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, 348 | {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, 349 | {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, 350 | {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, 351 | {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, 352 | {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, 353 | {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, 354 | {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, 355 | {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, 356 | {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, 357 | {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, 358 | {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, 359 | {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, 360 | {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, 361 | {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, 362 | {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, 363 | {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, 364 | {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, 365 | {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, 366 | {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, 367 | {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, 368 | {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, 369 | {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, 370 | {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, 371 | {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, 372 | {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, 373 | {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, 374 | {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, 375 | {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, 376 | {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, 377 | {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, 378 | ] 379 | 380 | [package.dependencies] 381 | mypy-extensions = ">=1.0.0" 382 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 383 | typing-extensions = ">=4.6.0" 384 | 385 | [package.extras] 386 | dmypy = ["psutil (>=4.0)"] 387 | faster-cache = ["orjson"] 388 | install-types = ["pip"] 389 | mypyc = ["setuptools (>=50)"] 390 | reports = ["lxml"] 391 | 392 | [[package]] 393 | name = "mypy-extensions" 394 | version = "1.0.0" 395 | description = "Type system extensions for programs checked with the mypy type checker." 396 | optional = false 397 | python-versions = ">=3.5" 398 | files = [ 399 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 400 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 401 | ] 402 | 403 | [[package]] 404 | name = "nodeenv" 405 | version = "1.9.1" 406 | description = "Node.js virtual environment builder" 407 | optional = false 408 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 409 | files = [ 410 | {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, 411 | {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, 412 | ] 413 | 414 | [[package]] 415 | name = "packaging" 416 | version = "24.2" 417 | description = "Core utilities for Python packages" 418 | optional = false 419 | python-versions = ">=3.8" 420 | files = [ 421 | {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, 422 | {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, 423 | ] 424 | 425 | [[package]] 426 | name = "platformdirs" 427 | version = "4.3.6" 428 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 429 | optional = false 430 | python-versions = ">=3.8" 431 | files = [ 432 | {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, 433 | {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, 434 | ] 435 | 436 | [package.extras] 437 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] 438 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] 439 | type = ["mypy (>=1.11.2)"] 440 | 441 | [[package]] 442 | name = "pluggy" 443 | version = "1.5.0" 444 | description = "plugin and hook calling mechanisms for python" 445 | optional = false 446 | python-versions = ">=3.8" 447 | files = [ 448 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 449 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 450 | ] 451 | 452 | [package.extras] 453 | dev = ["pre-commit", "tox"] 454 | testing = ["pytest", "pytest-benchmark"] 455 | 456 | [[package]] 457 | name = "pre-commit" 458 | version = "4.0.1" 459 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 460 | optional = false 461 | python-versions = ">=3.9" 462 | files = [ 463 | {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, 464 | {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, 465 | ] 466 | 467 | [package.dependencies] 468 | cfgv = ">=2.0.0" 469 | identify = ">=1.0.0" 470 | nodeenv = ">=0.11.1" 471 | pyyaml = ">=5.1" 472 | virtualenv = ">=20.10.0" 473 | 474 | [[package]] 475 | name = "pydantic" 476 | version = "2.10.3" 477 | description = "Data validation using Python type hints" 478 | optional = false 479 | python-versions = ">=3.8" 480 | files = [ 481 | {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, 482 | {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, 483 | ] 484 | 485 | [package.dependencies] 486 | annotated-types = ">=0.6.0" 487 | pydantic-core = "2.27.1" 488 | typing-extensions = ">=4.12.2" 489 | 490 | [package.extras] 491 | email = ["email-validator (>=2.0.0)"] 492 | timezone = ["tzdata"] 493 | 494 | [[package]] 495 | name = "pydantic-core" 496 | version = "2.27.1" 497 | description = "Core functionality for Pydantic validation and serialization" 498 | optional = false 499 | python-versions = ">=3.8" 500 | files = [ 501 | {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, 502 | {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, 503 | {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, 504 | {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, 505 | {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, 506 | {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, 507 | {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, 508 | {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, 509 | {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, 510 | {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, 511 | {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, 512 | {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, 513 | {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, 514 | {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, 515 | {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, 516 | {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, 517 | {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, 518 | {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, 519 | {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, 520 | {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, 521 | {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, 522 | {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, 523 | {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, 524 | {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, 525 | {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, 526 | {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, 527 | {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, 528 | {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, 529 | {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, 530 | {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, 531 | {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, 532 | {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, 533 | {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, 534 | {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, 535 | {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, 536 | {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, 537 | {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, 538 | {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, 539 | {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, 540 | {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, 541 | {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, 542 | {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, 543 | {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, 544 | {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, 545 | {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, 546 | {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, 547 | {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, 548 | {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, 549 | {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, 550 | {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, 551 | {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, 552 | {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, 553 | {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, 554 | {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, 555 | {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, 556 | {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, 557 | {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, 558 | {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, 559 | {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, 560 | {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, 561 | {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, 562 | {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, 563 | {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, 564 | {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, 565 | {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, 566 | {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, 567 | {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, 568 | {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, 569 | {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, 570 | {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, 571 | {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, 572 | {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, 573 | {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, 574 | {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, 575 | {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, 576 | {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, 577 | {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, 578 | {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, 579 | {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, 580 | {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, 581 | {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, 582 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, 583 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, 584 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, 585 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, 586 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, 587 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, 588 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, 589 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, 590 | {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, 591 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, 592 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, 593 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, 594 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, 595 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, 596 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, 597 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, 598 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, 599 | {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, 600 | {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, 601 | ] 602 | 603 | [package.dependencies] 604 | typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" 605 | 606 | [[package]] 607 | name = "pytest" 608 | version = "8.3.4" 609 | description = "pytest: simple powerful testing with Python" 610 | optional = false 611 | python-versions = ">=3.8" 612 | files = [ 613 | {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, 614 | {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, 615 | ] 616 | 617 | [package.dependencies] 618 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 619 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 620 | iniconfig = "*" 621 | packaging = "*" 622 | pluggy = ">=1.5,<2" 623 | tomli = {version = ">=1", markers = "python_version < \"3.11\""} 624 | 625 | [package.extras] 626 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 627 | 628 | [[package]] 629 | name = "pytest-asyncio" 630 | version = "0.25.0" 631 | description = "Pytest support for asyncio" 632 | optional = false 633 | python-versions = ">=3.9" 634 | files = [ 635 | {file = "pytest_asyncio-0.25.0-py3-none-any.whl", hash = "sha256:db5432d18eac6b7e28b46dcd9b69921b55c3b1086e85febfe04e70b18d9e81b3"}, 636 | {file = "pytest_asyncio-0.25.0.tar.gz", hash = "sha256:8c0610303c9e0442a5db8604505fc0f545456ba1528824842b37b4a626cbf609"}, 637 | ] 638 | 639 | [package.dependencies] 640 | pytest = ">=8.2,<9" 641 | 642 | [package.extras] 643 | docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] 644 | testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] 645 | 646 | [[package]] 647 | name = "pytest-cov" 648 | version = "6.0.0" 649 | description = "Pytest plugin for measuring coverage." 650 | optional = false 651 | python-versions = ">=3.9" 652 | files = [ 653 | {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, 654 | {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, 655 | ] 656 | 657 | [package.dependencies] 658 | coverage = {version = ">=7.5", extras = ["toml"]} 659 | pytest = ">=4.6" 660 | 661 | [package.extras] 662 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] 663 | 664 | [[package]] 665 | name = "pyyaml" 666 | version = "6.0.2" 667 | description = "YAML parser and emitter for Python" 668 | optional = false 669 | python-versions = ">=3.8" 670 | files = [ 671 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 672 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 673 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 674 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 675 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 676 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 677 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 678 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 679 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 680 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 681 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 682 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 683 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 684 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 685 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 686 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 687 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 688 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 689 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 690 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 691 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 692 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 693 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 694 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 695 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 696 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 697 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 698 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 699 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 700 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 701 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 702 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 703 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 704 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 705 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 706 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 707 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 708 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 709 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 710 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 711 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 712 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 713 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 714 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 715 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 716 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 717 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 718 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 719 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 720 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 721 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 722 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 723 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 724 | ] 725 | 726 | [[package]] 727 | name = "requests" 728 | version = "2.32.3" 729 | description = "Python HTTP for Humans." 730 | optional = false 731 | python-versions = ">=3.8" 732 | files = [ 733 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, 734 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, 735 | ] 736 | 737 | [package.dependencies] 738 | certifi = ">=2017.4.17" 739 | charset-normalizer = ">=2,<4" 740 | idna = ">=2.5,<4" 741 | urllib3 = ">=1.21.1,<3" 742 | 743 | [package.extras] 744 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 745 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 746 | 747 | [[package]] 748 | name = "ruff" 749 | version = "0.8.3" 750 | description = "An extremely fast Python linter and code formatter, written in Rust." 751 | optional = false 752 | python-versions = ">=3.7" 753 | files = [ 754 | {file = "ruff-0.8.3-py3-none-linux_armv6l.whl", hash = "sha256:8d5d273ffffff0acd3db5bf626d4b131aa5a5ada1276126231c4174543ce20d6"}, 755 | {file = "ruff-0.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e4d66a21de39f15c9757d00c50c8cdd20ac84f55684ca56def7891a025d7e939"}, 756 | {file = "ruff-0.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c356e770811858bd20832af696ff6c7e884701115094f427b64b25093d6d932d"}, 757 | {file = "ruff-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c0a60a825e3e177116c84009d5ebaa90cf40dfab56e1358d1df4e29a9a14b13"}, 758 | {file = "ruff-0.8.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fb782f4db39501210ac093c79c3de581d306624575eddd7e4e13747e61ba18"}, 759 | {file = "ruff-0.8.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f26bc76a133ecb09a38b7868737eded6941b70a6d34ef53a4027e83913b6502"}, 760 | {file = "ruff-0.8.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:01b14b2f72a37390c1b13477c1c02d53184f728be2f3ffc3ace5b44e9e87b90d"}, 761 | {file = "ruff-0.8.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53babd6e63e31f4e96ec95ea0d962298f9f0d9cc5990a1bbb023a6baf2503a82"}, 762 | {file = "ruff-0.8.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ae441ce4cf925b7f363d33cd6570c51435972d697e3e58928973994e56e1452"}, 763 | {file = "ruff-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7c65bc0cadce32255e93c57d57ecc2cca23149edd52714c0c5d6fa11ec328cd"}, 764 | {file = "ruff-0.8.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5be450bb18f23f0edc5a4e5585c17a56ba88920d598f04a06bd9fd76d324cb20"}, 765 | {file = "ruff-0.8.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8faeae3827eaa77f5721f09b9472a18c749139c891dbc17f45e72d8f2ca1f8fc"}, 766 | {file = "ruff-0.8.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:db503486e1cf074b9808403991663e4277f5c664d3fe237ee0d994d1305bb060"}, 767 | {file = "ruff-0.8.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6567be9fb62fbd7a099209257fef4ad2c3153b60579818b31a23c886ed4147ea"}, 768 | {file = "ruff-0.8.3-py3-none-win32.whl", hash = "sha256:19048f2f878f3ee4583fc6cb23fb636e48c2635e30fb2022b3a1cd293402f964"}, 769 | {file = "ruff-0.8.3-py3-none-win_amd64.whl", hash = "sha256:f7df94f57d7418fa7c3ffb650757e0c2b96cf2501a0b192c18e4fb5571dfada9"}, 770 | {file = "ruff-0.8.3-py3-none-win_arm64.whl", hash = "sha256:fe2756edf68ea79707c8d68b78ca9a58ed9af22e430430491ee03e718b5e4936"}, 771 | {file = "ruff-0.8.3.tar.gz", hash = "sha256:5e7558304353b84279042fc584a4f4cb8a07ae79b2bf3da1a7551d960b5626d3"}, 772 | ] 773 | 774 | [[package]] 775 | name = "tenacity" 776 | version = "9.0.0" 777 | description = "Retry code until it succeeds" 778 | optional = false 779 | python-versions = ">=3.8" 780 | files = [ 781 | {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, 782 | {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, 783 | ] 784 | 785 | [package.extras] 786 | doc = ["reno", "sphinx"] 787 | test = ["pytest", "tornado (>=4.5)", "typeguard"] 788 | 789 | [[package]] 790 | name = "tomli" 791 | version = "2.2.1" 792 | description = "A lil' TOML parser" 793 | optional = false 794 | python-versions = ">=3.8" 795 | files = [ 796 | {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, 797 | {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, 798 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, 799 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, 800 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, 801 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, 802 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, 803 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, 804 | {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, 805 | {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, 806 | {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, 807 | {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, 808 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, 809 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, 810 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, 811 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, 812 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, 813 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, 814 | {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, 815 | {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, 816 | {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, 817 | {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, 818 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, 819 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, 820 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, 821 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, 822 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, 823 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, 824 | {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, 825 | {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, 826 | {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, 827 | {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, 828 | ] 829 | 830 | [[package]] 831 | name = "types-setuptools" 832 | version = "75.6.0.20241126" 833 | description = "Typing stubs for setuptools" 834 | optional = false 835 | python-versions = ">=3.8" 836 | files = [ 837 | {file = "types_setuptools-75.6.0.20241126-py3-none-any.whl", hash = "sha256:aaae310a0e27033c1da8457d4d26ac673b0c8a0de7272d6d4708e263f2ea3b9b"}, 838 | {file = "types_setuptools-75.6.0.20241126.tar.gz", hash = "sha256:7bf25ad4be39740e469f9268b6beddda6e088891fa5a27e985c6ce68bf62ace0"}, 839 | ] 840 | 841 | [[package]] 842 | name = "types-toml" 843 | version = "0.10.8.20240310" 844 | description = "Typing stubs for toml" 845 | optional = false 846 | python-versions = ">=3.8" 847 | files = [ 848 | {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, 849 | {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, 850 | ] 851 | 852 | [[package]] 853 | name = "typing-extensions" 854 | version = "4.12.2" 855 | description = "Backported and Experimental Type Hints for Python 3.8+" 856 | optional = false 857 | python-versions = ">=3.8" 858 | files = [ 859 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 860 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 861 | ] 862 | 863 | [[package]] 864 | name = "urllib3" 865 | version = "2.2.3" 866 | description = "HTTP library with thread-safe connection pooling, file post, and more." 867 | optional = false 868 | python-versions = ">=3.8" 869 | files = [ 870 | {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, 871 | {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, 872 | ] 873 | 874 | [package.extras] 875 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 876 | h2 = ["h2 (>=4,<5)"] 877 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 878 | zstd = ["zstandard (>=0.18.0)"] 879 | 880 | [[package]] 881 | name = "virtualenv" 882 | version = "20.28.0" 883 | description = "Virtual Python Environment builder" 884 | optional = false 885 | python-versions = ">=3.8" 886 | files = [ 887 | {file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"}, 888 | {file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"}, 889 | ] 890 | 891 | [package.dependencies] 892 | distlib = ">=0.3.7,<1" 893 | filelock = ">=3.12.2,<4" 894 | platformdirs = ">=3.9.1,<5" 895 | 896 | [package.extras] 897 | 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)"] 898 | test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] 899 | 900 | [metadata] 901 | lock-version = "2.0" 902 | python-versions = ">=3.9,<4" 903 | content-hash = "0371d6ba0912ae01902af9c1685ecab38e31da22fb71c173f3ddddcc3baf65c1" 904 | --------------------------------------------------------------------------------