├── 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 | 
2 | 
3 | 
4 | 
5 | 
6 | [](https://github.com/mom1/api-client-pydantic/blob/master/LICENSE)
7 |
8 | [](https://pypi.python.org/pypi/api-client-pydantic)
9 | []()
10 | 
11 |
12 |
13 | [](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 |
--------------------------------------------------------------------------------