diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 34543fdf2..21901c0d4 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -2,16 +2,16 @@ name: Run Checks on: push: - branches: [ "main" ] + branches: [ "prod/2.x" ] pull_request: - branches: [ "main" ] + branches: [ "prod/2.x" ] merge_group: jobs: test: strategy: matrix: - python: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13" ] + python: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: @@ -30,33 +30,33 @@ jobs: uses: actions/cache@v4 with: path: .venv - key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies-${{ hashFiles('**/pdm.lock') }} + key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies-${{ hashFiles('**/poetry.lock') }} restore-keys: | ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies - - name: Install PDM - run: pip install pdm + - name: Install Poetry + run: pip install poetry - name: Install Dependencies - run: pdm install + run: poetry install - name: Check formatting - run: pdm run ruff format . --check + run: poetry run ruff format . --check - name: Run mypy - run: pdm mypy --show-error-codes + run: poetry run task mypy --show-error-codes - name: Lint - run: pdm run ruff check . + run: poetry run ruff check . - name: Run pytest without coverage if: matrix.os != 'ubuntu-latest' - run: pdm test + run: poetry run task test env: TASKIPY: true - name: Run pytest with coverage if: matrix.os == 'ubuntu-latest' - run: pdm test_with_coverage + run: poetry run task test_with_coverage env: TASKIPY: true @@ -131,7 +131,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5.3.0 with: - python-version: "3.8" + python-version: "3.9" - name: Get Python Version id: get_python_version run: echo "python_version=$(python --version)" >> $GITHUB_OUTPUT @@ -139,19 +139,23 @@ jobs: uses: actions/cache@v4 with: path: .venv - key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies-${{ hashFiles('**/pdm.lock') }} + key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies-${{ hashFiles('**/poetry.lock') }} restore-keys: | ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies + # General note on this job: the main project uses poetry, but the project inside integration-tests/ + # uses pdm. That's because integration-tests/ is a generated client-- the output from running the + # generator on a test API spec-- and the "make a pdm project" option was used in that process. - name: Install dependencies run: | + pip install poetry pip install pdm python -m venv .venv - pdm install + poetry install - name: Cache Generated Client Dependencies uses: actions/cache@v4 with: path: integration-tests/.venv - key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-integration-dependencies-${{ hashFiles('**/pdm.lock') }} + key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-integration-dependencies-${{ hashFiles('**/poetry.lock') }} restore-keys: | ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-integration-dependencies - name: Set httpx version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index bd34b1c41..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Release - -on: - pull_request: - types: [closed] - branches: [main] - -jobs: - release: - if: github.head_ref == 'knope/release' && github.event.pull_request.merged == true - runs-on: ubuntu-latest - permissions: - id-token: write - steps: - - uses: actions/checkout@v4.2.2 - - name: Install Hatchling - run: pip install --upgrade hatchling - - name: Build - run: hatchling build - - name: Push to PyPI - uses: pypa/gh-action-pypi-publish@v1.10.3 - with: - attestations: true diff --git a/.gitignore b/.gitignore index b04b9f514..124125572 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,7 @@ htmlcov/ # Generated end to end test data my-test-api-client/ custom-e2e/ -3-1-features-client \ No newline at end of file +3-1-features-client +localtest + +!tests/.env diff --git a/CHANGELOG.md b/CHANGELOG.md index 037998c3e..6569f30fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,72 @@ Programmatic usage of this project (e.g., importing it as a Python module) and t The 0.x prefix used in versions for this project is to indicate that breaking changes are expected frequently (several times a year). Breaking changes will increment the minor number, all other changes will increment the patch number. You can track the progress toward 1.0 [here](https://github.com/openapi-generators/openapi-python-client/projects/2). +## 0.23.0 (2024-12-24) + +### Breaking Changes + +#### Delete fewer files with `--overwrite` + +`--overwrite` will no longer delete the entire output directory before regenerating. Instead, it will only delete +specific, known directories within that directory. Right now, that is only the generated `models` and `api` directories. + +Other generated files, like `README.md`, will be overwritten. Extra files and directories outside of those listed above +will be left untouched, so you can any extra modules or files around while still updating `pyproject.toml` automatically. + +Closes #1105. + +### Features + +- Support httpx 0.28 (#1172) + +#### Add `generate_all_tags` config option + +You can now, optionally, generate **duplicate** endpoint functions/modules using _every_ tag for an endpoint, +not just the first one, by setting `generate_all_tags: true` in your configuration file. + +### Fixes + +- Support Typer 0.14 and 0.15 (#1173) + +#### Fix minimum `attrs` version + +The minimum `attrs` dependency version was incorrectly set to 21.3.0. This has been corrected to 22.2.0, the minimum +supported version since `openapi-python-client` 0.19.1. + +Closes #1084, thanks @astralblue! + +#### Fix compatibility with Pydantic 2.10+ + +##1176 by @Viicos + +Set `defer_build` to models that we know will fail to build, and call `model_rebuild` +in the `__init__.py` file. + +## 0.22.0 (2024-11-23) + +### Breaking Changes + +#### Drop support for Python 3.8 + +Python 3.8 is no longer supported. "New" 3.9 syntax, like generics on builtin collections, is used both in the generator +and the generated code. + +#### `type` is now a reserved field name + +Because `type` is used in type annotations now, it is no longer a valid field name. Fields which were previously named +`type` will be renamed to `type_`. + +### Features + +- Support Ruff 0.8 (#1169) + +## 0.21.7 (2024-10-28) + +### Fixes + +- allow required fields list to be specified as empty (#651) (#1149) +- import cast for required const properties, since it's used in the template (#1153) + ## 0.21.6 (2024-10-20) ### Features diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52b67100a..8cb7a56e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,48 +1,41 @@ -# Ways you can Contribute +# Note: this is the fork of openapi-python-client maintained and used by Benchling. -- Report bugs via [issues](https://github.com/openapi-generators/openapi-python-client/issues) -- Request features via [discussions](https://github.com/openapi-generators/openapi-python-client/discussions) -- Contribute code via [pull request](https://github.com/openapi-generators/openapi-python-client/pulls) +If you want the official package, go to the upstream repository: https://github.com/openapi-generators/openapi-python-client -## Reporting a bug +For contribution guidelines (including contributing fixes from our fork to the upstream repo), see Benchling internal documentation. -A bug is one of: +This document mostly contains basic developer instructions. -1. You get an exception when running the generator -2. The generated code is invalid or incorrect -3. An error message is unclear or incorrect -4. Something which used to work no longer works, except: - 1. Intentional breaking changes, which are documented in the [changelog](https://github.com/openapi-generators/openapi-python-client/blob/main/CHANGELOG.md) - 2. Breaking changes to unstable features, like custom templates +## Setting up environment -If your issue does not fall under one of the above, it is not a bug; check out "[Requesting a feature](#requesting-a-feature). +Create a virtualenv with the lowest compatible Python version (currently 3.9). -### Report requirements +To install dependencies: -A bug report **must** have an OpenAPI document that can be used to replicate the bug. Reports without a valid document will be closed. +``` +pip install poetry +poetry install +``` -## Requesting a feature +## Running tests -A feature is usually: +See the [upstream repo]() for a full description of how the tests in this project are written. -1. An improvement to the way the generated code works -2. A feature of the generator itself which makes its use easier (e.g., a new config option) -3. **Support for part of the OpenAPI spec**; this generator _does not yet_ support every OpenAPI feature, these missing features **are not bugs**. +`poetry run task unit` runs only the basic unit tests. -To request a feature: +`poetry run task test` runs unit tests plus end-to-end tests. -1. Search through [discussions](https://github.com/openapi-generators/openapi-python-client/discussions/categories/feature-request) to see if the feature you want has already been requested. If it has: - 1. Upvote it with the little arrow on the original post. This enables code contributors to prioritize the most-demanded features. - 2. Optionally leave a comment describing why _you_ want the feature, if no existing thread already covers your use-case -2. If a relevant discussion does not already exist, create a new one. If you are not requesting support for part of the OpenAPI spec, **you must** describe _why_ you want the feature. What real-world use-case does it improve? For example, "raise exceptions for invalid responses" might have a description of "it's not worth the effort to check every error case by hand for the one-off scripts I'm writing". +## Linting/type-checking -## Contributing Code +The project uses ruff. `poetry run task lint` and `poetry run task format` run `ruff check` and `ruff format`. + +`poetry run task mypy` runs `mypy` on only the package module (tests are excluded). -### Setting up a Dev Environment +`poetry run task check` runs all of those plus tests. -1. Make sure you have [PDM](https://pdm-project.org) installed and up to date. -2. Make sure you have a supported Python version (e.g. 3.8) installed. -3. Use `pdm install` in the project directory to create a virtual environment with the relevant dependencies. +## Contributing Code + +This section is a copy from the upstream repo, with some changes due to Benchling test framework additions that haven't yet been accepted upstream. ### Writing tests @@ -50,35 +43,37 @@ All changes must be tested, I recommend writing the test first, then writing the If you think that some of the added code is not testable (or testing it would add little value), mention that in your PR and we can discuss it. -1. If you're adding support for a new OpenAPI feature or covering a new edge case, add an [end-to-end test](#end-to-end-tests) -2. If you're modifying the way an existing feature works, make sure an existing test generates the _old_ code in `end_to_end_tests/golden-record`. You'll use this to check for the new code once your changes are complete. -3. If you're improving an error or adding a new error, add a [unit test](#unit-tests) +1. If you're adding support for a new OpenAPI feature or covering a new edge case, add [functional tests](#functional-tests), and optionally an [end-to-end snapshot test](#end-to-end-snapshot-tests). +2. If you're modifying the way an existing feature works, make sure functional tests cover this case. Existing end-to-end snapshot tests might also be affected if you have changed what generated model/endpoint code looks like. +3. If you're improving error handling or adding a new error, add [functional tests](#functional-tests). +4. For tests of low-level pieces of code that are fairly self-contained, and not tightly coupled to other internal implementation details, you can use regular [unit tests](#unit-tests). + +#### End-to-end snapshot tests -#### End-to-end tests +This project aims to have all "happy paths" (types of code which _can_ be generated) covered by end-to-end tests. There are two types of these: snapshot tests, and functional tests. -This project aims to have all "happy paths" (types of code which _can_ be generated) covered by end to end tests (snapshot tests). In order to check code changes against the previous set of snapshots (called a "golden record" here), you can run `pdm e2e`. To regenerate the snapshots, run `pdm regen`. +Snapshot tests verify that the generated code is identical to a previously-committed set of snapshots (called a "golden record" here). They are basically regression tests to catch any unintended changes in the generator output. -There are 4 types of snapshots generated right now, you may have to update only some or all of these depending on the changes you're making. Within the `end_to_end_tets` directory: +In order to check code changes against the previous set of snapshots (called a "golden record" here), you can run `poetry run task e2e`. To regenerate the snapshots, run `poetry run task regen`. + +There are 4 types of snapshots generated right now, you may have to update only some or all of these depending on the changes you're making. Within the `end_to_end_tests` directory: 1. `baseline_openapi_3.0.json` creates `golden-record` for testing OpenAPI 3.0 features 2. `baseline_openapi_3.1.yaml` is checked against `golden-record` for testing OpenAPI 3.1 features (and ensuring consistency with 3.0) 3. `test_custom_templates` are used with `baseline_openapi_3.0.json` to generate `custom-templates-golden-record` for testing custom templates 4. `3.1_specific.openapi.yaml` is used to generate `test-3-1-golden-record` and test 3.1-specific features (things which do not have a 3.0 equivalent) -#### Unit tests - -> **NOTE**: Several older-style unit tests using mocks exist in this project. These should be phased out rather than updated, as the tests are brittle and difficult to maintain. Only error cases should be tests with unit tests going forward. +#### Functional tests -In some cases, we need to test things which cannot be generated—like validating that errors are caught and handled correctly. These should be tested via unit tests in the `tests` directory, using the `pytest` framework. +These are black-box tests that verify the runtime behavior of generated code, as well as the generator's validation behavior. They are also end-to-end tests, since they run the generator as a shell command. -### Creating a Pull Request +This can sometimes identify issues with error handling, validation logic, module imports, etc., that might be harder to diagnose via the snapshot tests, especially during development of a new feature. For instance, they can verify that JSON data is correctly decoded into model class attributes, or that the generator will emit an appropriate warning or error for an invalid spec. -Once you've written the tests and code and run the checks, the next step is to create a pull request against the `main` branch of this repository. This repository uses [Knope] to auto-generate release notes and version numbers. This can either be done by setting the title of the PR to a [conventional commit] (for simple changes) or by adding [changesets]. If the changes are not documented yet, a check will fail on GitHub. The details of this check will have suggestions for documenting the change (including an example change file for changesets). +See [`end_to_end_tests/functional_tests`](./end_to_end_tests/functional_tests). -### Wait for Review +#### Unit tests -As soon as possible, your PR will be reviewed. If there are any changes requested there will likely be a bit of back and forth. Once this process is done, your changes will be merged into main and included in the next release. If you need your changes available on PyPI by a certain time, please mention it in the PR, and we'll do our best to accommodate. +These include: -[Knope]: https://knope.tech -[changesets]: https://knope.tech/reference/concepts/changeset/ -[Conventional Commits]: https://knope.tech/reference/concepts/conventional-commits/ +* Regular unit tests of basic pieces of fairly self-contained low-level functionality, such as helper functions. These are implemented in the `tests` directory, using the `pytest` framework. +* Older-style unit tests of low-level functions like `property_from_data` that have complex behavior. These are brittle and difficult to maintain, and should not be used going forward. Instead, they should be migrated to functional tests. diff --git a/README.md b/README.md index 871f3a296..85efbe0c3 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ -![Run Checks](https://github.com/openapi-generators/openapi-python-client/workflows/Run%20Checks/badge.svg) -[![codecov](https://codecov.io/gh/openapi-generators/openapi-python-client/branch/main/graph/badge.svg)](https://codecov.io/gh/triaxtec/openapi-python-client) [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/) -[![Generic badge](https://img.shields.io/badge/type_checked-mypy-informational.svg)](https://mypy.readthedocs.io/en/stable/introduction.html) -[![PyPI version shields.io](https://img.shields.io/pypi/v/openapi-python-client.svg)](https://pypi.python.org/pypi/openapi-python-client/) -[![Downloads](https://static.pepy.tech/personalized-badge/openapi-python-client?period=total&units=international_system&left_color=blue&right_color=green&left_text=Downloads)](https://pepy.tech/project/openapi-python-client) + +# Note: this is the fork of openapi-python-client maintained and used by Benchling. + +If you want the official package, go to the upstream repository: https://github.com/openapi-generators/openapi-python-client + +The rest of this file is a copy of the upstream README, except for descriptions of extra options added in the Benchling fork. All other Benchling-specific notes are in our internal documentation. + +Benchling cannot provide any support for external use of this fork. # openapi-python-client @@ -97,6 +100,16 @@ class_overrides: The easiest way to find what needs to be overridden is probably to generate your client and go look at everything in the `models` folder. +### docstrings_on_attributes + +By default, when `openapi-python-client` generates a model class, it includes a list of attributes and their +descriptions in the docstring for the class. If you set this option to `true`, then the attribute descriptions +will be put in docstrings for the attributes themselves, and will not be in the class docstring. + +```yaml +docstrings_on_attributes: true +``` + ### literal_enums By default, `openapi-python-client` generates classes inheriting for `Enum` for enums. It can instead use `Literal` @@ -108,6 +121,16 @@ literal_enums: true This is especially useful if enum values, when transformed to their Python names, end up conflicting due to case sensitivity or special symbols. +### generate_all_tags + +`openapi-python-client` generates module names within the `api` module based on the OpenAPI `tags` of each endpoint. +By default, only the _first_ tag is generated. If you want to generate **duplicate** endpoint functions using _every_ tag +listed, you can enable this option: + +```yaml +generate_all_tags: true +``` + ### project_name_override and package_name_override Used to change the name of generated client library project/package. If the project name is changed but an override for the package name diff --git a/end_to_end_tests/__init__.py b/end_to_end_tests/__init__.py index 1bf33f63f..3793e0395 100644 --- a/end_to_end_tests/__init__.py +++ b/end_to_end_tests/__init__.py @@ -1 +1,5 @@ """ Generate a complete client and verify that it is correct """ +import pytest + +pytest.register_assert_rewrite("end_to_end_tests.end_to_end_test_helpers") +pytest.register_assert_rewrite("end_to_end_tests.functional_tests.helpers") diff --git a/end_to_end_tests/__snapshots__/test_end_to_end.ambr b/end_to_end_tests/__snapshots__/test_end_to_end.ambr index c87445ffb..525f8baf2 100644 --- a/end_to_end_tests/__snapshots__/test_end_to_end.ambr +++ b/end_to_end_tests/__snapshots__/test_end_to_end.ambr @@ -1,4 +1,18 @@ # serializer version: 1 +# name: test_documents_with_errors[bad-status-code] + ''' + Generating /test-documents-with-errors + Warning(s) encountered while generating. Client was generated, but some pieces may be missing + + WARNING parsing GET / within default. + + Invalid response status code abcdef (not a valid HTTP status code), response will be omitted from generated client + + + If you believe this was a mistake or this tool is missing a feature you need, please open an issue at https://github.com/openapi-generators/openapi-python-client/issues/new/choose + + ''' +# --- # name: test_documents_with_errors[circular-body-ref] ''' Generating /test-documents-with-errors diff --git a/end_to_end_tests/baseline_openapi_3.0.json b/end_to_end_tests/baseline_openapi_3.0.json index 22a786a4f..b38bcab41 100644 --- a/end_to_end_tests/baseline_openapi_3.0.json +++ b/end_to_end_tests/baseline_openapi_3.0.json @@ -991,6 +991,20 @@ } } }, + "/responses/reference": { + "get": { + "tags": [ + "responses" + ], + "summary": "Endpoint using predefined response", + "operationId": "reference_response", + "responses": { + "200": { + "$ref": "#/components/responses/AResponse" + } + } + } + }, "/auth/token_with_cookie": { "get": { "tags": [ @@ -1149,9 +1163,7 @@ }, "/tag_with_number": { "get": { - "tags": [ - "1" - ], + "tags": ["1", "2"], "responses": { "200": { "description": "Success" @@ -2844,53 +2856,6 @@ "ModelWithBackslashInDescription": { "type": "object", "description": "Description with special character: \\" - }, - "ModelWithDiscriminatedUnion": { - "type": "object", - "properties": { - "discriminated_union": { - "allOf": [ - { - "$ref": "#/components/schemas/ADiscriminatedUnion" - } - ], - "nullable": true - } - } - }, - "ADiscriminatedUnion": { - "type": "object", - "discriminator": { - "propertyName": "modelType", - "mapping": { - "type1": "#/components/schemas/ADiscriminatedUnionType1", - "type2": "#/components/schemas/ADiscriminatedUnionType2" - } - }, - "oneOf": [ - { - "$ref": "#/components/schemas/ADiscriminatedUnionType1" - }, - { - "$ref": "#/components/schemas/ADiscriminatedUnionType2" - } - ] - }, - "ADiscriminatedUnionType1": { - "type": "object", - "properties": { - "modelType": { - "type": "string" - } - } - }, - "ADiscriminatedUnionType2": { - "type": "object", - "properties": { - "modelType": { - "type": "string" - } - } } }, "parameters": { @@ -2971,6 +2936,18 @@ } } } + }, + "responses": { + "AResponse": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AModel" + } + } + } + } } } } diff --git a/end_to_end_tests/baseline_openapi_3.1.yaml b/end_to_end_tests/baseline_openapi_3.1.yaml index a19e46ce3..8cb3e3510 100644 --- a/end_to_end_tests/baseline_openapi_3.1.yaml +++ b/end_to_end_tests/baseline_openapi_3.1.yaml @@ -983,6 +983,20 @@ info: } } }, + "/responses/reference": { + "get": { + "tags": [ + "responses" + ], + "summary": "Endpoint using predefined response", + "operationId": "reference_response", + "responses": { + "200": { + "$ref": "#/components/responses/AResponse" + } + } + } + }, "/auth/token_with_cookie": { "get": { "tags": [ @@ -1141,9 +1155,7 @@ info: }, "/tag_with_number": { "get": { - "tags": [ - "1" - ], + "tags": ["1", "2"], "responses": { "200": { "description": "Success" @@ -2836,55 +2848,6 @@ info: "ModelWithBackslashInDescription": { "type": "object", "description": "Description with special character: \\" - }, - "ModelWithDiscriminatedUnion": { - "type": "object", - "properties": { - "discriminated_union": { - "oneOf": [ - { - "$ref": "#/components/schemas/ADiscriminatedUnion" - }, - { - "type": "null" - } - ], - } - } - }, - "ADiscriminatedUnion": { - "type": "object", - "discriminator": { - "propertyName": "modelType", - "mapping": { - "type1": "#/components/schemas/ADiscriminatedUnionType1", - "type2": "#/components/schemas/ADiscriminatedUnionType2" - } - }, - "oneOf": [ - { - "$ref": "#/components/schemas/ADiscriminatedUnionType1" - }, - { - "$ref": "#/components/schemas/ADiscriminatedUnionType2" - } - ] - }, - "ADiscriminatedUnionType1": { - "type": "object", - "properties": { - "modelType": { - "type": "string" - } - } - }, - "ADiscriminatedUnionType2": { - "type": "object", - "properties": { - "modelType": { - "type": "string" - } - } } } "parameters": { @@ -2962,3 +2925,10 @@ info: "application/json": "schema": "$ref": "#/components/schemas/AModel" + responses: + AResponse: + description: OK + content: + "application/json": + "schema": + "$ref": "#/components/schemas/AModel" diff --git a/end_to_end_tests/config.yml b/end_to_end_tests/config.yml index 64e58439a..a813deddd 100644 --- a/end_to_end_tests/config.yml +++ b/end_to_end_tests/config.yml @@ -11,3 +11,4 @@ class_overrides: field_prefix: attr_ content_type_overrides: openapi/python/client: application/json +generate_all_tags: true diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py index 79a699a1e..d1102fa1a 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py @@ -1,7 +1,5 @@ """Contains methods for accessing the API""" -from typing import Type - from .bodies import BodiesEndpoints from .config import ConfigEndpoints from .default import DefaultEndpoints @@ -13,59 +11,64 @@ from .parameters import ParametersEndpoints from .responses import ResponsesEndpoints from .tag1 import Tag1Endpoints +from .tag2 import Tag2Endpoints from .tests import TestsEndpoints from .true_ import True_Endpoints class MyTestApiClientApi: @classmethod - def bodies(cls) -> Type[BodiesEndpoints]: + def bodies(cls) -> type[BodiesEndpoints]: return BodiesEndpoints @classmethod - def tests(cls) -> Type[TestsEndpoints]: + def tests(cls) -> type[TestsEndpoints]: return TestsEndpoints @classmethod - def defaults(cls) -> Type[DefaultsEndpoints]: + def defaults(cls) -> type[DefaultsEndpoints]: return DefaultsEndpoints @classmethod - def enums(cls) -> Type[EnumsEndpoints]: + def enums(cls) -> type[EnumsEndpoints]: return EnumsEndpoints @classmethod - def responses(cls) -> Type[ResponsesEndpoints]: + def responses(cls) -> type[ResponsesEndpoints]: return ResponsesEndpoints @classmethod - def default(cls) -> Type[DefaultEndpoints]: + def default(cls) -> type[DefaultEndpoints]: return DefaultEndpoints @classmethod - def parameters(cls) -> Type[ParametersEndpoints]: + def parameters(cls) -> type[ParametersEndpoints]: return ParametersEndpoints @classmethod - def tag1(cls) -> Type[Tag1Endpoints]: + def tag1(cls) -> type[Tag1Endpoints]: return Tag1Endpoints @classmethod - def location(cls) -> Type[LocationEndpoints]: + def tag2(cls) -> type[Tag2Endpoints]: + return Tag2Endpoints + + @classmethod + def location(cls) -> type[LocationEndpoints]: return LocationEndpoints @classmethod - def true_(cls) -> Type[True_Endpoints]: + def true_(cls) -> type[True_Endpoints]: return True_Endpoints @classmethod - def naming(cls) -> Type[NamingEndpoints]: + def naming(cls) -> type[NamingEndpoints]: return NamingEndpoints @classmethod - def parameter_references(cls) -> Type[ParameterReferencesEndpoints]: + def parameter_references(cls) -> type[ParameterReferencesEndpoints]: return ParameterReferencesEndpoints @classmethod - def config(cls) -> Type[ConfigEndpoints]: + def config(cls) -> type[ConfigEndpoints]: return ConfigEndpoints diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/responses/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/responses/__init__.py index 6000bd0e7..e09dee3e3 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/responses/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/responses/__init__.py @@ -2,7 +2,7 @@ import types -from . import post_responses_unions_simple_before_complex, text_response +from . import post_responses_unions_simple_before_complex, reference_response, text_response class ResponsesEndpoints: @@ -19,3 +19,10 @@ def text_response(cls) -> types.ModuleType: Text Response """ return text_response + + @classmethod + def reference_response(cls) -> types.ModuleType: + """ + Endpoint using predefined response + """ + return reference_response diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tag2/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tag2/__init__.py new file mode 100644 index 000000000..65edddf25 --- /dev/null +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tag2/__init__.py @@ -0,0 +1,11 @@ +"""Contains methods for accessing the API Endpoints""" + +import types + +from . import get_tag_with_number + + +class Tag2Endpoints: + @classmethod + def get_tag_with_number(cls) -> types.ModuleType: + return get_tag_with_number diff --git a/end_to_end_tests/documents_with_errors/bad-status-code.yaml b/end_to_end_tests/documents_with_errors/bad-status-code.yaml new file mode 100644 index 000000000..17c3ab2cf --- /dev/null +++ b/end_to_end_tests/documents_with_errors/bad-status-code.yaml @@ -0,0 +1,14 @@ +openapi: "3.1.0" +info: + title: "There's something wrong with me" + version: "0.1.0" +paths: + "/": + get: + responses: + "abcdef": + description: "Successful Response" + content: + "application/json": + schema: + const: "Why have a fixed response? I dunno" diff --git a/end_to_end_tests/functional_tests/README.md b/end_to_end_tests/functional_tests/README.md new file mode 100644 index 000000000..1008527c5 --- /dev/null +++ b/end_to_end_tests/functional_tests/README.md @@ -0,0 +1,75 @@ +## The `functional_tests` module + +These are end-to-end tests which run the client generator against many small API documents that are specific to various test cases. + +Rather than testing low-level implementation details (like the unit tests in `tests`), or making assertions about the exact content of the generated code (like the "golden record"-based end-to-end tests), these treat both the generator and the generated code as black boxes and make assertions about their behavior. + +The tests are in two submodules: + +# `generated_code_execution` + +These tests use valid API specs, and after running the generator, they _import and execute_ pieces of the generated code to verify that it actually works at runtime. + +Each test class follows this pattern: + +- Use the decorator `@with_generated_client_fixture`, providing an inline API spec (JSON or YAML) that contains whatever schemas/paths/etc. are relevant to this test class. + - The spec can omit the `openapi:`, `info:`, and `paths:`, blocks, unless those are relevant to the test. + - The decorator creates a temporary file for the inline spec and a temporary directory for the generated code, and runs the client generator. + - It creates a `GeneratedClientContext` object (defined in `end_to_end_test_helpers.py`) to keep track of things like the location of the generated code and the output of the generator command. + - This object is injected into the test class as a fixture called `generated_client`, although most tests will not need to reference the fixture directly. + - `sys.path` is temporarily changed, for the scope of this test class, to allow imports from the generated code. +- Use the decorator `@with_generated_code_imports` or `@with_generated_code_import` to make classes or functions from the generated code available to the tests. + - `@with_generated_code_imports(".models.MyModel1", ".models.MyModel2)` would execute `from [package name].models import MyModel1, MyModel2` and inject the imported classes into the test class as fixtures called `MyModel1` and `MyModel2`. + - `@with_generated_code_import(".api.my_operation.sync", alias="endpoint_method")` would execute `from [package name].api.my_operation import sync`, but the fixture would be named `endpoint_method`. + - After the test class finishes, these imports are discarded. + +Example: + +```python +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + type: object + properties: + stringProp: {"type": "string"} +""") +@with_generated_code_import(".models.MyModel") +class TestSimpleJsonObject: + def test_encoding(self, MyModel): + instance = MyModel(string_prop="abc") + assert instance.to_dict() == {"stringProp": "abc"} +``` + +# `generator_failure_cases` + +These run the generator with an invalid API spec and make assertions about the warning/error output. Some of these invalid conditions are expected to only produce warnings about the affected schemas, while others are expected to produce fatal errors that terminate the generator. + +For warning conditions, each test class uses `@with_generated_client_fixture` as above, then uses `assert_bad_schema` to parse the output and check for a specific warning message for a specific schema name. + +```python +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + # some kind of invalid schema +""") +class TestBadSchema: + def test_encoding(self, generated_client): + assert_bad_schema(generated_client, "MyModel", "some expected warning text") +``` + +Or, for fatal error conditions: + +- Call `inline_spec_should_fail`, providing an inline API spec (JSON or YAML). + +```python +class TestBadSpec: + def test_some_spec_error(self): + result = inline_spec_should_fail(""" +# some kind of invalid spec +""") + assert "some expected error text" in result.output +``` diff --git a/end_to_end_tests/functional_tests/generated_code_execution/test_arrays.py b/end_to_end_tests/functional_tests/generated_code_execution/test_arrays.py new file mode 100644 index 000000000..5cf44e78e --- /dev/null +++ b/end_to_end_tests/functional_tests/generated_code_execution/test_arrays.py @@ -0,0 +1,150 @@ +from typing import Any, ForwardRef, Union + +from end_to_end_tests.functional_tests.helpers import ( + assert_model_decode_encode, + assert_model_property_type_hint, + with_generated_client_fixture, + with_generated_code_imports, +) + + +@with_generated_client_fixture( +""" +components: + schemas: + SimpleObject: + type: object + properties: + name: {"type": "string"} + ModelWithArrayOfAny: + properties: + arrayProp: + type: array + items: {} + ModelWithArrayOfInts: + properties: + arrayProp: + type: array + items: {"type": "integer"} + ModelWithArrayOfObjects: + properties: + arrayProp: + type: array + items: {"$ref": "#/components/schemas/SimpleObject"} +""") +@with_generated_code_imports( + ".models.ModelWithArrayOfAny", + ".models.ModelWithArrayOfInts", + ".models.ModelWithArrayOfObjects", + ".models.SimpleObject", + ".types.Unset", +) +class TestArraySchemas: + def test_array_of_any(self, ModelWithArrayOfAny): + assert_model_decode_encode( + ModelWithArrayOfAny, + {"arrayProp": ["a", 1]}, + ModelWithArrayOfAny(array_prop=["a", 1]), + ) + + def test_array_of_int(self, ModelWithArrayOfInts): + assert_model_decode_encode( + ModelWithArrayOfInts, + {"arrayProp": [1, 2]}, + ModelWithArrayOfInts(array_prop=[1, 2]), + ) + # Note, currently arrays of simple types are not validated, so the following assertion would fail: + # with pytest.raises(TypeError): + # ModelWithArrayOfInt.from_dict({"arrayProp": [1, "a"]}) + + def test_array_of_object(self, ModelWithArrayOfObjects, SimpleObject): + assert_model_decode_encode( + ModelWithArrayOfObjects, + {"arrayProp": [{"name": "a"}, {"name": "b"}]}, + ModelWithArrayOfObjects(array_prop=[SimpleObject(name="a"), SimpleObject(name="b")]), + ) + + def test_type_hints(self, ModelWithArrayOfAny, ModelWithArrayOfInts, ModelWithArrayOfObjects, Unset): + assert_model_property_type_hint(ModelWithArrayOfAny, "array_prop", Union[list[Any], Unset]) + assert_model_property_type_hint(ModelWithArrayOfInts, "array_prop", Union[list[int], Unset]) + assert_model_property_type_hint(ModelWithArrayOfObjects, "array_prop", Union[list["SimpleObject"], Unset]) # type: ignore + + +@with_generated_client_fixture( +""" +components: + schemas: + SimpleObject: + type: object + properties: + name: {"type": "string"} + ModelWithSinglePrefixItem: + type: object + properties: + arrayProp: + type: array + prefixItems: + - type: string + ModelWithPrefixItems: + type: object + properties: + arrayProp: + type: array + prefixItems: + - $ref: "#/components/schemas/SimpleObject" + - type: string + ModelWithMixedItems: + type: object + properties: + arrayProp: + type: array + prefixItems: + - $ref: "#/components/schemas/SimpleObject" + items: + type: string +""") +@with_generated_code_imports( + ".models.ModelWithSinglePrefixItem", + ".models.ModelWithPrefixItems", + ".models.ModelWithMixedItems", + ".models.SimpleObject", + ".types.Unset", +) +class TestArraysWithPrefixItems: + def test_single_prefix_item(self, ModelWithSinglePrefixItem): + assert_model_decode_encode( + ModelWithSinglePrefixItem, + {"arrayProp": ["a"]}, + ModelWithSinglePrefixItem(array_prop=["a"]), + ) + + def test_prefix_items(self, ModelWithPrefixItems, SimpleObject): + assert_model_decode_encode( + ModelWithPrefixItems, + {"arrayProp": [{"name": "a"}, "b"]}, + ModelWithPrefixItems(array_prop=[SimpleObject(name="a"), "b"]), + ) + + def test_prefix_items_and_regular_items(self, ModelWithMixedItems, SimpleObject): + assert_model_decode_encode( + ModelWithMixedItems, + {"arrayProp": [{"name": "a"}, "b"]}, + ModelWithMixedItems(array_prop=[SimpleObject(name="a"), "b"]), + ) + + def test_type_hints(self, ModelWithSinglePrefixItem, ModelWithPrefixItems, ModelWithMixedItems, Unset): + assert_model_property_type_hint(ModelWithSinglePrefixItem, "array_prop", Union[list[str], Unset]) + assert_model_property_type_hint( + ModelWithPrefixItems, + "array_prop", + Union[list[Union[ForwardRef("SimpleObject"), str]], Unset], + ) + assert_model_property_type_hint( + ModelWithMixedItems, + "array_prop", + Union[list[Union[ForwardRef("SimpleObject"), str]], Unset], + ) + # Note, this test is asserting the current behavior which, due to limitations of the implementation + # (see: https://github.com/openapi-generators/openapi-python-client/pull/1130), is not really doing + # tuple type validation-- the ordering of prefixItems is ignored, and instead all of the types are + # simply treated as a union. diff --git a/end_to_end_tests/functional_tests/generated_code_execution/test_defaults.py b/end_to_end_tests/functional_tests/generated_code_execution/test_defaults.py new file mode 100644 index 000000000..8f63df3fa --- /dev/null +++ b/end_to_end_tests/functional_tests/generated_code_execution/test_defaults.py @@ -0,0 +1,146 @@ +import datetime +import uuid + +from end_to_end_tests.functional_tests.helpers import ( + with_generated_client_fixture, + with_generated_code_imports, +) + + +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + type: object + properties: + booleanProp: {"type": "boolean", "default": true} + stringProp: {"type": "string", "default": "a"} + numberProp: {"type": "number", "default": 1.5} + intProp: {"type": "integer", "default": 2} + dateProp: {"type": "string", "format": "date", "default": "2024-01-02"} + dateTimeProp: {"type": "string", "format": "date-time", "default": "2024-01-02T03:04:05Z"} + uuidProp: {"type": "string", "format": "uuid", "default": "07EF8B4D-AA09-4FFA-898D-C710796AFF41"} + anyPropWithString: {"default": "b"} + anyPropWithInt: {"default": 3} + booleanWithStringTrue1: {"type": "boolean", "default": "True"} + booleanWithStringTrue2: {"type": "boolean", "default": "true"} + booleanWithStringFalse1: {"type": "boolean", "default": "False"} + booleanWithStringFalse2: {"type": "boolean", "default": "false"} + intWithStringValue: {"type": "integer", "default": "4"} + numberWithIntValue: {"type": "number", "default": 5} + numberWithStringValue: {"type": "number", "default": "5.5"} + stringWithNumberValue: {"type": "string", "default": 6} + stringConst: {"type": "string", "const": "always", "default": "always"} + unionWithValidDefaultForType1: + anyOf: [{"type": "boolean"}, {"type": "integer"}] + default: true + unionWithValidDefaultForType2: + anyOf: [{"type": "boolean"}, {"type": "integer"}] + default: 3 +""") +@with_generated_code_imports(".models.MyModel") +class TestSimpleDefaults: + # Note, the null/None type is not covered here due to a known bug: + # https://github.com/openapi-generators/openapi-python-client/issues/1162 + def test_defaults_in_initializer(self, MyModel): + instance = MyModel() + assert instance == MyModel( + boolean_prop=True, + string_prop="a", + number_prop=1.5, + int_prop=2, + date_prop=datetime.date(2024, 1, 2), + date_time_prop=datetime.datetime(2024, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc), + uuid_prop=uuid.UUID("07EF8B4D-AA09-4FFA-898D-C710796AFF41"), + any_prop_with_string="b", + any_prop_with_int=3, + boolean_with_string_true_1=True, + boolean_with_string_true_2=True, + boolean_with_string_false_1=False, + boolean_with_string_false_2=False, + int_with_string_value=4, + number_with_int_value=5, + number_with_string_value=5.5, + string_with_number_value="6", + string_const="always", + union_with_valid_default_for_type_1=True, + union_with_valid_default_for_type_2=3, + ) + + + +@with_generated_client_fixture( +""" +components: + schemas: + MyEnum: + type: string + enum: ["a", "b"] + MyModel: + type: object + properties: + enumProp: + allOf: + - $ref: "#/components/schemas/MyEnum" + default: "a" + +""") +@with_generated_code_imports(".models.MyEnum", ".models.MyModel") +class TestEnumDefaults: + def test_enum_default(self, MyEnum, MyModel): + assert MyModel().enum_prop == MyEnum.A + + +@with_generated_client_fixture( +""" +components: + schemas: + MyEnum: + type: string + enum: ["a", "A"] + MyModel: + properties: + enumProp: + allOf: + - $ref: "#/components/schemas/MyEnum" + default: A +""", + config="literal_enums: true", +) +@with_generated_code_imports(".models.MyModel") +class TestLiteralEnumDefaults: + def test_default_value(self, MyModel): + assert MyModel().enum_prop == "A" + + +@with_generated_client_fixture( +""" +# Test the ability to specify a default value for a union type as long as that value is +# supported by at least one of the variants + +components: + schemas: + MyModel: + type: object + properties: + simpleTypeProp1: + type: ["integer", "boolean", "string"] + default: 3 + simpleTypeProp2: + type: ["integer", "boolean", "string"] + default: true + simpleTypeProp3: + type: ["integer", "boolean", "string"] + default: abc +""" +) +@with_generated_code_imports(".models.MyModel") +class TestUnionDefaults: + def test_simple_type(self, MyModel): + instance = MyModel() + assert instance == MyModel( + simple_type_prop_1=3, + simple_type_prop_2=True, + simple_type_prop_3="abc", + ) diff --git a/end_to_end_tests/functional_tests/generated_code_execution/test_docstrings.py b/end_to_end_tests/functional_tests/generated_code_execution/test_docstrings.py new file mode 100644 index 000000000..54eea955a --- /dev/null +++ b/end_to_end_tests/functional_tests/generated_code_execution/test_docstrings.py @@ -0,0 +1,204 @@ +import re +from typing import Any, List + +from end_to_end_tests.functional_tests.helpers import ( + with_generated_code_import, + with_generated_client_fixture, +) +from end_to_end_tests.generated_client import GeneratedClientContext + + +class DocstringParser: + lines: List[str] + + def __init__(self, item: Any): + self.lines = [line.lstrip() for line in item.__doc__.split("\n")] + + def get_section(self, header_line: str) -> List[str]: + lines = self.lines[self.lines.index(header_line)+1:] + return lines[0:lines.index("")] + + +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + description: I like this type. + type: object + properties: + reqStr: + type: string + description: This is necessary. + optStr: + type: string + description: This isn't necessary. + undescribedProp: + type: string + required: ["reqStr", "undescribedProp"] +""") +@with_generated_code_import(".models.MyModel") +class TestSchemaDocstringsDefaultBehavior: + def test_model_description(self, MyModel): + assert DocstringParser(MyModel).lines[0] == "I like this type." + + def test_model_properties_in_model_description(self, MyModel): + assert set(DocstringParser(MyModel).get_section("Attributes:")) == { + "req_str (str): This is necessary.", + "opt_str (Union[Unset, str]): This isn't necessary.", + "undescribed_prop (str):", + } + + +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + description: I like this type. + type: object + properties: + prop1: + type: string + description: This attribute has a description + prop2: + type: string # no description for this one + required: ["prop1", "prop2"] +""", +config="docstrings_on_attributes: true", +) +@with_generated_code_import(".models.MyModel") +class TestSchemaWithDocstringsOnAttributesOption: + def test_model_description_is_entire_docstring(self, MyModel): + assert MyModel.__doc__.strip() == "I like this type." + + def test_attrs_have_docstrings(self, generated_client: GeneratedClientContext): + # A docstring that appears after an attribute is *not* stored in __doc__ anywhere + # by the interpreter, so we can't inspect it that way-- but it's still valid for it + # to appear there, and it will be recognized by documentation tools. So we'll assert + # that these strings appear in the source code. The code should look like this: + # class MyModel: + # """I like this type.""" + # prop1: str + # """This attribute has a description""" + # prop2: str + # + source_file_path = generated_client.output_path / generated_client.base_module / "models" / "my_model.py" + content = source_file_path.read_text() + assert re.search('\n *prop1: *str\n *""" *This attribute has a description *"""\n', content) + assert re.search('\n *prop2: *str\n *[^"]', content) + + +@with_generated_client_fixture( +""" +tags: + - name: service1 +paths: + "/simple": + get: + operationId: getSimpleThing + description: Get a simple thing. + responses: + "200": + description: Success! + content: + application/json: + schema: + $ref: "#/components/schemas/GoodResponse" + tags: + - service1 + post: + operationId: postSimpleThing + description: Post a simple thing. + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Thing" + responses: + "200": + description: Success! + content: + application/json: + schema: + $ref: "#/components/schemas/GoodResponse" + "400": + description: Failure!! + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + tags: + - service1 + "/simple/{id}/{index}": + get: + operationId: getAttributeByIndex + description: Get a simple thing's attribute. + parameters: + - name: id + in: path + required: true + schema: + type: string + description: Which one. + - name: index + in: path + required: true + schema: + type: integer + - name: fries + in: query + required: false + schema: + type: boolean + description: Do you want fries with that? + responses: + "200": + description: Success! + content: + application/json: + schema: + $ref: "#/components/schemas/GoodResponse" + tags: + - service1 + +components: + schemas: + GoodResponse: + type: object + ErrorResponse: + type: object + Thing: + type: object + description: The thing. +""") +@with_generated_code_import(".api.service1.get_simple_thing.sync", alias="get_simple_thing_sync") +@with_generated_code_import(".api.service1.post_simple_thing.sync", alias="post_simple_thing_sync") +@with_generated_code_import(".api.service1.get_attribute_by_index.sync", alias="get_attribute_by_index_sync") +class TestEndpointDocstrings: + def test_description(self, get_simple_thing_sync): + assert DocstringParser(get_simple_thing_sync).lines[0] == "Get a simple thing." + + def test_response_single_type(self, get_simple_thing_sync): + assert DocstringParser(get_simple_thing_sync).get_section("Returns:") == [ + "GoodResponse", + ] + + def test_response_union_type(self, post_simple_thing_sync): + returns_line = DocstringParser(post_simple_thing_sync).get_section("Returns:")[0] + assert returns_line in ( + "Union[GoodResponse, ErrorResponse]", + "Union[ErrorResponse, GoodResponse]", + ) + + def test_request_body(self, post_simple_thing_sync): + assert DocstringParser(post_simple_thing_sync).get_section("Args:") == [ + "body (Thing): The thing." + ] + + def test_params(self, get_attribute_by_index_sync): + assert DocstringParser(get_attribute_by_index_sync).get_section("Args:") == [ + "id (str): Which one.", + "index (int):", + "fries (Union[Unset, bool]): Do you want fries with that?", + ] diff --git a/end_to_end_tests/functional_tests/generated_code_execution/test_enums_and_consts.py b/end_to_end_tests/functional_tests/generated_code_execution/test_enums_and_consts.py new file mode 100644 index 000000000..f04665f78 --- /dev/null +++ b/end_to_end_tests/functional_tests/generated_code_execution/test_enums_and_consts.py @@ -0,0 +1,319 @@ +from typing import Literal, Union +import pytest + +from end_to_end_tests.functional_tests.helpers import ( + assert_model_decode_encode, + assert_model_property_type_hint, + with_generated_client_fixture, + with_generated_code_imports, +) + + +@with_generated_client_fixture( +""" +components: + schemas: + MyEnum: + type: string + enum: ["a", "B", "a23", "123", "1bc", "a Thing WIth spaces", ""] + MyModel: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + inlineEnumProp: + type: string + enum: ["a", "b"] + MyModelWithRequired: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + required: ["enumProp"] +""") +@with_generated_code_imports( + ".models.MyEnum", + ".models.MyModel", + ".models.MyModelInlineEnumProp", + ".models.MyModelWithRequired", + ".types.Unset", +) +class TestStringEnumClass: + @pytest.mark.parametrize( + "expected_name,expected_value", + [ + ("A", "a"), + ("B", "B"), + ("A23", "a23"), + ("VALUE_3", "123"), + ("VALUE_4", "1bc"), + ("A_THING_WITH_SPACES", "a Thing WIth spaces"), + ("VALUE_6", ""), + ], + ) + def test_enum_values(self, MyEnum, expected_name, expected_value): + assert getattr(MyEnum, expected_name) == MyEnum(expected_value) + + def test_enum_prop_in_object(self, MyEnum, MyModel, MyModelInlineEnumProp): + assert_model_decode_encode(MyModel, {"enumProp": "B"}, MyModel(enum_prop=MyEnum.B)) + assert_model_decode_encode( + MyModel, + {"inlineEnumProp": "a"}, + MyModel(inline_enum_prop=MyModelInlineEnumProp.A), + ) + + def test_type_hints(self, MyModel, MyModelWithRequired, MyEnum, Unset): + optional_type = Union[Unset, MyEnum] + assert_model_property_type_hint(MyModel,"enum_prop", optional_type) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", MyEnum) + + def test_invalid_values(self, MyModel): + with pytest.raises(ValueError): + MyModel.from_dict({"enumProp": "c"}) + with pytest.raises(ValueError): + MyModel.from_dict({"enumProp": "A"}) + with pytest.raises(ValueError): + MyModel.from_dict({"enumProp": 2}) + + +@with_generated_client_fixture( +""" +components: + schemas: + MyEnum: + type: integer + enum: [2, 3, -4] + MyModel: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + inlineEnumProp: + type: string + enum: [2, 3] + MyModelWithRequired: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + required: ["enumProp"] +""") +@with_generated_code_imports( + ".models.MyEnum", + ".models.MyModel", + ".models.MyModelInlineEnumProp", + ".models.MyModelWithRequired", + ".types.Unset", +) +class TestIntEnumClass: + @pytest.mark.parametrize( + "expected_name,expected_value", + [ + ("VALUE_2", 2), + ("VALUE_3", 3), + ("VALUE_NEGATIVE_4", -4), + ], + ) + def test_enum_values(self, MyEnum, expected_name, expected_value): + assert getattr(MyEnum, expected_name) == MyEnum(expected_value) + + def test_enum_prop_in_object(self, MyEnum, MyModel, MyModelInlineEnumProp): + assert_model_decode_encode(MyModel, {"enumProp": 2}, MyModel(enum_prop=MyEnum.VALUE_2)) + assert_model_decode_encode( + MyModel, + {"inlineEnumProp": 2}, + MyModel(inline_enum_prop=MyModelInlineEnumProp.VALUE_2), + ) + + def test_type_hints(self, MyModel, MyModelWithRequired, MyEnum, Unset): + optional_type = Union[Unset, MyEnum] + assert_model_property_type_hint(MyModel,"enum_prop", optional_type) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", MyEnum) + + def test_invalid_values(self, MyModel): + with pytest.raises(ValueError): + MyModel.from_dict({"enumProp": 5}) + with pytest.raises(ValueError): + MyModel.from_dict({"enumProp": "a"}) + + +@with_generated_client_fixture( +""" +components: + schemas: + EnumOfNullOnly: + enum: [null] + MyModel: + properties: + nullOnlyEnumProp: {"$ref": "#/components/schemas/EnumOfNullOnly"} + required: ["nullOnlyEnumProp"] +""") +@with_generated_code_imports( + ".models.MyModel", +) +class TestSingleValueNullEnum: + def test_enum_of_null_only(self, MyModel): + assert_model_decode_encode(MyModel, {"nullOnlyEnumProp": None}, MyModel(null_only_enum_prop=None)) + + def test_type_hints(self, MyModel): + assert_model_property_type_hint(MyModel, "null_only_enum_prop", None) + + +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + properties: + mustBeErnest: + const: Ernest + mustBeThirty: + const: 30 +""", +) +@with_generated_code_imports(".models.MyModel") +class TestConst: + def test_valid_string(self, MyModel): + assert_model_decode_encode( + MyModel, + {"mustBeErnest": "Ernest"}, + MyModel(must_be_ernest="Ernest"), + ) + + def test_valid_int(self, MyModel): + assert_model_decode_encode( + MyModel, + {"mustBeThirty": 30}, + MyModel(must_be_thirty=30), + ) + + def test_invalid_string(self, MyModel): + with pytest.raises(ValueError): + MyModel.from_dict({"mustBeErnest": "Jack"}) + + def test_invalid_int(self, MyModel): + with pytest.raises(ValueError): + MyModel.from_dict({"mustBeThirty": 29}) + + +# The following tests of literal enums use basically the same specs as the tests above, but +# the "literal_enums" option is enabled in the test configuration. + +@with_generated_client_fixture( +""" +# Tests of literal_enums mode, where enums become a typing.Literal type instead of a class + +components: + schemas: + MyEnum: + type: string + enum: ["a", "A", "b"] + MyModel: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + inlineEnumProp: + type: string + enum: ["a", "b"] + MyModelWithRequired: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + required: ["enumProp"] +""", + config="literal_enums: true", +) +@with_generated_code_imports( + ".models.MyModel", + ".models.MyModelWithRequired", + ".types.Unset", +) +class TestStringLiteralEnum: + def test_enum_prop(self, MyModel): + assert_model_decode_encode(MyModel, {"enumProp": "a"}, MyModel(enum_prop="a")) + assert_model_decode_encode(MyModel, {"enumProp": "A"}, MyModel(enum_prop="A")) + assert_model_decode_encode(MyModel, {"inlineEnumProp": "a"}, MyModel(inline_enum_prop="a")) + + def test_type_hints(self, MyModel, MyModelWithRequired, Unset): + literal_type = Literal["a", "A", "b"] + optional_type = Union[Unset, literal_type] + assert_model_property_type_hint(MyModel, "enum_prop", optional_type) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", literal_type) + + def test_invalid_values(self, MyModel): + with pytest.raises(TypeError): + MyModel.from_dict({"enumProp": "c"}) + with pytest.raises(TypeError): + MyModel.from_dict({"enumProp": 2}) + + +@with_generated_client_fixture( +""" +# Tests of literal_enums mode, where enums become a typing.Literal type instead of a class + +components: + schemas: + MyEnum: + type: integer + enum: [2, 3, -4] + MyModel: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + inlineEnumProp: + type: string + enum: [2, 3] + MyModelWithRequired: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + required: ["enumProp"] +""", + config="literal_enums: true", +) +@with_generated_code_imports( + ".models.MyModel", + ".models.MyModelWithRequired", + ".types.Unset", +) +class TestIntLiteralEnum: + def test_enum_prop(self, MyModel): + assert_model_decode_encode(MyModel, {"enumProp": 2}, MyModel(enum_prop=2)) + assert_model_decode_encode(MyModel, {"enumProp": -4}, MyModel(enum_prop=-4)) + assert_model_decode_encode(MyModel, {"inlineEnumProp": 2}, MyModel(inline_enum_prop=2)) + + def test_type_hints(self, MyModel, MyModelWithRequired, Unset): + literal_type = Literal[2, 3, -4] + optional_type = Union[Unset, literal_type] + assert_model_property_type_hint(MyModel, "enum_prop", optional_type) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", literal_type) + + def test_invalid_values(self, MyModel): + with pytest.raises(TypeError): + MyModel.from_dict({"enumProp": 4}) + with pytest.raises(TypeError): + MyModel.from_dict({"enumProp": "a"}) + + +@with_generated_client_fixture( +""" +# Similar to some of the "union with null" tests in test_unions.py, but in literal_enums mode + +components: + schemas: + MyEnum: + type: string + enum: ["a", "A"] + MyEnumIncludingNull: + type: ["string", "null"] + enum: ["a", "b", null] + MyNullOnlyEnum: + enum: [null] + MyModel: + properties: + enumProp: {"$ref": "#/components/schemas/MyEnum"} + nullableEnumProp: + oneOf: + - {"$ref": "#/components/schemas/MyEnum"} + - type: "null" + enumIncludingNullProp: {"$ref": "#/components/schemas/MyEnumIncludingNull"} + nullOnlyEnumProp: {"$ref": "#/components/schemas/MyNullOnlyEnum"} +""", + config="literal_enums: true", +) +@with_generated_code_imports(".models.MyModel") +class TestNullableLiteralEnum: + def test_nullable_enum_prop(self, MyModel): + assert_model_decode_encode(MyModel, {"nullableEnumProp": "B"}, MyModel(nullable_enum_prop="B")) + assert_model_decode_encode(MyModel, {"nullableEnumProp": None}, MyModel(nullable_enum_prop=None)) + assert_model_decode_encode(MyModel, {"enumIncludingNullProp": "a"}, MyModel(enum_including_null_prop="a")) + assert_model_decode_encode(MyModel, {"enumIncludingNullProp": None}, MyModel(enum_including_null_prop=None)) + assert_model_decode_encode(MyModel, {"nullOnlyEnumProp": None}, MyModel(null_only_enum_prop=None)) diff --git a/end_to_end_tests/functional_tests/generated_code_execution/test_properties.py b/end_to_end_tests/functional_tests/generated_code_execution/test_properties.py new file mode 100644 index 000000000..e1cfce9a5 --- /dev/null +++ b/end_to_end_tests/functional_tests/generated_code_execution/test_properties.py @@ -0,0 +1,186 @@ +import datetime +from typing import Any, ForwardRef, Union +import uuid +import pytest + +from end_to_end_tests.functional_tests.helpers import ( + assert_model_decode_encode, + assert_model_property_type_hint, + with_generated_client_fixture, + with_generated_code_imports, +) + + +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + type: object + properties: + req1: {"type": "string"} + req2: {"type": "string"} + opt: {"type": "string"} + required: ["req1", "req2"] + DerivedModel: + allOf: + - $ref: "#/components/schemas/MyModel" + - type: object + properties: + req3: {"type": "string"} + required: ["req3"] +""") +@with_generated_code_imports( + ".models.MyModel", + ".models.DerivedModel", + ".types.Unset", +) +class TestRequiredAndOptionalProperties: + def test_required_ok(self, MyModel, DerivedModel): + assert_model_decode_encode( + MyModel, + {"req1": "a", "req2": "b"}, + MyModel(req1="a", req2="b"), + ) + assert_model_decode_encode( + DerivedModel, + {"req1": "a", "req2": "b", "req3": "c"}, + DerivedModel(req1="a", req2="b", req3="c"), + ) + + def test_required_and_optional(self, MyModel, DerivedModel): + assert_model_decode_encode( + MyModel, + {"req1": "a", "req2": "b", "opt": "c"}, + MyModel(req1="a", req2="b", opt="c"), + ) + assert_model_decode_encode( + DerivedModel, + {"req1": "a", "req2": "b", "req3": "c", "opt": "d"}, + DerivedModel(req1="a", req2="b", req3="c", opt="d"), + ) + + def test_required_missing(self, MyModel, DerivedModel): + with pytest.raises(KeyError): + MyModel.from_dict({"req1": "a"}) + with pytest.raises(KeyError): + MyModel.from_dict({"req2": "b"}) + with pytest.raises(KeyError): + DerivedModel.from_dict({"req1": "a", "req2": "b"}) + + def test_type_hints(self, MyModel, Unset): + assert_model_property_type_hint(MyModel, "req1", str) + assert_model_property_type_hint(MyModel, "opt", Union[str, Unset]) + + +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + type: object + properties: + booleanProp: {"type": "boolean"} + stringProp: {"type": "string"} + numberProp: {"type": "number"} + intProp: {"type": "integer"} + anyObjectProp: {"$ref": "#/components/schemas/AnyObject"} + nullProp: {"type": "null"} + anyProp: {} + AnyObject: + type: object +""") +@with_generated_code_imports( + ".models.MyModel", + ".models.AnyObject", + ".types.Unset", +) +class TestBasicModelProperties: + def test_decode_encode(self, MyModel, AnyObject): + json_data = { + "booleanProp": True, + "stringProp": "a", + "numberProp": 1.5, + "intProp": 2, + "anyObjectProp": {"d": 3}, + "nullProp": None, + "anyProp": "e" + } + expected_any_object = AnyObject() + expected_any_object.additional_properties = {"d": 3} + assert_model_decode_encode( + MyModel, + json_data, + MyModel( + boolean_prop=True, + string_prop="a", + number_prop=1.5, + int_prop=2, + any_object_prop = expected_any_object, + null_prop=None, + any_prop="e", + ) + ) + + @pytest.mark.parametrize( + "bad_data", + ["a", True, 2, None], + ) + def test_decode_error_not_object(self, bad_data, MyModel): + with pytest.raises(Exception): + # Exception is overly broad, but unfortunately in the current implementation, the error + # being raised is AttributeError (because it tries to call bad_data.copy()) which isn't + # very meaningful + MyModel.from_dict(bad_data) + + def test_type_hints(self, MyModel, Unset): + assert_model_property_type_hint(MyModel, "boolean_prop", Union[bool, Unset]) + assert_model_property_type_hint(MyModel, "string_prop", Union[str, Unset]) + assert_model_property_type_hint(MyModel, "number_prop", Union[float, Unset]) + assert_model_property_type_hint(MyModel, "int_prop", Union[int, Unset]) + assert_model_property_type_hint(MyModel, "any_object_prop", Union[ForwardRef("AnyObject"), Unset]) + assert_model_property_type_hint(MyModel, "null_prop", Union[None, Unset]) + assert_model_property_type_hint(MyModel, "any_prop", Union[Any, Unset]) + + +@with_generated_client_fixture( +""" +components: + schemas: + MyModel: + type: object + properties: + dateProp: {"type": "string", "format": "date"} + dateTimeProp: {"type": "string", "format": "date-time"} + uuidProp: {"type": "string", "format": "uuid"} + unknownFormatProp: {"type": "string", "format": "weird"} +""") +@with_generated_code_imports( + ".models.MyModel", + ".types.Unset", +) +class TestSpecialStringFormats: + def test_date(self, MyModel): + date_value = datetime.date.today() + json_data = {"dateProp": date_value.isoformat()} + assert_model_decode_encode(MyModel, json_data, MyModel(date_prop=date_value)) + + def test_date_time(self, MyModel): + date_time_value = datetime.datetime.now(datetime.timezone.utc) + json_data = {"dateTimeProp": date_time_value.isoformat()} + assert_model_decode_encode(MyModel, json_data, MyModel(date_time_prop=date_time_value)) + + def test_uuid(self, MyModel): + uuid_value = uuid.uuid1() + json_data = {"uuidProp": str(uuid_value)} + assert_model_decode_encode(MyModel, json_data, MyModel(uuid_prop=uuid_value)) + + def test_unknown_format(self, MyModel): + json_data = {"unknownFormatProp": "whatever"} + assert_model_decode_encode(MyModel, json_data, MyModel(unknown_format_prop="whatever")) + + def test_type_hints(self, MyModel, Unset): + assert_model_property_type_hint(MyModel, "date_prop", Union[datetime.date, Unset]) + assert_model_property_type_hint(MyModel, "date_time_prop", Union[datetime.datetime, Unset]) + assert_model_property_type_hint(MyModel, "uuid_prop", Union[uuid.UUID, Unset]) + assert_model_property_type_hint(MyModel, "unknown_format_prop", Union[str, Unset]) diff --git a/end_to_end_tests/functional_tests/generated_code_execution/test_unions.py b/end_to_end_tests/functional_tests/generated_code_execution/test_unions.py new file mode 100644 index 000000000..6326ae582 --- /dev/null +++ b/end_to_end_tests/functional_tests/generated_code_execution/test_unions.py @@ -0,0 +1,508 @@ +from typing import ForwardRef, Union + +import pytest + +from end_to_end_tests.functional_tests.helpers import ( + assert_model_decode_encode, + assert_model_property_type_hint, + with_generated_client_fixture, + with_generated_code_imports, +) + + +@with_generated_client_fixture( +""" +# Various use cases for oneOf + +components: + schemas: + ThingA: + type: object + properties: + propA: { type: "string" } + required: ["propA"] + ThingB: + type: object + properties: + propB: { type: "string" } + required: ["propB"] + ThingAOrB: + oneOf: + - $ref: "#/components/schemas/ThingA" + - $ref: "#/components/schemas/ThingB" + ModelWithUnion: + type: object + properties: + thing: {"$ref": "#/components/schemas/ThingAOrB"} + thingOrString: + oneOf: + - $ref: "#/components/schemas/ThingA" + - type: string + ModelWithRequiredUnion: + type: object + properties: + thing: {"$ref": "#/components/schemas/ThingAOrB"} + required: ["thing"] + ModelWithNestedUnion: + type: object + properties: + thingOrValue: + oneOf: + - "$ref": "#/components/schemas/ThingAOrB" + - oneOf: + - type: string + - type: number + ModelWithUnionOfOne: + type: object + properties: + thing: + oneOf: + - $ref: "#/components/schemas/ThingA" + requiredThing: + oneOf: + - $ref: "#/components/schemas/ThingA" + required: ["requiredThing"] +""") +@with_generated_code_imports( + ".models.ThingA", + ".models.ThingB", + ".models.ModelWithUnion", + ".models.ModelWithRequiredUnion", + ".models.ModelWithNestedUnion", + ".models.ModelWithUnionOfOne", + ".types.Unset" +) +class TestOneOf: + def test_disambiguate_objects_via_required_properties(self, ThingA, ThingB, ModelWithUnion): + assert_model_decode_encode( + ModelWithUnion, + {"thing": {"propA": "x"}}, + ModelWithUnion(thing=ThingA(prop_a="x")), + ) + assert_model_decode_encode( + ModelWithUnion, + {"thing": {"propB": "x"}}, + ModelWithUnion(thing=ThingB(prop_b="x")), + ) + + def test_disambiguate_object_and_non_object(self, ThingA, ModelWithUnion): + assert_model_decode_encode( + ModelWithUnion, + {"thingOrString": {"propA": "x"}}, + ModelWithUnion(thing_or_string=ThingA(prop_a="x")), + ) + assert_model_decode_encode( + ModelWithUnion, + {"thingOrString": "x"}, + ModelWithUnion(thing_or_string="x"), + ) + + def test_disambiguate_nested_union(self, ThingA, ThingB, ModelWithNestedUnion): + assert_model_decode_encode( + ModelWithNestedUnion, + {"thingOrValue": {"propA": "x"}}, + ModelWithNestedUnion(thing_or_value=ThingA(prop_a="x")), + ) + assert_model_decode_encode( + ModelWithNestedUnion, + {"thingOrValue": 3}, + ModelWithNestedUnion(thing_or_value=3), + ) + + def test_type_hints(self, ModelWithUnion, ModelWithRequiredUnion, ModelWithUnionOfOne, ThingA, Unset): + assert_model_property_type_hint( + ModelWithUnion, + "thing", + Union[ForwardRef("ThingA"), ForwardRef("ThingB"), Unset], + ) + assert_model_property_type_hint( + ModelWithRequiredUnion, + "thing", + Union[ForwardRef("ThingA"), ForwardRef("ThingB")], + ) + assert_model_property_type_hint( + ModelWithUnionOfOne, "thing", Union[ForwardRef("ThingA"), Unset] + ) + assert_model_property_type_hint( + ModelWithUnionOfOne, "required_thing", "ThingA" + ) + + +@with_generated_client_fixture( +""" +# Various use cases for a oneOf where one of the variants is null, since these are handled +# a bit differently in the generator + +components: + schemas: + MyEnum: + type: string + enum: ["a", "b"] + MyObject: + type: object + properties: + name: + type: string + MyModel: + properties: + nullableEnumProp: + oneOf: + - {"$ref": "#/components/schemas/MyEnum"} + - type: "null" + nullableObjectProp: + oneOf: + - {"$ref": "#/components/schemas/MyObject"} + - type: "null" + inlineNullableObject: + # Note, the generated class for this should be called "MyModelInlineNullableObject", + # since the generator's rule for inline schemas that require their own class is to + # concatenate the property name to the parent schema name. + oneOf: + - type: object + properties: + name: + type: string + - type: "null" +""") +@with_generated_code_imports( + ".models.MyEnum", + ".models.MyObject", + ".models.MyModel", + ".models.MyModelInlineNullableObject", + ".types.Unset", +) +class TestUnionsWithNull: + def test_nullable_enum_prop(self, MyModel, MyEnum): + assert_model_decode_encode(MyModel, {"nullableEnumProp": "b"}, MyModel(nullable_enum_prop=MyEnum.B)) + assert_model_decode_encode(MyModel, {"nullableEnumProp": None}, MyModel(nullable_enum_prop=None)) + + def test_nullable_object_prop(self, MyModel, MyObject): + assert_model_decode_encode( MyModel, {"nullableObjectProp": None}, MyModel(nullable_object_prop=None)) + assert_model_decode_encode( MyModel, {"nullableObjectProp": None}, MyModel(nullable_object_prop=None)) + + def test_nullable_object_prop_with_inline_schema(self, MyModel, MyModelInlineNullableObject): + assert_model_decode_encode( + MyModel, + {"inlineNullableObject": {"name": "a"}}, + MyModel(inline_nullable_object=MyModelInlineNullableObject(name="a")), + ) + assert_model_decode_encode( MyModel, {"inlineNullableObject": None}, MyModel(inline_nullable_object=None)) + + def test_type_hints(self, MyModel, MyEnum, Unset): + assert_model_property_type_hint(MyModel, "nullable_enum_prop", Union[MyEnum, None, Unset]) + assert_model_property_type_hint(MyModel, "nullable_object_prop", Union[ForwardRef("MyObject"), None, Unset]) + assert_model_property_type_hint( + MyModel, + "inline_nullable_object", + Union[ForwardRef("MyModelInlineNullableObject"), None, Unset], + ) + + +@with_generated_client_fixture( +""" +# Tests for combining the OpenAPI 3.0 "nullable" attribute with an enum + +openapi: 3.0.0 + +components: + schemas: + MyEnum: + type: string + enum: ["a", "b"] + MyEnumIncludingNull: + type: string + nullable: true + enum: ["a", "b", null] + MyModel: + properties: + nullableEnumProp: + allOf: + - {"$ref": "#/components/schemas/MyEnum"} + nullable: true + enumIncludingNullProp: {"$ref": "#/components/schemas/MyEnumIncludingNull"} +""") +@with_generated_code_imports( + ".models.MyEnum", + ".models.MyEnumIncludingNull", + ".models.MyModel", + ".types.Unset", +) +class TestNullableEnumsInOpenAPI30: + def test_nullable_enum_prop(self, MyModel, MyEnum, MyEnumIncludingNull): + assert_model_decode_encode(MyModel, {"nullableEnumProp": "b"}, MyModel(nullable_enum_prop=MyEnum.B)) + assert_model_decode_encode(MyModel, {"nullableEnumProp": None}, MyModel(nullable_enum_prop=None)) + assert_model_decode_encode( + MyModel, + {"enumIncludingNullProp": "a"}, + MyModel(enum_including_null_prop=MyEnumIncludingNull.A), + ) + assert_model_decode_encode( MyModel, {"enumIncludingNullProp": None}, MyModel(enum_including_null_prop=None)) + + def test_type_hints(self, MyModel, MyEnum, MyEnumIncludingNull, Unset): + assert_model_property_type_hint(MyModel, "nullable_enum_prop", Union[MyEnum, None, Unset]) + assert_model_property_type_hint(MyModel, "enum_including_null_prop", Union[MyEnumIncludingNull, None, Unset]) + + +@with_generated_client_fixture( +""" +# Tests for using a discriminator property + +components: + schemas: + ModelType1: + type: object + properties: + modelType: {"type": "string"} + name: {"type": "string"} + required: ["modelType"] + ModelType2: + type: object + properties: + modelType: {"type": "string"} + name: {"type": "string"} + required: ["modelType"] + Corgi: + type: object + properties: + modelType: {"type": "string"} + dogType: {"type": "string"} + name: {"type": "string"} + required: ["modelType"] + Schnauzer: + type: object + properties: + modelType: {"type": "string"} + dogType: {"type": "string"} + name: {"type": "string"} + required: ["modelType"] + WithDiscriminatedUnion: + type: object + properties: + unionProp: + oneOf: + - $ref: "#/components/schemas/ModelType1" + - $ref: "#/components/schemas/ModelType2" + discriminator: + propertyName: modelType + mapping: + "type1": "#/components/schemas/ModelType1" + "type2": "ModelType2" # exactly equivalent to "#/components/schemas/ModelType2" + "another-value": "#/components/schemas/ModelType2" # deliberately mapped to same type as previous line + WithDiscriminatedUnionImplicitMapping: + type: object + properties: + unionProp: + oneOf: + - $ref: "#/components/schemas/ModelType1" + - $ref: "#/components/schemas/ModelType2" + discriminator: + propertyName: modelType + WithNestedDiscriminatorsSameProperty: + type: object + properties: + unionProp: + oneOf: + - oneOf: + - $ref: "#/components/schemas/ModelType1" + - $ref: "#/components/schemas/ModelType2" + discriminator: + propertyName: modelType + - oneOf: + - $ref: "#/components/schemas/Corgi" + - $ref: "#/components/schemas/Schnauzer" + discriminator: + propertyName: modelType + WithNestedDiscriminatorsDifferentProperty: + type: object + properties: + unionProp: + oneOf: + - oneOf: + - $ref: "#/components/schemas/ModelType1" + - $ref: "#/components/schemas/ModelType2" + discriminator: + propertyName: modelType + - oneOf: + - $ref: "#/components/schemas/Corgi" + - $ref: "#/components/schemas/Schnauzer" + discriminator: + propertyName: dogType +""") +@with_generated_code_imports( + ".models.ModelType1", + ".models.ModelType2", + ".models.Corgi", + ".models.Schnauzer", + ".models.WithDiscriminatedUnion", + ".models.WithDiscriminatedUnionImplicitMapping", + ".models.WithNestedDiscriminatorsSameProperty", + ".models.WithNestedDiscriminatorsDifferentProperty", +) +class TestDiscriminators: + def test_with_explicit_mapping(self, ModelType1, ModelType2, WithDiscriminatedUnion): + assert_model_decode_encode( + WithDiscriminatedUnion, + {"unionProp": {"modelType": "type1", "name": "a"}}, + WithDiscriminatedUnion(union_prop=ModelType1(model_type="type1", name="a")), + ) + assert_model_decode_encode( + WithDiscriminatedUnion, + {"unionProp": {"modelType": "type2", "name": "a"}}, + WithDiscriminatedUnion(union_prop=ModelType2(model_type="type2", name="a")), + ) + assert_model_decode_encode( + WithDiscriminatedUnion, + {"unionProp": {"modelType": "another-value", "name": "a"}}, + WithDiscriminatedUnion(union_prop=ModelType2(model_type="another-value", name="a")), + ) + with pytest.raises(TypeError): + WithDiscriminatedUnion.from_dict({"unionProp": {"modelType": "ModelType1"}}) + with pytest.raises(TypeError): + WithDiscriminatedUnion.from_dict({"unionProp": {"modelType": "unknown-value"}}) + + def test_with_implicit_mapping(self, ModelType1, ModelType2, WithDiscriminatedUnionImplicitMapping): + assert_model_decode_encode( + WithDiscriminatedUnionImplicitMapping, + {"unionProp": {"modelType": "ModelType1", "name": "a"}}, + WithDiscriminatedUnionImplicitMapping(union_prop=ModelType1(model_type="ModelType1", name="a")), + ) + assert_model_decode_encode( + WithDiscriminatedUnionImplicitMapping, + {"unionProp": {"modelType": "ModelType2", "name": "a"}}, + WithDiscriminatedUnionImplicitMapping(union_prop=ModelType2(model_type="ModelType2", name="a")), + ) + with pytest.raises(TypeError): + WithDiscriminatedUnionImplicitMapping.from_dict({"unionProp": {"modelType": "unknown-value"}}) + + def test_nested_with_same_property(self, ModelType1, Schnauzer, WithNestedDiscriminatorsSameProperty): + assert_model_decode_encode( + WithNestedDiscriminatorsSameProperty, + {"unionProp": {"modelType": "ModelType1", "name": "a"}}, + WithNestedDiscriminatorsSameProperty(union_prop=ModelType1(model_type="ModelType1", name="a")), + ) + assert_model_decode_encode( + WithNestedDiscriminatorsSameProperty, + {"unionProp": {"modelType": "Schnauzer", "name": "a"}}, + WithNestedDiscriminatorsSameProperty(union_prop=Schnauzer(model_type="Schnauzer", name="a")), + ) + + def test_nested_with_different_property(self, ModelType1, Schnauzer, WithNestedDiscriminatorsDifferentProperty): + assert_model_decode_encode( + WithNestedDiscriminatorsDifferentProperty, + {"unionProp": {"modelType": "ModelType1", "name": "a"}}, + WithNestedDiscriminatorsDifferentProperty(union_prop=ModelType1(model_type="ModelType1", name="a")), + ) + assert_model_decode_encode( + WithNestedDiscriminatorsDifferentProperty, + {"unionProp": {"modelType": "irrelevant", "dogType": "Schnauzer", "name": "a"}}, + WithNestedDiscriminatorsDifferentProperty(union_prop=Schnauzer(model_type="irrelevant", dog_type="Schnauzer", name="a")), + ) + + +@with_generated_client_fixture( +""" +# Tests for using multiple values of "type:" in one schema (OpenAPI 3.1) + +components: + schemas: + StringOrInt: + type: ["string", "integer"] + MyModel: + type: object + properties: + stringOrIntProp: + type: ["string", "integer"] +""" +) +@with_generated_code_imports( + ".models.MyModel", + ".types.Unset" +) +class TestListOfSimpleTypes: + def test_decode_encode(self, MyModel): + assert_model_decode_encode(MyModel, {"stringOrIntProp": "a"}, MyModel(string_or_int_prop="a")) + assert_model_decode_encode(MyModel, {"stringOrIntProp": 1}, MyModel(string_or_int_prop=1)) + + def test_type_hints(self, MyModel, Unset): + assert_model_property_type_hint(MyModel, "string_or_int_prop", Union[str, int, Unset]) + + +@with_generated_client_fixture( +""" +# Test cases where there's a union of types *and* an explicit list of multiple "type:"s - +# there was a bug where this could cause enum/model classes to be generated incorrectly + +components: + schemas: + MyStringEnum: + type: string + enum: ["a", "b"] + MyIntEnum: + type: integer + enum: [1, 2] + MyEnumIncludingNull: + type: ["string", "null"] + enum: ["a", "b", null] + MyObject: + type: object + properties: + name: + type: string + MyModel: + properties: + enumsWithListOfTypesProp: + type: ["string", "integer"] + oneOf: + - {"$ref": "#/components/schemas/MyStringEnum"} + - {"$ref": "#/components/schemas/MyIntEnum"} + enumIncludingNullProp: {"$ref": "#/components/schemas/MyEnumIncludingNull"} + nullableObjectWithListOfTypesProp: + type: ["string", "object"] + oneOf: + - {"$ref": "#/components/schemas/MyObject"} + - type: "null" +""") +@with_generated_code_imports( + ".models.MyStringEnum", + ".models.MyIntEnum", + ".models.MyEnumIncludingNull", + ".models.MyObject", + ".models.MyModel", + ".types.Unset", +) +class TestUnionsWithListOfSimpleTypes: + def test_union_of_enums(self, MyModel, MyStringEnum, MyIntEnum): + assert_model_decode_encode( + MyModel, + {"enumsWithListOfTypesProp": "b"}, + MyModel(enums_with_list_of_types_prop=MyStringEnum.B), + ) + assert_model_decode_encode( + MyModel, + {"enumsWithListOfTypesProp": 2}, + MyModel(enums_with_list_of_types_prop=MyIntEnum.VALUE_2), + ) + + def test_union_of_enum_with_null(self, MyModel, MyEnumIncludingNull): + assert_model_decode_encode( + MyModel, + {"enumIncludingNullProp": "b"}, + MyModel(enum_including_null_prop=MyEnumIncludingNull.B), + ) + assert_model_decode_encode( + MyModel, + {"enumIncludingNullProp": None}, + MyModel(enum_including_null_prop=None), + ) + + def test_nullable_object_with_list_of_types(self, MyModel, MyObject): + assert_model_decode_encode( + MyModel, + {"nullableObjectWithListOfTypesProp": {"name": "a"}}, + MyModel(nullable_object_with_list_of_types_prop=MyObject(name="a")), + ) + assert_model_decode_encode( + MyModel, + {"nullableObjectWithListOfTypesProp": None}, + MyModel(nullable_object_with_list_of_types_prop=None), + ) diff --git a/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_arrays.py b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_arrays.py new file mode 100644 index 000000000..e4ef0cffd --- /dev/null +++ b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_arrays.py @@ -0,0 +1,23 @@ +import pytest + +from end_to_end_tests.functional_tests.helpers import assert_bad_schema, with_generated_client_fixture + + +@with_generated_client_fixture( +""" +components: + schemas: + ArrayWithNoItems: + type: array + ArrayWithInvalidItemsRef: + type: array + items: + $ref: "#/components/schemas/DoesntExist" +""" +) +class TestArrayInvalidSchemas: + def test_no_items(self, generated_client): + assert_bad_schema(generated_client, "ArrayWithNoItems", "must have items or prefixItems defined") + + def test_invalid_items_ref(self, generated_client): + assert_bad_schema(generated_client, "ArrayWithInvalidItemsRef", "invalid data in items of array") diff --git a/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_defaults.py b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_defaults.py new file mode 100644 index 000000000..93f5e11d4 --- /dev/null +++ b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_defaults.py @@ -0,0 +1,88 @@ +import pytest + +from end_to_end_tests.functional_tests.helpers import assert_bad_schema, with_generated_client_fixture + + +@with_generated_client_fixture( +""" +components: + schemas: + WithBadBoolean: + properties: + badBoolean: {"type": "boolean", "default": "not a boolean"} + WithBadIntAsString: + properties: + badInt: {"type": "integer", "default": "not an int"} + WithBadIntAsOther: + properties: + badInt: {"type": "integer", "default": true} + WithBadFloatAsString: + properties: + badInt: {"type": "number", "default": "not a number"} + WithBadFloatAsOther: + properties: + badInt: {"type": "number", "default": true} + WithBadDateAsString: + properties: + badDate: {"type": "string", "format": "date", "default": "xxx"} + WithBadDateAsOther: + properties: + badDate: {"type": "string", "format": "date", "default": 3} + WithBadDateTimeAsString: + properties: + badDate: {"type": "string", "format": "date-time", "default": "xxx"} + WithBadDateTimeAsOther: + properties: + badDate: {"type": "string", "format": "date-time", "default": 3} + WithBadUuidAsString: + properties: + badUuid: {"type": "string", "format": "uuid", "default": "xxx"} + WithBadUuidAsOther: + properties: + badUuid: {"type": "string", "format": "uuid", "default": 3} + WithBadEnum: + properties: + badEnum: {"type": "string", "enum": ["a", "b"], "default": "x"} + GoodEnum: + type: string + enum: ["a", "b"] + OverriddenEnumWithBadDefault: + properties: + badEnum: + allOf: + - $ref: "#/components/schemas/GoodEnum" + default: "x" + UnionWithNoValidDefault: + properties: + badBoolOrInt: + anyOf: + - type: boolean + - type: integer + default: "xxx" +""" +) +class TestInvalidDefaultValues: + # Note, the null/None type, and binary strings (files), are not covered here due to a known bug: + # https://github.com/openapi-generators/openapi-python-client/issues/1162 + + @pytest.mark.parametrize( + ("model_name", "message"), + [ + ("WithBadBoolean", "Invalid boolean value"), + ("WithBadIntAsString", "Invalid int value"), + ("WithBadIntAsOther", "Invalid int value"), + ("WithBadFloatAsString", "Invalid float value"), + ("WithBadFloatAsOther", "Cannot convert True to a float"), + ("WithBadDateAsString", "Invalid date"), + ("WithBadDateAsOther", "Cannot convert 3 to a date"), + ("WithBadDateTimeAsString", "Invalid datetime"), + ("WithBadDateTimeAsOther", "Cannot convert 3 to a datetime"), + ("WithBadUuidAsString", "Invalid UUID value"), + ("WithBadUuidAsOther", "Invalid UUID value"), + ("WithBadEnum", "Value x is not valid for enum"), + ("OverriddenEnumWithBadDefault", "Value x is not valid for enum"), + ("UnionWithNoValidDefault", "Invalid int value"), + ] + ) + def test_bad_default_warning(self, model_name, message, generated_client): + assert_bad_schema(generated_client, model_name, message) diff --git a/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_enums_and_consts.py b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_enums_and_consts.py new file mode 100644 index 000000000..7f1586f29 --- /dev/null +++ b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_enums_and_consts.py @@ -0,0 +1,128 @@ +from end_to_end_tests.functional_tests.helpers import ( + assert_bad_schema, + inline_spec_should_fail, + with_generated_client_fixture, +) + + +@with_generated_client_fixture( +""" +components: + schemas: + WithBadDefaultValue: + enum: ["A"] + default: "B" + WithBadDefaultType: + enum: ["A"] + default: 123 + WithMixedTypes: + enum: ["A", 1] + WithUnsupportedType: + enum: [1.4, 1.5] + DefaultNotMatchingConst: + const: "aaa" + default: "bbb" + WithConflictingInlineNames: + type: object + properties: + "12": + enum: ["a", "b"] + WithConflictingInlineNames1: + type: object + properties: + "2": + enum: ["c", "d"] +""" +) +class TestEnumAndConstInvalidSchemas: + def test_enum_bad_default_value(self, generated_client): + assert_bad_schema(generated_client, "WithBadDefaultValue", "Value B is not valid") + + def test_enum_bad_default_type(self, generated_client): + assert_bad_schema(generated_client, "WithBadDefaultType", "Cannot convert 123 to enum") + + def test_enum_mixed_types(self, generated_client): + assert_bad_schema(generated_client, "WithMixedTypes", "Enum values must all be the same type") + + def test_enum_unsupported_type(self, generated_client): + assert_bad_schema(generated_client, "WithUnsupportedType", "Unsupported enum type") + + def test_const_default_not_matching(self, generated_client): + assert_bad_schema(generated_client, "DefaultNotMatchingConst", "Invalid value for const") + + def test_conflicting_inline_class_names(self, generated_client): + assert "Found conflicting enums named WithConflictingInlineNames12 with incompatible values" in generated_client.generator_result.output + + def test_enum_duplicate_values(self): + # This one currently causes a full generator failure rather than a warning + result = inline_spec_should_fail( +""" +components: + schemas: + WithDuplicateValues: + enum: ["x", "x"] +""" + ) + assert "Duplicate key X in enum" in str(result.exception) + + +@with_generated_client_fixture( +""" +components: + schemas: + WithBadDefaultValue: + enum: ["A"] + default: "B" + WithBadDefaultType: + enum: ["A"] + default: 123 + WithMixedTypes: + enum: ["A", 1] + WithUnsupportedType: + enum: [1.4, 1.5] + DefaultNotMatchingConst: + const: "aaa" + default: "bbb" + WithConflictingInlineNames: + type: object + properties: + "12": + enum: ["a", "b"] + WithConflictingInlineNames1: + type: object + properties: + "2": + enum: ["c", "d"] +""", + config="literal_enums: true", +) +class TestLiteralEnumInvalidSchemas: + def test_literal_enum_bad_default_value(self, generated_client): + assert_bad_schema(generated_client, "WithBadDefaultValue", "Value B is not valid") + + def test_literal_enum_bad_default_type(self, generated_client): + assert_bad_schema(generated_client, "WithBadDefaultType", "Cannot convert 123 to enum") + + def test_literal_enum_mixed_types(self, generated_client): + assert_bad_schema(generated_client, "WithMixedTypes", "Enum values must all be the same type") + + def test_literal_enum_unsupported_type(self, generated_client): + assert_bad_schema(generated_client, "WithUnsupportedType", "Unsupported enum type") + + def test_const_default_not_matching(self, generated_client): + assert_bad_schema(generated_client, "DefaultNotMatchingConst", "Invalid value for const") + + def test_conflicting_inline_literal_enum_names(self, generated_client): + assert "Found conflicting enums named WithConflictingInlineNames12 with incompatible values" in generated_client.generator_result.output + + def test_literal_enum_duplicate_values(self): + # This one currently causes a full generator failure rather than a warning + result = inline_spec_should_fail( +""" +components: + schemas: + WithDuplicateValues: + enum: ["x", "x"] +""" + ) + assert "Duplicate key X in enum" in str(result.exception) diff --git a/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_spec_format.py b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_spec_format.py new file mode 100644 index 000000000..2b0dfdda9 --- /dev/null +++ b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_spec_format.py @@ -0,0 +1,86 @@ +import pytest +from end_to_end_tests.functional_tests.helpers import ( + inline_spec_should_fail, +) + + +class TestInvalidSpecFormats: + @pytest.mark.parametrize( + ("filename_suffix", "content", "expected_error"), + ( + (".yaml", "not a valid openapi document", "Failed to parse OpenAPI document"), + (".json", "Invalid JSON", "Invalid JSON"), + (".yaml", "{", "Invalid YAML"), + ), + ids=("invalid_openapi", "invalid_json", "invalid_yaml"), + ) + def test_unparseable_file(self, filename_suffix, content, expected_error): + result = inline_spec_should_fail(content, filename_suffix=filename_suffix, add_missing_sections=False) + assert expected_error in result.output + + def test_missing_openapi_version(self): + result = inline_spec_should_fail( +""" +info: + title: My API + version: "1.0" +paths: {} +""", + add_missing_sections=False, + ) + for text in ["Failed to parse OpenAPI document", "1 validation error", "openapi"]: + assert text in result.output + + def test_missing_title(self): + result = inline_spec_should_fail( +""" +info: + version: "1.0" +openapi: "3.1.0" +paths: {} +""", + add_missing_sections=False, + ) + for text in ["Failed to parse OpenAPI document", "1 validation error", "title"]: + assert text in result.output + + def test_missing_version(self): + result = inline_spec_should_fail( +""" +info: + title: My API +openapi: "3.1.0" +paths: {} +""", + add_missing_sections=False, + ) + for text in ["Failed to parse OpenAPI document", "1 validation error", "version"]: + assert text in result.output + + def test_missing_paths(self): + result = inline_spec_should_fail( +""" +info: + title: My API + version: "1.0" +openapi: "3.1.0" +""", + add_missing_sections=False, + ) + for text in ["Failed to parse OpenAPI document", "1 validation error", "paths"]: + assert text in result.output + + def test_swagger_unsupported(self): + result = inline_spec_should_fail( +""" +swagger: "2.0" +info: + title: My API + version: "1.0" +openapi: "3.1" +paths: {} +components: {} +""", + add_missing_sections=False, + ) + assert "You may be trying to use a Swagger document; this is not supported by this project." in result.output diff --git a/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_unions.py b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_unions.py new file mode 100644 index 000000000..a3786d275 --- /dev/null +++ b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_unions.py @@ -0,0 +1,133 @@ +from end_to_end_tests.functional_tests.helpers import assert_bad_schema, with_generated_client_fixture + + +@with_generated_client_fixture( +""" +components: + schemas: + UnionWithInvalidReference: + anyOf: + - $ref: "#/components/schemas/DoesntExist" + UnionWithInvalidDefault: + type: ["number", "integer"] + default: aaa + UnionWithMalformedVariant: + anyOf: + - type: string + - type: array # invalid because no items +""" +) +class TestUnionInvalidSchemas: + def test_invalid_reference(self, generated_client): + assert_bad_schema(generated_client, "UnionWithInvalidReference", "Could not find reference") + + def test_invalid_default(self, generated_client): + assert_bad_schema(generated_client, "UnionWithInvalidDefault", "Invalid int value: aaa") + + def test_invalid_default(self, generated_client): + assert_bad_schema(generated_client, "UnionWithInvalidDefault", "Invalid int value: aaa") + + def test_invalid_property(self, generated_client): + assert_bad_schema(generated_client, "UnionWithMalformedVariant", "Invalid property in union") + + +@with_generated_client_fixture( +""" +components: + schemas: + ModelType1: + type: object + properties: + modelType: {"type": "string"} + name: {"type": "string"} + required: ["modelType"] + ModelType2: + type: object + properties: + modelType: {"type": "string"} + name: {"type": "string"} + required: ["modelType"] + ModelType3: + type: object + properties: + modelType: {"type": "string"} + name: {"type": "string"} + required: ["modelType"] + StringType: + type: string + WithUnknownSchemaInMapping: + type: object + properties: + unionProp: + oneOf: + - $ref: "#/components/schemas/ModelType1" + - $ref: "#/components/schemas/ModelType2" + discriminator: + propertyName: modelType + mapping: + "type1": "#/components/schemas/ModelType1" + "type2": "#/components/schemas/DoesntExist" + WithReferenceToSchemaNotInUnion: + type: object + properties: + unionProp: + oneOf: + - $ref: "#/components/schemas/ModelType1" + - $ref: "#/components/schemas/ModelType2" + discriminator: + propertyName: modelType + mapping: + "type1": "#/components/schemas/ModelType1" + "type2": "#/components/schemas/ModelType2" + "type3": "#/components/schemas/ModelType3" + WithNonObjectVariant: + type: object + properties: + unionProp: + oneOf: + - $ref: "#/components/schemas/ModelType1" + - $ref: "#/components/schemas/StringType" + discriminator: + propertyName: modelType + WithInlineSchema: + type: object + properties: + unionProp: + oneOf: + - $ref: "#/components/schemas/ModelType1" + - type: object + properties: + modelType: {"type": "string"} + name: {"type": "string"} + discriminator: + propertyName: modelType +""" +) +class TestInvalidDiscriminators: + def test_invalid_reference(self, generated_client): + assert_bad_schema( + generated_client, + "WithUnknownSchemaInMapping", + 'Invalid reference "#/components/schemas/DoesntExist" in discriminator mapping', + ) + + def test_reference_to_schema_not_in_union(self, generated_client): + assert_bad_schema( + generated_client, + "WithReferenceToSchemaNotInUnion", + 'Discriminator mapping referred to "ModelType3" which is not one of the schema variants', + ) + + def test_non_object_variant(self, generated_client): + assert_bad_schema( + generated_client, + "WithNonObjectVariant", + "All schema variants must be objects when using a discriminator", + ) + + def test_inline_schema(self, generated_client): + assert_bad_schema( + generated_client, + "WithInlineSchema", + "Inline schema declarations are not allowed when using a discriminator", + ) diff --git a/end_to_end_tests/functional_tests/helpers.py b/end_to_end_tests/functional_tests/helpers.py new file mode 100644 index 000000000..aadd5ede6 --- /dev/null +++ b/end_to_end_tests/functional_tests/helpers.py @@ -0,0 +1,135 @@ +from typing import Any, Dict +import re +from typing import List, Optional + +from click.testing import Result +import pytest + +from end_to_end_tests.generated_client import generate_client_from_inline_spec, GeneratedClientContext + + +def with_generated_client_fixture( + openapi_spec: str, + name: str="generated_client", + config: str="", + extra_args: List[str] = [], +): + """Decorator to apply to a test class to create a fixture inside it called 'generated_client'. + + The fixture value will be a GeneratedClientContext created by calling + generate_client_from_inline_spec(). + """ + def _decorator(cls): + def generated_client(self): + with generate_client_from_inline_spec(openapi_spec, extra_args=extra_args, config=config) as g: + print(g.generator_result.stdout) # so we'll see the output if a test failed + yield g + + setattr(cls, name, pytest.fixture(scope="class")(generated_client)) + return cls + + return _decorator + + +def with_generated_code_import(import_path: str, alias: Optional[str] = None): + """Decorator to apply to a test class to create a fixture from a generated code import. + + The 'generated_client' fixture must also be present. + + If import_path is "a.b.c", then the fixture's value is equal to "from a.b import c", and + its name is "c" unless you specify a different name with the alias parameter. + """ + parts = import_path.split(".") + module_name = ".".join(parts[0:-1]) + import_name = parts[-1] + + def _decorator(cls): + nonlocal alias + + def _func(self, generated_client): + return generated_client.import_symbol(module_name, import_name) + + alias = alias or import_name + _func.__name__ = alias + setattr(cls, alias, pytest.fixture(scope="class")(_func)) + return cls + + return _decorator + + +def with_generated_code_imports(*import_paths: str): + def _decorator(cls): + decorated = cls + for import_path in import_paths: + decorated = with_generated_code_import(import_path)(decorated) + return decorated + + return _decorator + + +def assert_model_decode_encode(model_class: Any, json_data: dict, expected_instance: Any) -> None: + instance = model_class.from_dict(json_data) + assert instance == expected_instance + assert instance.to_dict() == json_data + + +def assert_model_property_type_hint(model_class: Any, name: str, expected_type_hint: Any) -> None: + assert model_class.__annotations__[name] == expected_type_hint + + +def inline_spec_should_fail( + openapi_spec: str, + extra_args: List[str] = [], + config: str = "", + filename_suffix: str = "", + add_missing_sections = True, +) -> Result: + """Asserts that the generator could not process the spec. + + Returns the command result, which could include stdout data or an exception. + """ + with generate_client_from_inline_spec( + openapi_spec, + extra_args, + config, + filename_suffix=filename_suffix, + add_missing_sections=add_missing_sections, + raise_on_error=False, + ) as generated_client: + assert generated_client.generator_result.exit_code != 0 + return generated_client.generator_result + + +def assert_bad_schema( + generated_client: GeneratedClientContext, + schema_name: str, + expected_message_str: str, +) -> None: + warnings = _GeneratorWarningsParser(generated_client) + assert schema_name in warnings.by_schema, f"Did not find warning for schema {schema_name} in output: {warnings.output}" + assert expected_message_str in warnings.by_schema[schema_name] + + +class _GeneratorWarningsParser: + output: str + by_schema: Dict[str, str] + + def __init__(self, generated_client: GeneratedClientContext) -> None: + """Runs the generator, asserts that it printed warnings, and parses the warnings.""" + + assert generated_client.generator_result.exit_code == 0 + output = generated_client.generator_result.stdout + assert "Warning(s) encountered while generating" in output + self.by_schema = {} + self.output = output + bad_schema_regex = "Unable to (parse|process) schema /components/schemas/(\w*)" + last_name = "" + while True: + if not (match := re.search(bad_schema_regex, output)): + break + if last_name: + self.by_schema[last_name] = output[0:match.start()] + output = output[match.end():] + last_name = match.group(2) + if last_name: + self.by_schema[last_name] = output diff --git a/end_to_end_tests/generated_client.py b/end_to_end_tests/generated_client.py new file mode 100644 index 000000000..8007e6384 --- /dev/null +++ b/end_to_end_tests/generated_client.py @@ -0,0 +1,156 @@ +import importlib +import os +import re +import shutil +from pathlib import Path +import sys +import tempfile +from typing import Any, List, Optional, Set + +from attrs import define +import pytest +from click.testing import Result +from typer.testing import CliRunner + +from openapi_python_client.cli import app + + +@define +class GeneratedClientContext: + """A context manager with helpers for tests that run against generated client code. + + On entering this context, sys.path is changed to include the root directory of the + generated code, so its modules can be imported. On exit, the original sys.path is + restored, and any modules that were loaded within the context are removed. + """ + + output_path: Path + generator_result: Result + base_module: str + monkeypatch: pytest.MonkeyPatch + old_modules: Optional[Set[str]] = None + + def __enter__(self) -> "GeneratedClientContext": + self.monkeypatch.syspath_prepend(self.output_path) + self.old_modules = set(sys.modules.keys()) + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.monkeypatch.undo() + for module_name in set(sys.modules.keys()) - self.old_modules: + del sys.modules[module_name] + shutil.rmtree(self.output_path, ignore_errors=True) + + def import_module(self, module_path: str) -> Any: + """Attempt to import a module from the generated code.""" + return importlib.import_module(f"{self.base_module}{module_path}") + + def import_symbol(self, module_path: str, name: str) -> Any: + module = self.import_module(module_path) + try: + return getattr(module, name) + except AttributeError: + existing = ", ".join(name for name in dir(module) if not name.startswith("_")) + assert False, ( + f"Couldn't find import \"{name}\" in \"{self.base_module}{module_path}\".\n" + f"Available imports in that module are: {existing}\n" + f"Output from generator was: {self.generator_result.stdout}" + ) + +def _run_command( + command: str, + extra_args: Optional[List[str]] = None, + openapi_document: Optional[str] = None, + url: Optional[str] = None, + config_path: Optional[Path] = None, + raise_on_error: bool = True, +) -> Result: + """Generate a client from an OpenAPI document and return the result of the command.""" + runner = CliRunner() + if openapi_document is not None: + openapi_path = Path(__file__).parent / openapi_document + source_arg = f"--path={openapi_path}" + else: + source_arg = f"--url={url}" + config_path = config_path or (Path(__file__).parent / "config.yml") + args = [command, f"--config={config_path}", source_arg] + if extra_args: + args.extend(extra_args) + result = runner.invoke(app, args) + if result.exit_code != 0 and raise_on_error: + message = f"{result.stdout}\n{result.exception}" if result.exception else result.stdout + raise Exception(message) + return result + + +def generate_client( + openapi_document: str, + extra_args: List[str] = [], + output_path: str = "my-test-api-client", + base_module: str = "my_test_api_client", + specify_output_path_explicitly: bool = True, + overwrite: bool = True, + raise_on_error: bool = True, +) -> GeneratedClientContext: + """Run the generator and return a GeneratedClientContext for accessing the generated code.""" + full_output_path = Path.cwd() / output_path + if not overwrite: + shutil.rmtree(full_output_path, ignore_errors=True) + args = extra_args + if specify_output_path_explicitly: + args = [*args, "--output-path", str(full_output_path)] + if overwrite: + args = [*args, "--overwrite"] + generator_result = _run_command("generate", args, openapi_document, raise_on_error=raise_on_error) + return GeneratedClientContext( + full_output_path, + generator_result, + base_module, + pytest.MonkeyPatch(), + ) + + +def generate_client_from_inline_spec( + openapi_spec: str, + extra_args: List[str] = [], + config: str = "", + filename_suffix: Optional[str] = None, + base_module: str = "testapi_client", + add_missing_sections = True, + raise_on_error: bool = True, +) -> GeneratedClientContext: + """Run the generator on a temporary file created with the specified contents. + + You can also optionally tell it to create a temporary config file. + """ + if add_missing_sections: + if not re.search("^openapi:", openapi_spec, re.MULTILINE): + openapi_spec += "\nopenapi: '3.1.0'\n" + if not re.search("^info:", openapi_spec, re.MULTILINE): + openapi_spec += "\ninfo: {'title': 'testapi', 'description': 'my test api', 'version': '0.0.1'}\n" + if not re.search("^paths:", openapi_spec, re.MULTILINE): + openapi_spec += "\npaths: {}\n" + + output_path = tempfile.mkdtemp() + file = tempfile.NamedTemporaryFile(suffix=filename_suffix, delete=False) + file.write(openapi_spec.encode('utf-8')) + file.close() + + if config: + config_file = tempfile.NamedTemporaryFile(delete=False) + config_file.write(config.encode('utf-8')) + config_file.close() + extra_args = [*extra_args, "--config", config_file.name] + + generated_client = generate_client( + file.name, + extra_args, + output_path, + base_module, + raise_on_error=raise_on_error, + ) + os.unlink(file.name) + if config: + os.unlink(config_file.name) + + return generated_client diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/bodies/json_like.py b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/json_like.py index 646279c98..626bacfa6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/bodies/json_like.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/json_like.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,10 +12,10 @@ def _get_kwargs( *, body: JsonLikeBody, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/bodies/json-like", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/bodies/post_bodies_multiple.py b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/post_bodies_multiple.py index 136cd3b5b..84ec8eee0 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/bodies/post_bodies_multiple.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/post_bodies_multiple.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -19,10 +19,10 @@ def _get_kwargs( PostBodiesMultipleDataBody, PostBodiesMultipleFilesBody, ], -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/bodies/multiple", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/bodies/refs.py b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/refs.py index 20eb25160..a79cf178a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/bodies/refs.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/refs.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,10 +12,10 @@ def _get_kwargs( *, body: AModel, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/bodies/refs", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/config/content_type_override.py b/end_to_end_tests/golden-record/my_test_api_client/api/config/content_type_override.py index 29d4ed7c3..2bd74aac4 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/config/content_type_override.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/config/content_type_override.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -11,10 +11,10 @@ def _get_kwargs( *, body: str, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/config/content-type-override", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py index 7bf48a0f8..7de222f55 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,14 +11,14 @@ def _get_kwargs( *, common: Union[Unset, str] = UNSET, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["common"] = common params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/common_parameters", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_allof.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_allof.py index 1d2fa73a8..9d837acd6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_allof.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_allof.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -9,8 +9,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/models/allof", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_oneof_with_required_const.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_oneof_with_required_const.py index 85be98c28..85f68fb7c 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_oneof_with_required_const.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_oneof_with_required_const.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -14,8 +14,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/models/oneof-with-required-const", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py index 8fc836ad3..5bd941c69 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,14 +11,14 @@ def _get_kwargs( *, common: Union[Unset, str] = UNSET, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["common"] = common params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/common_parameters", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/reserved_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/reserved_parameters.py index ec2f3fc3c..fe7adf04c 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/default/reserved_parameters.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/reserved_parameters.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,8 +12,8 @@ def _get_kwargs( *, client_query: str, url_query: str, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["client"] = client_query @@ -21,7 +21,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/naming/reserved-parameters", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py index 12ac54947..ffc9b535e 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py @@ -1,6 +1,6 @@ import datetime from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union import httpx from dateutil.parser import isoparse @@ -22,14 +22,14 @@ def _get_kwargs( float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, - list_prop: List[AnEnum], + list_prop: list[AnEnum], union_prop: Union[float, str] = "not a float", union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: AnEnum, model_prop: "ModelWithUnionProperty", required_model_prop: "ModelWithUnionProperty", -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["string_prop"] = string_prop @@ -77,7 +77,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/defaults", "params": params, @@ -123,7 +123,7 @@ def sync_detailed( float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, - list_prop: List[AnEnum], + list_prop: list[AnEnum], union_prop: Union[float, str] = "not a float", union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: AnEnum, @@ -140,7 +140,7 @@ def sync_detailed( float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. - list_prop (List[AnEnum]): + list_prop (list[AnEnum]): union_prop (Union[float, str]): Default: 'not a float'. union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used @@ -188,7 +188,7 @@ def sync( float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, - list_prop: List[AnEnum], + list_prop: list[AnEnum], union_prop: Union[float, str] = "not a float", union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: AnEnum, @@ -205,7 +205,7 @@ def sync( float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. - list_prop (List[AnEnum]): + list_prop (list[AnEnum]): union_prop (Union[float, str]): Default: 'not a float'. union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used @@ -248,7 +248,7 @@ async def asyncio_detailed( float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, - list_prop: List[AnEnum], + list_prop: list[AnEnum], union_prop: Union[float, str] = "not a float", union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: AnEnum, @@ -265,7 +265,7 @@ async def asyncio_detailed( float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. - list_prop (List[AnEnum]): + list_prop (list[AnEnum]): union_prop (Union[float, str]): Default: 'not a float'. union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used @@ -311,7 +311,7 @@ async def asyncio( float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, - list_prop: List[AnEnum], + list_prop: list[AnEnum], union_prop: Union[float, str] = "not a float", union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: AnEnum, @@ -328,7 +328,7 @@ async def asyncio( float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. - list_prop (List[AnEnum]): + list_prop (list[AnEnum]): union_prop (Union[float, str]): Default: 'not a float'. union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/enums/bool_enum_tests_bool_enum_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/enums/bool_enum_tests_bool_enum_post.py index 851cdf385..52385855c 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/enums/bool_enum_tests_bool_enum_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/enums/bool_enum_tests_bool_enum_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,14 +11,14 @@ def _get_kwargs( *, bool_enum: bool, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["bool_enum"] = bool_enum params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/enum/bool", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/enums/int_enum_tests_int_enum_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/enums/int_enum_tests_int_enum_post.py index f7c403771..26c3729fe 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/enums/int_enum_tests_int_enum_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/enums/int_enum_tests_int_enum_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,15 +12,15 @@ def _get_kwargs( *, int_enum: AnIntEnum, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} json_int_enum = int_enum.value params["int_enum"] = json_int_enum params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/enum/int", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py index 140bb3780..ad9428a72 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -18,8 +18,8 @@ def _get_kwargs( integer_header: Union[Unset, int] = UNSET, int_enum_header: Union[Unset, GetLocationHeaderTypesIntEnumHeader] = UNSET, string_enum_header: Union[Unset, GetLocationHeaderTypesStringEnumHeader] = UNSET, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} if not isinstance(boolean_header, Unset): headers["Boolean-Header"] = "true" if boolean_header else "false" @@ -38,7 +38,7 @@ def _get_kwargs( if not isinstance(string_enum_header, Unset): headers["String-Enum-Header"] = str(string_enum_header) - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/location/header/types", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py index fcbc8213a..e28e37a36 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py @@ -1,6 +1,6 @@ import datetime from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -15,8 +15,8 @@ def _get_kwargs( null_required: Union[None, datetime.datetime], null_not_required: Union[None, Unset, datetime.datetime] = UNSET, not_null_not_required: Union[Unset, datetime.datetime] = UNSET, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} json_not_null_required = not_null_required.isoformat() params["not_null_required"] = json_not_null_required @@ -44,7 +44,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/location/query/optionality", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/naming/hyphen_in_path.py b/end_to_end_tests/golden-record/my_test_api_client/api/naming/hyphen_in_path.py index a94062b1b..a0caba2d6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/naming/hyphen_in_path.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/naming/hyphen_in_path.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -10,8 +10,8 @@ def _get_kwargs( hyphen_in_path: str, -) -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +) -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": f"/naming/{hyphen_in_path}", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/naming/mixed_case.py b/end_to_end_tests/golden-record/my_test_api_client/api/naming/mixed_case.py index ece16e492..7df2d318f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/naming/mixed_case.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/naming/mixed_case.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -13,8 +13,8 @@ def _get_kwargs( *, mixed_case: str, mixedCase: str, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["mixed_case"] = mixed_case @@ -22,7 +22,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/naming/mixed-case", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/naming/post_naming_property_conflict_with_import.py b/end_to_end_tests/golden-record/my_test_api_client/api/naming/post_naming_property_conflict_with_import.py index 083bdd12d..3bb8b698b 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/naming/post_naming_property_conflict_with_import.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/naming/post_naming_property_conflict_with_import.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -15,10 +15,10 @@ def _get_kwargs( *, body: PostNamingPropertyConflictWithImportBody, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/naming/property-conflict-with-import", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py index 3d8c6ad36..e7a8e2712 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -15,8 +15,8 @@ def _get_kwargs( integer_param: Union[Unset, int] = 0, header_param: Union[None, Unset, str] = UNSET, cookie_param: Union[Unset, str] = UNSET, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} if not isinstance(header_param, Unset): headers["header param"] = header_param @@ -24,7 +24,7 @@ def _get_kwargs( if cookie_param is not UNSET: cookies["cookie param"] = cookie_param - params: Dict[str, Any] = {} + params: dict[str, Any] = {} params["string param"] = string_param @@ -32,7 +32,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": f"/parameter-references/{path_param}", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py index b842b8834..704996107 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,14 +12,14 @@ def _get_kwargs( param_path: str, *, param_query: Union[Unset, str] = UNSET, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["param"] = param_query params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "delete", "url": f"/common_parameters_overriding/{param_path}", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py index 5dc4aa7ec..b6efbba9b 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,14 +12,14 @@ def _get_kwargs( param_path: str, *, param_query: str = "overridden_in_GET", -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["param"] = param_query params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": f"/common_parameters_overriding/{param_path}", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py index 834875ff7..6a7ed7fd5 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -14,8 +14,8 @@ def _get_kwargs( param_query: Union[Unset, str] = UNSET, param_header: Union[Unset, str] = UNSET, param_cookie: Union[Unset, str] = UNSET, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} if not isinstance(param_header, Unset): headers["param"] = param_header @@ -23,13 +23,13 @@ def _get_kwargs( if param_cookie is not UNSET: cookies["param"] = param_cookie - params: Dict[str, Any] = {} + params: dict[str, Any] = {} params["param"] = param_query params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": f"/same-name-multiple-locations/{param_path}", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py index 22f476963..44345aa26 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -13,8 +13,8 @@ def _get_kwargs( param2: int, param1: str, param3: int, -) -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +) -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": f"/multiple-path-parameters/{param4}/something/{param2}/{param1}/{param3}", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py b/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py index 97bc5922a..cf0599306 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,8 +11,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "post", "url": "/responses/unions/simple_before_complex", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/responses/reference_response.py b/end_to_end_tests/golden-record/my_test_api_client/api/responses/reference_response.py new file mode 100644 index 000000000..ac71e9e50 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/responses/reference_response.py @@ -0,0 +1,122 @@ +from http import HTTPStatus +from typing import Any, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.a_model import AModel +from ...types import Response + + +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/responses/reference", + } + + return _kwargs + + +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[AModel]: + if response.status_code == 200: + response_200 = AModel.from_dict(response.json()) + + return response_200 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[AModel]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[AModel]: + """Endpoint using predefined response + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AModel] + """ + + kwargs = _get_kwargs() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[AModel]: + """Endpoint using predefined response + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AModel + """ + + return sync_detailed( + client=client, + ).parsed + + +async def asyncio_detailed( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[AModel]: + """Endpoint using predefined response + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AModel] + """ + + kwargs = _get_kwargs() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[AModel]: + """Endpoint using predefined response + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AModel + """ + + return ( + await asyncio_detailed( + client=client, + ) + ).parsed diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/responses/text_response.py b/end_to_end_tests/golden-record/my_test_api_client/api/responses/text_response.py index cc4dd9531..057ceb2de 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/responses/text_response.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/responses/text_response.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "post", "url": "/responses/text", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py b/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py index c6756b522..62631355f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tag_with_number", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tag2/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/api/tag2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tag2/get_tag_with_number.py b/end_to_end_tests/golden-record/my_test_api_client/api/tag2/get_tag_with_number.py new file mode 100644 index 000000000..62631355f --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tag2/get_tag_with_number.py @@ -0,0 +1,77 @@ +from http import HTTPStatus +from typing import Any, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...types import Response + + +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/tag_with_number", + } + + return _kwargs + + +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: + if response.status_code == 200: + return None + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Any]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Any]: + """ + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +async def asyncio_detailed( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Any]: + """ + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py index 30ed5aa7f..0852815d2 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -13,10 +13,10 @@ def _get_kwargs( *, body: AModel, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/callback", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/description_with_backslash.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/description_with_backslash.py index 4cfc8e5a5..e7cd44f70 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/description_with_backslash.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/description_with_backslash.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/description-with-backslash", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py index 53cb6598d..147eed3a7 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/booleans", } @@ -17,9 +17,9 @@ def _get_kwargs() -> Dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[List[bool]]: +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[list[bool]]: if response.status_code == 200: - response_200 = cast(List[bool], response.json()) + response_200 = cast(list[bool], response.json()) return response_200 if client.raise_on_unexpected_status: @@ -28,7 +28,7 @@ def _parse_response(*, client: Union[AuthenticatedClient, Client], response: htt return None -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[List[bool]]: +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[list[bool]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -40,7 +40,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[bool]]: +) -> Response[list[bool]]: """Get Basic List Of Booleans Get a list of booleans @@ -50,7 +50,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[bool]] + Response[list[bool]] """ kwargs = _get_kwargs() @@ -65,7 +65,7 @@ def sync_detailed( def sync( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[bool]]: +) -> Optional[list[bool]]: """Get Basic List Of Booleans Get a list of booleans @@ -75,7 +75,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[bool] + list[bool] """ return sync_detailed( @@ -86,7 +86,7 @@ def sync( async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[bool]]: +) -> Response[list[bool]]: """Get Basic List Of Booleans Get a list of booleans @@ -96,7 +96,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[bool]] + Response[list[bool]] """ kwargs = _get_kwargs() @@ -109,7 +109,7 @@ async def asyncio_detailed( async def asyncio( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[bool]]: +) -> Optional[list[bool]]: """Get Basic List Of Booleans Get a list of booleans @@ -119,7 +119,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[bool] + list[bool] """ return ( diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py index 7d1f71559..02b3abb1f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/floats", } @@ -17,9 +17,9 @@ def _get_kwargs() -> Dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[List[float]]: +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[list[float]]: if response.status_code == 200: - response_200 = cast(List[float], response.json()) + response_200 = cast(list[float], response.json()) return response_200 if client.raise_on_unexpected_status: @@ -28,7 +28,7 @@ def _parse_response(*, client: Union[AuthenticatedClient, Client], response: htt return None -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[List[float]]: +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[list[float]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -40,7 +40,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[float]]: +) -> Response[list[float]]: """Get Basic List Of Floats Get a list of floats @@ -50,7 +50,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[float]] + Response[list[float]] """ kwargs = _get_kwargs() @@ -65,7 +65,7 @@ def sync_detailed( def sync( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[float]]: +) -> Optional[list[float]]: """Get Basic List Of Floats Get a list of floats @@ -75,7 +75,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[float] + list[float] """ return sync_detailed( @@ -86,7 +86,7 @@ def sync( async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[float]]: +) -> Response[list[float]]: """Get Basic List Of Floats Get a list of floats @@ -96,7 +96,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[float]] + Response[list[float]] """ kwargs = _get_kwargs() @@ -109,7 +109,7 @@ async def asyncio_detailed( async def asyncio( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[float]]: +) -> Optional[list[float]]: """Get Basic List Of Floats Get a list of floats @@ -119,7 +119,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[float] + list[float] """ return ( diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py index 14d978288..e71537363 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/integers", } @@ -17,9 +17,9 @@ def _get_kwargs() -> Dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[List[int]]: +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[list[int]]: if response.status_code == 200: - response_200 = cast(List[int], response.json()) + response_200 = cast(list[int], response.json()) return response_200 if client.raise_on_unexpected_status: @@ -28,7 +28,7 @@ def _parse_response(*, client: Union[AuthenticatedClient, Client], response: htt return None -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[List[int]]: +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[list[int]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -40,7 +40,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[int]]: +) -> Response[list[int]]: """Get Basic List Of Integers Get a list of integers @@ -50,7 +50,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[int]] + Response[list[int]] """ kwargs = _get_kwargs() @@ -65,7 +65,7 @@ def sync_detailed( def sync( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[int]]: +) -> Optional[list[int]]: """Get Basic List Of Integers Get a list of integers @@ -75,7 +75,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[int] + list[int] """ return sync_detailed( @@ -86,7 +86,7 @@ def sync( async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[int]]: +) -> Response[list[int]]: """Get Basic List Of Integers Get a list of integers @@ -96,7 +96,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[int]] + Response[list[int]] """ kwargs = _get_kwargs() @@ -109,7 +109,7 @@ async def asyncio_detailed( async def asyncio( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[int]]: +) -> Optional[list[int]]: """Get Basic List Of Integers Get a list of integers @@ -119,7 +119,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[int] + list[int] """ return ( diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py index 61b2bc3f5..70f153829 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/strings", } @@ -17,9 +17,9 @@ def _get_kwargs() -> Dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[List[str]]: +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[list[str]]: if response.status_code == 200: - response_200 = cast(List[str], response.json()) + response_200 = cast(list[str], response.json()) return response_200 if client.raise_on_unexpected_status: @@ -28,7 +28,7 @@ def _parse_response(*, client: Union[AuthenticatedClient, Client], response: htt return None -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[List[str]]: +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[list[str]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -40,7 +40,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[str]]: +) -> Response[list[str]]: """Get Basic List Of Strings Get a list of strings @@ -50,7 +50,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[str]] + Response[list[str]] """ kwargs = _get_kwargs() @@ -65,7 +65,7 @@ def sync_detailed( def sync( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[str]]: +) -> Optional[list[str]]: """Get Basic List Of Strings Get a list of strings @@ -75,7 +75,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[str] + list[str] """ return sync_detailed( @@ -86,7 +86,7 @@ def sync( async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], -) -> Response[List[str]]: +) -> Response[list[str]]: """Get Basic List Of Strings Get a list of strings @@ -96,7 +96,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List[str]] + Response[list[str]] """ kwargs = _get_kwargs() @@ -109,7 +109,7 @@ async def asyncio_detailed( async def asyncio( *, client: Union[AuthenticatedClient, Client], -) -> Optional[List[str]]: +) -> Optional[list[str]]: """Get Basic List Of Strings Get a list of strings @@ -119,7 +119,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List[str] + list[str] """ return ( diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py index 444657982..a708cf71d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py @@ -1,6 +1,6 @@ import datetime from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union import httpx @@ -15,12 +15,12 @@ def _get_kwargs( *, - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], some_date: Union[datetime.date, datetime.datetime], -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} json_an_enum_value = [] for an_enum_value_item_data in an_enum_value: @@ -54,7 +54,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/", "params": params, @@ -65,7 +65,7 @@ def _get_kwargs( def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[HTTPValidationError, List["AModel"]]]: +) -> Optional[Union[HTTPValidationError, list["AModel"]]]: if response.status_code == 200: response_200 = [] _response_200 = response.json() @@ -91,7 +91,7 @@ def _parse_response( def _build_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[HTTPValidationError, List["AModel"]]]: +) -> Response[Union[HTTPValidationError, list["AModel"]]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -103,19 +103,19 @@ def _build_response( def sync_detailed( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], some_date: Union[datetime.date, datetime.datetime], -) -> Response[Union[HTTPValidationError, List["AModel"]]]: +) -> Response[Union[HTTPValidationError, list["AModel"]]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): some_date (Union[datetime.date, datetime.datetime]): Raises: @@ -123,7 +123,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[HTTPValidationError, List['AModel']]] + Response[Union[HTTPValidationError, list['AModel']]] """ kwargs = _get_kwargs( @@ -143,19 +143,19 @@ def sync_detailed( def sync( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], some_date: Union[datetime.date, datetime.datetime], -) -> Optional[Union[HTTPValidationError, List["AModel"]]]: +) -> Optional[Union[HTTPValidationError, list["AModel"]]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): some_date (Union[datetime.date, datetime.datetime]): Raises: @@ -163,7 +163,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[HTTPValidationError, List['AModel']] + Union[HTTPValidationError, list['AModel']] """ return sync_detailed( @@ -178,19 +178,19 @@ def sync( async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], some_date: Union[datetime.date, datetime.datetime], -) -> Response[Union[HTTPValidationError, List["AModel"]]]: +) -> Response[Union[HTTPValidationError, list["AModel"]]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): some_date (Union[datetime.date, datetime.datetime]): Raises: @@ -198,7 +198,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[HTTPValidationError, List['AModel']]] + Response[Union[HTTPValidationError, list['AModel']]] """ kwargs = _get_kwargs( @@ -216,19 +216,19 @@ async def asyncio_detailed( async def asyncio( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], some_date: Union[datetime.date, datetime.datetime], -) -> Optional[Union[HTTPValidationError, List["AModel"]]]: +) -> Optional[Union[HTTPValidationError, list["AModel"]]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): some_date (Union[datetime.date, datetime.datetime]): Raises: @@ -236,7 +236,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[HTTPValidationError, List['AModel']] + Union[HTTPValidationError, list['AModel']] """ return ( diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py index b9b17aeac..f33a23dc7 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -13,10 +13,10 @@ def _get_kwargs( *, body: AModel, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/json_body", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py index f78e06eed..586947f49 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/no_response", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py index 4810d5ebc..efb0f4ae5 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py @@ -1,6 +1,6 @@ from http import HTTPStatus from io import BytesIO -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -9,8 +9,8 @@ from ...types import File, Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/octet_stream", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_post.py index c7faeb15f..ea0cbd65a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -12,10 +12,10 @@ def _get_kwargs( *, body: File, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/octet_stream", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py index a2e7232c2..ec65d0363 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,10 +12,10 @@ def _get_kwargs( *, body: AFormData, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/post_form_data", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py index 290e6efdb..bc5ad7cc4 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,10 +12,10 @@ def _get_kwargs( *, body: PostFormDataInlineBody, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/post_form_data_inline", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py index bc80281c9..ba40de26f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -12,10 +12,10 @@ def _get_kwargs( *, body: str, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/json_body/string", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py index 07c16c748..287ea4a1a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -13,10 +13,10 @@ def _get_kwargs( *, body: TestInlineObjectsBody, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/inline_objects", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/token_with_cookie_auth_token_with_cookie_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/token_with_cookie_auth_token_with_cookie_get.py index e71ee24e9..22ac00650 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/token_with_cookie_auth_token_with_cookie_get.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/token_with_cookie_auth_token_with_cookie_get.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,11 +11,11 @@ def _get_kwargs( *, my_token: str, -) -> Dict[str, Any]: +) -> dict[str, Any]: cookies = {} cookies["MyToken"] = my_token - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/auth/token_with_cookie", "cookies": cookies, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py index e22ed5125..61e8434e6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -8,8 +8,8 @@ from ...types import Response -def _get_kwargs() -> Dict[str, Any]: - _kwargs: Dict[str, Any] = { +def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/unsupported_content", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py index 88b305101..9f1864ec3 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -13,10 +13,10 @@ def _get_kwargs( *, body: BodyUploadFileTestsUploadPost, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/upload", } diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py index 098ae7a13..3f8edc817 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,11 +11,11 @@ def _get_kwargs( *, - body: List[File], -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} + body: list[File], +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/upload/multiple", } @@ -62,14 +62,14 @@ def _build_response( def sync_detailed( *, client: Union[AuthenticatedClient, Client], - body: List[File], + body: list[File], ) -> Response[Union[Any, HTTPValidationError]]: """Upload multiple files Upload several files in the same request Args: - body (List[File]): + body (list[File]): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -93,14 +93,14 @@ def sync_detailed( def sync( *, client: Union[AuthenticatedClient, Client], - body: List[File], + body: list[File], ) -> Optional[Union[Any, HTTPValidationError]]: """Upload multiple files Upload several files in the same request Args: - body (List[File]): + body (list[File]): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -119,14 +119,14 @@ def sync( async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], - body: List[File], + body: list[File], ) -> Response[Union[Any, HTTPValidationError]]: """Upload multiple files Upload several files in the same request Args: - body (List[File]): + body (list[File]): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -148,14 +148,14 @@ async def asyncio_detailed( async def asyncio( *, client: Union[AuthenticatedClient, Client], - body: List[File], + body: list[File], ) -> Optional[Union[Any, HTTPValidationError]]: """Upload multiple files Upload several files in the same request Args: - body (List[File]): + body (list[File]): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py b/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py index 891485f62..b46550153 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,14 +11,14 @@ def _get_kwargs( *, import_: str, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["import"] = import_ params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/naming/keywords", "params": params, diff --git a/end_to_end_tests/golden-record/my_test_api_client/client.py b/end_to_end_tests/golden-record/my_test_api_client/client.py index 0f6d15e84..e80446f10 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/client.py +++ b/end_to_end_tests/golden-record/my_test_api_client/client.py @@ -1,5 +1,5 @@ import ssl -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx from attrs import define, evolve, field @@ -36,16 +36,16 @@ class Client: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) - def with_headers(self, headers: Dict[str, str]) -> "Client": + def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -53,7 +53,7 @@ def with_headers(self, headers: Dict[str, str]) -> "Client": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "Client": + def with_cookies(self, cookies: dict[str, str]) -> "Client": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) @@ -166,12 +166,12 @@ class AuthenticatedClient: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) @@ -179,7 +179,7 @@ class AuthenticatedClient: prefix: str = "Bearer" auth_header_name: str = "Authorization" - def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": + def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -187,7 +187,7 @@ def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "AuthenticatedClient": + def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index f354c31c7..06b8a1d9f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py @@ -1,7 +1,5 @@ """Contains all the data models used in inputs/outputs""" -from .a_discriminated_union_type_1 import ADiscriminatedUnionType1 -from .a_discriminated_union_type_2 import ADiscriminatedUnionType2 from .a_form_data import AFormData from .a_model import AModel from .a_model_with_properties_reference_that_are_not_object import AModelWithPropertiesReferenceThatAreNotObject @@ -54,14 +52,13 @@ ) from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed from .model_with_any_json_properties import ModelWithAnyJsonProperties -from .model_with_any_json_properties_additional_property_type_0 import ModelWithAnyJsonPropertiesAdditionalPropertyType0 +from .model_with_any_json_properties_additional_property import ModelWithAnyJsonPropertiesAdditionalProperty from .model_with_backslash_in_description import ModelWithBackslashInDescription from .model_with_circular_ref_a import ModelWithCircularRefA from .model_with_circular_ref_b import ModelWithCircularRefB from .model_with_circular_ref_in_additional_properties_a import ModelWithCircularRefInAdditionalPropertiesA from .model_with_circular_ref_in_additional_properties_b import ModelWithCircularRefInAdditionalPropertiesB from .model_with_date_time_property import ModelWithDateTimeProperty -from .model_with_discriminated_union import ModelWithDiscriminatedUnion from .model_with_merged_properties import ModelWithMergedProperties from .model_with_merged_properties_string_to_enum import ModelWithMergedPropertiesStringToEnum from .model_with_no_properties import ModelWithNoProperties @@ -82,16 +79,14 @@ from .post_naming_property_conflict_with_import_body import PostNamingPropertyConflictWithImportBody from .post_naming_property_conflict_with_import_response_200 import PostNamingPropertyConflictWithImportResponse200 from .post_responses_unions_simple_before_complex_response_200 import PostResponsesUnionsSimpleBeforeComplexResponse200 -from .post_responses_unions_simple_before_complex_response_200a_type_1 import ( - PostResponsesUnionsSimpleBeforeComplexResponse200AType1, +from .post_responses_unions_simple_before_complex_response_200a import ( + PostResponsesUnionsSimpleBeforeComplexResponse200A, ) from .test_inline_objects_body import TestInlineObjectsBody from .test_inline_objects_response_200 import TestInlineObjectsResponse200 from .validation_error import ValidationError __all__ = ( - "ADiscriminatedUnionType1", - "ADiscriminatedUnionType2", "AFormData", "AllOfHasPropertiesButNoType", "AllOfHasPropertiesButNoTypeTypeEnum", @@ -136,14 +131,13 @@ "ModelWithAdditionalPropertiesInlinedAdditionalProperty", "ModelWithAdditionalPropertiesRefed", "ModelWithAnyJsonProperties", - "ModelWithAnyJsonPropertiesAdditionalPropertyType0", + "ModelWithAnyJsonPropertiesAdditionalProperty", "ModelWithBackslashInDescription", "ModelWithCircularRefA", "ModelWithCircularRefB", "ModelWithCircularRefInAdditionalPropertiesA", "ModelWithCircularRefInAdditionalPropertiesB", "ModelWithDateTimeProperty", - "ModelWithDiscriminatedUnion", "ModelWithMergedProperties", "ModelWithMergedPropertiesStringToEnum", "ModelWithNoProperties", @@ -164,7 +158,7 @@ "PostNamingPropertyConflictWithImportBody", "PostNamingPropertyConflictWithImportResponse200", "PostResponsesUnionsSimpleBeforeComplexResponse200", - "PostResponsesUnionsSimpleBeforeComplexResponse200AType1", + "PostResponsesUnionsSimpleBeforeComplexResponse200A", "TestInlineObjectsBody", "TestInlineObjectsResponse200", "ValidationError", diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_discriminated_union_type_1.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_discriminated_union_type_1.py deleted file mode 100644 index cb1184b18..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_discriminated_union_type_1.py +++ /dev/null @@ -1,58 +0,0 @@ -from typing import Any, Dict, List, Type, TypeVar, Union - -from attrs import define as _attrs_define -from attrs import field as _attrs_field - -from ..types import UNSET, Unset - -T = TypeVar("T", bound="ADiscriminatedUnionType1") - - -@_attrs_define -class ADiscriminatedUnionType1: - """ - Attributes: - model_type (Union[Unset, str]): - """ - - model_type: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - model_type = self.model_type - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if model_type is not UNSET: - field_dict["modelType"] = model_type - - return field_dict - - @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: - d = src_dict.copy() - model_type = d.pop("modelType", UNSET) - - a_discriminated_union_type_1 = cls( - model_type=model_type, - ) - - a_discriminated_union_type_1.additional_properties = d - return a_discriminated_union_type_1 - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_discriminated_union_type_2.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_discriminated_union_type_2.py deleted file mode 100644 index 734f3bef4..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_discriminated_union_type_2.py +++ /dev/null @@ -1,58 +0,0 @@ -from typing import Any, Dict, List, Type, TypeVar, Union - -from attrs import define as _attrs_define -from attrs import field as _attrs_field - -from ..types import UNSET, Unset - -T = TypeVar("T", bound="ADiscriminatedUnionType2") - - -@_attrs_define -class ADiscriminatedUnionType2: - """ - Attributes: - model_type (Union[Unset, str]): - """ - - model_type: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - model_type = self.model_type - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if model_type is not UNSET: - field_dict["modelType"] = model_type - - return field_dict - - @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: - d = src_dict.copy() - model_type = d.pop("modelType", UNSET) - - a_discriminated_union_type_2 = cls( - model_type=model_type, - ) - - a_discriminated_union_type_2.additional_properties = d - return a_discriminated_union_type_2 - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_form_data.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_form_data.py index a4c5cd8a7..12c57c3bc 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_form_data.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_form_data.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,14 +18,14 @@ class AFormData: an_required_field: str an_optional_field: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: an_required_field = self.an_required_field an_optional_field = self.an_optional_field - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -38,7 +38,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() an_required_field = d.pop("an_required_field") @@ -53,7 +53,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return a_form_data @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py index a14400c9d..07b3eed76 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py @@ -1,5 +1,5 @@ import datetime -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from uuid import UUID from attrs import define as _attrs_define @@ -38,7 +38,7 @@ class AModel: nullable_model (Union['ModelWithUnionProperty', None]): any_value (Union[Unset, Any]): Default: 'default'. an_optional_allof_enum (Union[Unset, AnAllOfEnum]): - nested_list_of_enums (Union[Unset, List[List[DifferentEnum]]]): + nested_list_of_enums (Union[Unset, list[list[DifferentEnum]]]): a_not_required_date (Union[Unset, datetime.date]): a_not_required_uuid (Union[Unset, UUID]): attr_1_leading_digit (Union[Unset, str]): @@ -66,7 +66,7 @@ class AModel: a_nullable_uuid: Union[None, UUID] = UUID("07EF8B4D-AA09-4FFA-898D-C710796AFF41") any_value: Union[Unset, Any] = "default" an_optional_allof_enum: Union[Unset, AnAllOfEnum] = UNSET - nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET + nested_list_of_enums: Union[Unset, list[list[DifferentEnum]]] = UNSET a_not_required_date: Union[Unset, datetime.date] = UNSET a_not_required_uuid: Union[Unset, UUID] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET @@ -78,7 +78,7 @@ class AModel: not_required_model: Union[Unset, "ModelWithUnionProperty"] = UNSET not_required_nullable_model: Union["ModelWithUnionProperty", None, Unset] = UNSET - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: from ..models.free_form_model import FreeFormModel from ..models.model_with_union_property import ModelWithUnionProperty @@ -113,7 +113,7 @@ def to_dict(self) -> Dict[str, Any]: required_not_nullable = self.required_not_nullable - one_of_models: Union[Any, Dict[str, Any]] + one_of_models: Union[Any, dict[str, Any]] if isinstance(self.one_of_models, FreeFormModel): one_of_models = self.one_of_models.to_dict() elif isinstance(self.one_of_models, ModelWithUnionProperty): @@ -121,7 +121,7 @@ def to_dict(self) -> Dict[str, Any]: else: one_of_models = self.one_of_models - nullable_one_of_models: Union[Dict[str, Any], None] + nullable_one_of_models: Union[None, dict[str, Any]] if isinstance(self.nullable_one_of_models, FreeFormModel): nullable_one_of_models = self.nullable_one_of_models.to_dict() elif isinstance(self.nullable_one_of_models, ModelWithUnionProperty): @@ -131,7 +131,7 @@ def to_dict(self) -> Dict[str, Any]: model = self.model.to_dict() - nullable_model: Union[Dict[str, Any], None] + nullable_model: Union[None, dict[str, Any]] if isinstance(self.nullable_model, ModelWithUnionProperty): nullable_model = self.nullable_model.to_dict() else: @@ -143,7 +143,7 @@ def to_dict(self) -> Dict[str, Any]: if not isinstance(self.an_optional_allof_enum, Unset): an_optional_allof_enum = self.an_optional_allof_enum.value - nested_list_of_enums: Union[Unset, List[List[str]]] = UNSET + nested_list_of_enums: Union[Unset, list[list[str]]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] for nested_list_of_enums_item_data in self.nested_list_of_enums: @@ -174,7 +174,7 @@ def to_dict(self) -> Dict[str, Any]: not_required_not_nullable = self.not_required_not_nullable - not_required_one_of_models: Union[Dict[str, Any], Unset] + not_required_one_of_models: Union[Unset, dict[str, Any]] if isinstance(self.not_required_one_of_models, Unset): not_required_one_of_models = UNSET elif isinstance(self.not_required_one_of_models, FreeFormModel): @@ -182,7 +182,7 @@ def to_dict(self) -> Dict[str, Any]: else: not_required_one_of_models = self.not_required_one_of_models.to_dict() - not_required_nullable_one_of_models: Union[Dict[str, Any], None, Unset, str] + not_required_nullable_one_of_models: Union[None, Unset, dict[str, Any], str] if isinstance(self.not_required_nullable_one_of_models, Unset): not_required_nullable_one_of_models = UNSET elif isinstance(self.not_required_nullable_one_of_models, FreeFormModel): @@ -192,11 +192,11 @@ def to_dict(self) -> Dict[str, Any]: else: not_required_nullable_one_of_models = self.not_required_nullable_one_of_models - not_required_model: Union[Unset, Dict[str, Any]] = UNSET + not_required_model: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.not_required_model, Unset): not_required_model = self.not_required_model.to_dict() - not_required_nullable_model: Union[Dict[str, Any], None, Unset] + not_required_nullable_model: Union[None, Unset, dict[str, Any]] if isinstance(self.not_required_nullable_model, Unset): not_required_nullable_model = UNSET elif isinstance(self.not_required_nullable_model, ModelWithUnionProperty): @@ -204,7 +204,7 @@ def to_dict(self) -> Dict[str, Any]: else: not_required_nullable_model = self.not_required_nullable_model - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update( { "an_enum_value": an_enum_value, @@ -252,7 +252,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.free_form_model import FreeFormModel from ..models.model_with_union_property import ModelWithUnionProperty @@ -373,9 +373,9 @@ def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None] try: if not isinstance(data, dict): raise TypeError() - nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) + nullable_model = ModelWithUnionProperty.from_dict(data) - return nullable_model_type_1 + return nullable_model except: # noqa: E722 pass return cast(Union["ModelWithUnionProperty", None], data) @@ -495,9 +495,9 @@ def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionPro try: if not isinstance(data, dict): raise TypeError() - not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) + not_required_nullable_model = ModelWithUnionProperty.from_dict(data) - return not_required_nullable_model_type_1 + return not_required_nullable_model except: # noqa: E722 pass return cast(Union["ModelWithUnionProperty", None, Unset], data) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_properties_reference_that_are_not_object.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_properties_reference_that_are_not_object.py index 88ffd349f..c1a905195 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_properties_reference_that_are_not_object.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_properties_reference_that_are_not_object.py @@ -1,6 +1,6 @@ import datetime from io import BytesIO -from typing import Any, Dict, List, Type, TypeVar, cast +from typing import Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,26 +16,26 @@ class AModelWithPropertiesReferenceThatAreNotObject: """ Attributes: - enum_properties_ref (List[AnEnum]): - str_properties_ref (List[str]): - date_properties_ref (List[datetime.date]): - datetime_properties_ref (List[datetime.datetime]): - int32_properties_ref (List[int]): - int64_properties_ref (List[int]): - float_properties_ref (List[float]): - double_properties_ref (List[float]): - file_properties_ref (List[File]): - bytestream_properties_ref (List[str]): - enum_properties (List[AnEnum]): - str_properties (List[str]): - date_properties (List[datetime.date]): - datetime_properties (List[datetime.datetime]): - int32_properties (List[int]): - int64_properties (List[int]): - float_properties (List[float]): - double_properties (List[float]): - file_properties (List[File]): - bytestream_properties (List[str]): + enum_properties_ref (list[AnEnum]): + str_properties_ref (list[str]): + date_properties_ref (list[datetime.date]): + datetime_properties_ref (list[datetime.datetime]): + int32_properties_ref (list[int]): + int64_properties_ref (list[int]): + float_properties_ref (list[float]): + double_properties_ref (list[float]): + file_properties_ref (list[File]): + bytestream_properties_ref (list[str]): + enum_properties (list[AnEnum]): + str_properties (list[str]): + date_properties (list[datetime.date]): + datetime_properties (list[datetime.datetime]): + int32_properties (list[int]): + int64_properties (list[int]): + float_properties (list[float]): + double_properties (list[float]): + file_properties (list[File]): + bytestream_properties (list[str]): enum_property_ref (AnEnum): For testing Enums in all the ways they can be used str_property_ref (str): date_property_ref (datetime.date): @@ -48,26 +48,26 @@ class AModelWithPropertiesReferenceThatAreNotObject: bytestream_property_ref (str): """ - enum_properties_ref: List[AnEnum] - str_properties_ref: List[str] - date_properties_ref: List[datetime.date] - datetime_properties_ref: List[datetime.datetime] - int32_properties_ref: List[int] - int64_properties_ref: List[int] - float_properties_ref: List[float] - double_properties_ref: List[float] - file_properties_ref: List[File] - bytestream_properties_ref: List[str] - enum_properties: List[AnEnum] - str_properties: List[str] - date_properties: List[datetime.date] - datetime_properties: List[datetime.datetime] - int32_properties: List[int] - int64_properties: List[int] - float_properties: List[float] - double_properties: List[float] - file_properties: List[File] - bytestream_properties: List[str] + enum_properties_ref: list[AnEnum] + str_properties_ref: list[str] + date_properties_ref: list[datetime.date] + datetime_properties_ref: list[datetime.datetime] + int32_properties_ref: list[int] + int64_properties_ref: list[int] + float_properties_ref: list[float] + double_properties_ref: list[float] + file_properties_ref: list[File] + bytestream_properties_ref: list[str] + enum_properties: list[AnEnum] + str_properties: list[str] + date_properties: list[datetime.date] + datetime_properties: list[datetime.datetime] + int32_properties: list[int] + int64_properties: list[int] + float_properties: list[float] + double_properties: list[float] + file_properties: list[File] + bytestream_properties: list[str] enum_property_ref: AnEnum str_property_ref: str date_property_ref: datetime.date @@ -78,9 +78,9 @@ class AModelWithPropertiesReferenceThatAreNotObject: double_property_ref: float file_property_ref: File bytestream_property_ref: str - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: enum_properties_ref = [] for componentsschemas_an_other_array_of_enum_item_data in self.enum_properties_ref: componentsschemas_an_other_array_of_enum_item = componentsschemas_an_other_array_of_enum_item_data.value @@ -173,7 +173,7 @@ def to_dict(self) -> Dict[str, Any]: bytestream_property_ref = self.bytestream_property_ref - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -213,7 +213,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() enum_properties_ref = [] _enum_properties_ref = d.pop("enum_properties_ref") @@ -222,7 +222,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: enum_properties_ref.append(componentsschemas_an_other_array_of_enum_item) - str_properties_ref = cast(List[str], d.pop("str_properties_ref")) + str_properties_ref = cast(list[str], d.pop("str_properties_ref")) date_properties_ref = [] _date_properties_ref = d.pop("date_properties_ref") @@ -242,13 +242,13 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: datetime_properties_ref.append(componentsschemas_an_other_array_of_date_time_item) - int32_properties_ref = cast(List[int], d.pop("int32_properties_ref")) + int32_properties_ref = cast(list[int], d.pop("int32_properties_ref")) - int64_properties_ref = cast(List[int], d.pop("int64_properties_ref")) + int64_properties_ref = cast(list[int], d.pop("int64_properties_ref")) - float_properties_ref = cast(List[float], d.pop("float_properties_ref")) + float_properties_ref = cast(list[float], d.pop("float_properties_ref")) - double_properties_ref = cast(List[float], d.pop("double_properties_ref")) + double_properties_ref = cast(list[float], d.pop("double_properties_ref")) file_properties_ref = [] _file_properties_ref = d.pop("file_properties_ref") @@ -259,7 +259,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: file_properties_ref.append(componentsschemas_an_other_array_of_file_item) - bytestream_properties_ref = cast(List[str], d.pop("bytestream_properties_ref")) + bytestream_properties_ref = cast(list[str], d.pop("bytestream_properties_ref")) enum_properties = [] _enum_properties = d.pop("enum_properties") @@ -268,7 +268,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: enum_properties.append(componentsschemas_an_array_of_enum_item) - str_properties = cast(List[str], d.pop("str_properties")) + str_properties = cast(list[str], d.pop("str_properties")) date_properties = [] _date_properties = d.pop("date_properties") @@ -284,13 +284,13 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: datetime_properties.append(componentsschemas_an_array_of_date_time_item) - int32_properties = cast(List[int], d.pop("int32_properties")) + int32_properties = cast(list[int], d.pop("int32_properties")) - int64_properties = cast(List[int], d.pop("int64_properties")) + int64_properties = cast(list[int], d.pop("int64_properties")) - float_properties = cast(List[float], d.pop("float_properties")) + float_properties = cast(list[float], d.pop("float_properties")) - double_properties = cast(List[float], d.pop("double_properties")) + double_properties = cast(list[float], d.pop("double_properties")) file_properties = [] _file_properties = d.pop("file_properties") @@ -301,7 +301,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: file_properties.append(componentsschemas_an_array_of_file_item) - bytestream_properties = cast(List[str], d.pop("bytestream_properties")) + bytestream_properties = cast(list[str], d.pop("bytestream_properties")) enum_property_ref = AnEnum(d.pop("enum_property_ref")) @@ -360,7 +360,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return a_model_with_properties_reference_that_are_not_object @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/all_of_has_properties_but_no_type.py b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_has_properties_but_no_type.py index 245a1b04a..6a39f4951 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/all_of_has_properties_but_no_type.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_has_properties_but_no_type.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,42 +14,42 @@ class AllOfHasPropertiesButNoType: """ Attributes: a_sub_property (Union[Unset, str]): - type (Union[Unset, str]): + type_ (Union[Unset, str]): type_enum (Union[Unset, AllOfHasPropertiesButNoTypeTypeEnum]): """ a_sub_property: Union[Unset, str] = UNSET - type: Union[Unset, str] = UNSET + type_: Union[Unset, str] = UNSET type_enum: Union[Unset, AllOfHasPropertiesButNoTypeTypeEnum] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_sub_property = self.a_sub_property - type = self.type + type_ = self.type_ type_enum: Union[Unset, int] = UNSET if not isinstance(self.type_enum, Unset): type_enum = self.type_enum.value - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a_sub_property is not UNSET: field_dict["a_sub_property"] = a_sub_property - if type is not UNSET: - field_dict["type"] = type + if type_ is not UNSET: + field_dict["type"] = type_ if type_enum is not UNSET: field_dict["type_enum"] = type_enum return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_sub_property = d.pop("a_sub_property", UNSET) - type = d.pop("type", UNSET) + type_ = d.pop("type", UNSET) _type_enum = d.pop("type_enum", UNSET) type_enum: Union[Unset, AllOfHasPropertiesButNoTypeTypeEnum] @@ -60,7 +60,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: all_of_has_properties_but_no_type = cls( a_sub_property=a_sub_property, - type=type, + type_=type_, type_enum=type_enum, ) @@ -68,7 +68,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return all_of_has_properties_but_no_type @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/all_of_sub_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_sub_model.py index 550b9b9c4..dfb672ce1 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/all_of_sub_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_sub_model.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,42 +14,42 @@ class AllOfSubModel: """ Attributes: a_sub_property (Union[Unset, str]): - type (Union[Unset, str]): + type_ (Union[Unset, str]): type_enum (Union[Unset, AllOfSubModelTypeEnum]): """ a_sub_property: Union[Unset, str] = UNSET - type: Union[Unset, str] = UNSET + type_: Union[Unset, str] = UNSET type_enum: Union[Unset, AllOfSubModelTypeEnum] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_sub_property = self.a_sub_property - type = self.type + type_ = self.type_ type_enum: Union[Unset, int] = UNSET if not isinstance(self.type_enum, Unset): type_enum = self.type_enum.value - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a_sub_property is not UNSET: field_dict["a_sub_property"] = a_sub_property - if type is not UNSET: - field_dict["type"] = type + if type_ is not UNSET: + field_dict["type"] = type_ if type_enum is not UNSET: field_dict["type_enum"] = type_enum return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_sub_property = d.pop("a_sub_property", UNSET) - type = d.pop("type", UNSET) + type_ = d.pop("type", UNSET) _type_enum = d.pop("type_enum", UNSET) type_enum: Union[Unset, AllOfSubModelTypeEnum] @@ -60,7 +60,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: all_of_sub_model = cls( a_sub_property=a_sub_property, - type=type, + type_=type_, type_enum=type_enum, ) @@ -68,7 +68,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return all_of_sub_model @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_a_item.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_a_item.py index b7792fefc..02e9cfa4b 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_a_item.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_a_item.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,14 +16,14 @@ class AnArrayWithACircularRefInItemsObjectAItem: """ Attributes: - circular (Union[Unset, List['AnArrayWithACircularRefInItemsObjectBItem']]): + circular (Union[Unset, list['AnArrayWithACircularRefInItemsObjectBItem']]): """ - circular: Union[Unset, List["AnArrayWithACircularRefInItemsObjectBItem"]] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + circular: Union[Unset, list["AnArrayWithACircularRefInItemsObjectBItem"]] = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - circular: Union[Unset, List[Dict[str, Any]]] = UNSET + def to_dict(self) -> dict[str, Any]: + circular: Union[Unset, list[dict[str, Any]]] = UNSET if not isinstance(self.circular, Unset): circular = [] for componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item_data in self.circular: @@ -32,7 +32,7 @@ def to_dict(self) -> Dict[str, Any]: ) circular.append(componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if circular is not UNSET: @@ -41,7 +41,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_b_item import ( AnArrayWithACircularRefInItemsObjectBItem, ) @@ -66,7 +66,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return an_array_with_a_circular_ref_in_items_object_a_item @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_a_item.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_a_item.py index c505553b6..c72c0160a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_a_item.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_a_item.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem: """ """ - additional_properties: Dict[str, List["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem"]] = ( + additional_properties: dict[str, list["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem"]] = ( _attrs_field(init=False, factory=dict) ) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = [] for ( @@ -35,7 +35,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_additional_properties_b_item import ( AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem, ) @@ -68,14 +68,14 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return an_array_with_a_circular_ref_in_items_object_additional_properties_a_item @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> List["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem"]: + def __getitem__(self, key: str) -> list["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem"]: return self.additional_properties[key] def __setitem__( - self, key: str, value: List["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem"] + self, key: str, value: list["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem"] ) -> None: self.additional_properties[key] = value diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_b_item.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_b_item.py index 9d2dc9827..7ffb50a16 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_b_item.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_additional_properties_b_item.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem: """ """ - additional_properties: Dict[str, List["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem"]] = ( + additional_properties: dict[str, list["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem"]] = ( _attrs_field(init=False, factory=dict) ) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = [] for ( @@ -35,7 +35,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_additional_properties_a_item import ( AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem, ) @@ -68,14 +68,14 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return an_array_with_a_circular_ref_in_items_object_additional_properties_b_item @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> List["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem"]: + def __getitem__(self, key: str) -> list["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem"]: return self.additional_properties[key] def __setitem__( - self, key: str, value: List["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem"] + self, key: str, value: list["AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem"] ) -> None: self.additional_properties[key] = value diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_b_item.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_b_item.py index 622d5d999..6d5e83a65 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_b_item.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_circular_ref_in_items_object_b_item.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,14 +16,14 @@ class AnArrayWithACircularRefInItemsObjectBItem: """ Attributes: - circular (Union[Unset, List['AnArrayWithACircularRefInItemsObjectAItem']]): + circular (Union[Unset, list['AnArrayWithACircularRefInItemsObjectAItem']]): """ - circular: Union[Unset, List["AnArrayWithACircularRefInItemsObjectAItem"]] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + circular: Union[Unset, list["AnArrayWithACircularRefInItemsObjectAItem"]] = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - circular: Union[Unset, List[Dict[str, Any]]] = UNSET + def to_dict(self) -> dict[str, Any]: + circular: Union[Unset, list[dict[str, Any]]] = UNSET if not isinstance(self.circular, Unset): circular = [] for componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item_data in self.circular: @@ -32,7 +32,7 @@ def to_dict(self) -> Dict[str, Any]: ) circular.append(componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if circular is not UNSET: @@ -41,7 +41,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_a_item import ( AnArrayWithACircularRefInItemsObjectAItem, ) @@ -66,7 +66,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return an_array_with_a_circular_ref_in_items_object_b_item @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_additional_properties_item.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_additional_properties_item.py index e19cfc052..14dc6ba26 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_additional_properties_item.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_additional_properties_item.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,12 +10,12 @@ class AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem: """ """ - additional_properties: Dict[str, List["AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem"]] = ( + additional_properties: dict[str, list["AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem"]] = ( _attrs_field(init=False, factory=dict) ) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = [] for componentsschemas_an_array_with_a_recursive_ref_in_items_object_additional_properties_item_data in prop: @@ -27,7 +27,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() an_array_with_a_recursive_ref_in_items_object_additional_properties_item = cls() @@ -56,14 +56,14 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return an_array_with_a_recursive_ref_in_items_object_additional_properties_item @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> List["AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem"]: + def __getitem__(self, key: str) -> list["AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem"]: return self.additional_properties[key] def __setitem__( - self, key: str, value: List["AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem"] + self, key: str, value: list["AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem"] ) -> None: self.additional_properties[key] = value diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_item.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_item.py index 6b12b9b5d..c8629e83d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_item.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_array_with_a_recursive_ref_in_items_object_item.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -12,14 +12,14 @@ class AnArrayWithARecursiveRefInItemsObjectItem: """ Attributes: - recursive (Union[Unset, List['AnArrayWithARecursiveRefInItemsObjectItem']]): + recursive (Union[Unset, list['AnArrayWithARecursiveRefInItemsObjectItem']]): """ - recursive: Union[Unset, List["AnArrayWithARecursiveRefInItemsObjectItem"]] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + recursive: Union[Unset, list["AnArrayWithARecursiveRefInItemsObjectItem"]] = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - recursive: Union[Unset, List[Dict[str, Any]]] = UNSET + def to_dict(self) -> dict[str, Any]: + recursive: Union[Unset, list[dict[str, Any]]] = UNSET if not isinstance(self.recursive, Unset): recursive = [] for componentsschemas_an_array_with_a_recursive_ref_in_items_object_item_data in self.recursive: @@ -28,7 +28,7 @@ def to_dict(self) -> Dict[str, Any]: ) recursive.append(componentsschemas_an_array_with_a_recursive_ref_in_items_object_item) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if recursive is not UNSET: @@ -37,7 +37,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() recursive = [] _recursive = d.pop("recursive", UNSET) @@ -58,7 +58,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return an_array_with_a_recursive_ref_in_items_object_item @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model.py index fde2bb6f8..df2d9b2cd 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -15,49 +15,49 @@ class AnotherAllOfSubModel: """ Attributes: another_sub_property (Union[Unset, str]): - type (Union[Unset, AnotherAllOfSubModelType]): + type_ (Union[Unset, AnotherAllOfSubModelType]): type_enum (Union[Unset, AnotherAllOfSubModelTypeEnum]): """ another_sub_property: Union[Unset, str] = UNSET - type: Union[Unset, AnotherAllOfSubModelType] = UNSET + type_: Union[Unset, AnotherAllOfSubModelType] = UNSET type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: another_sub_property = self.another_sub_property - type: Union[Unset, str] = UNSET - if not isinstance(self.type, Unset): - type = self.type.value + type_: Union[Unset, str] = UNSET + if not isinstance(self.type_, Unset): + type_ = self.type_.value type_enum: Union[Unset, int] = UNSET if not isinstance(self.type_enum, Unset): type_enum = self.type_enum.value - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if another_sub_property is not UNSET: field_dict["another_sub_property"] = another_sub_property - if type is not UNSET: - field_dict["type"] = type + if type_ is not UNSET: + field_dict["type"] = type_ if type_enum is not UNSET: field_dict["type_enum"] = type_enum return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() another_sub_property = d.pop("another_sub_property", UNSET) - _type = d.pop("type", UNSET) - type: Union[Unset, AnotherAllOfSubModelType] - if isinstance(_type, Unset): - type = UNSET + _type_ = d.pop("type", UNSET) + type_: Union[Unset, AnotherAllOfSubModelType] + if isinstance(_type_, Unset): + type_ = UNSET else: - type = AnotherAllOfSubModelType(_type) + type_ = AnotherAllOfSubModelType(_type_) _type_enum = d.pop("type_enum", UNSET) type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] @@ -68,7 +68,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: another_all_of_sub_model = cls( another_sub_property=another_sub_property, - type=type, + type_=type_, type_enum=type_enum, ) @@ -76,7 +76,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return another_all_of_sub_model @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py index d7fbbf835..4bcc53863 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py @@ -1,7 +1,7 @@ import datetime import json from io import BytesIO -from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Type, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -41,8 +41,8 @@ class BodyUploadFileTestsUploadPost: a_date (Union[Unset, datetime.date]): some_number (Union[Unset, float]): some_nullable_number (Union[None, Unset, float]): - some_int_array (Union[Unset, List[Union[None, int]]]): - some_array (Union[List['AFormData'], None, Unset]): + some_int_array (Union[Unset, list[Union[None, int]]]): + some_array (Union[None, Unset, list['AFormData']]): some_optional_object (Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject]): some_enum (Union[Unset, DifferentEnum]): An enumeration. """ @@ -57,15 +57,15 @@ class BodyUploadFileTestsUploadPost: a_date: Union[Unset, datetime.date] = UNSET some_number: Union[Unset, float] = UNSET some_nullable_number: Union[None, Unset, float] = UNSET - some_int_array: Union[Unset, List[Union[None, int]]] = UNSET - some_array: Union[List["AFormData"], None, Unset] = UNSET + some_int_array: Union[Unset, list[Union[None, int]]] = UNSET + some_array: Union[None, Unset, list["AFormData"]] = UNSET some_optional_object: Union[Unset, "BodyUploadFileTestsUploadPostSomeOptionalObject"] = UNSET some_enum: Union[Unset, DifferentEnum] = UNSET - additional_properties: Dict[str, "BodyUploadFileTestsUploadPostAdditionalProperty"] = _attrs_field( + additional_properties: dict[str, "BodyUploadFileTestsUploadPostAdditionalProperty"] = _attrs_field( init=False, factory=dict ) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: from ..models.body_upload_file_tests_upload_post_some_nullable_object import ( BodyUploadFileTestsUploadPostSomeNullableObject, ) @@ -76,7 +76,7 @@ def to_dict(self) -> Dict[str, Any]: some_object = self.some_object.to_dict() - some_nullable_object: Union[Dict[str, Any], None] + some_nullable_object: Union[None, dict[str, Any]] if isinstance(self.some_nullable_object, BodyUploadFileTestsUploadPostSomeNullableObject): some_nullable_object = self.some_nullable_object.to_dict() else: @@ -104,7 +104,7 @@ def to_dict(self) -> Dict[str, Any]: else: some_nullable_number = self.some_nullable_number - some_int_array: Union[Unset, List[Union[None, int]]] = UNSET + some_int_array: Union[Unset, list[Union[None, int]]] = UNSET if not isinstance(self.some_int_array, Unset): some_int_array = [] for some_int_array_item_data in self.some_int_array: @@ -112,7 +112,7 @@ def to_dict(self) -> Dict[str, Any]: some_int_array_item = some_int_array_item_data some_int_array.append(some_int_array_item) - some_array: Union[List[Dict[str, Any]], None, Unset] + some_array: Union[None, Unset, list[dict[str, Any]]] if isinstance(self.some_array, Unset): some_array = UNSET elif isinstance(self.some_array, list): @@ -124,7 +124,7 @@ def to_dict(self) -> Dict[str, Any]: else: some_array = self.some_array - some_optional_object: Union[Unset, Dict[str, Any]] = UNSET + some_optional_object: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.some_optional_object, Unset): some_optional_object = self.some_optional_object.to_dict() @@ -132,7 +132,7 @@ def to_dict(self) -> Dict[str, Any]: if not isinstance(self.some_enum, Unset): some_enum = self.some_enum.value - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() field_dict.update( @@ -166,14 +166,14 @@ def to_dict(self) -> Dict[str, Any]: return field_dict - def to_multipart(self) -> Dict[str, Any]: + def to_multipart(self) -> dict[str, Any]: some_file = self.some_file.to_tuple() some_required_number = (None, str(self.some_required_number).encode(), "text/plain") some_object = (None, json.dumps(self.some_object.to_dict()).encode(), "application/json") - some_nullable_object: Tuple[None, bytes, str] + some_nullable_object: tuple[None, bytes, str] if isinstance(self.some_nullable_object, BodyUploadFileTestsUploadPostSomeNullableObject): some_nullable_object = (None, json.dumps(self.some_nullable_object.to_dict()).encode(), "application/json") @@ -204,7 +204,7 @@ def to_multipart(self) -> Dict[str, Any]: else (None, str(self.some_number).encode(), "text/plain") ) - some_nullable_number: Union[Tuple[None, bytes, str], Unset] + some_nullable_number: Union[Unset, tuple[None, bytes, str]] if isinstance(self.some_nullable_number, Unset): some_nullable_number = UNSET @@ -213,7 +213,7 @@ def to_multipart(self) -> Dict[str, Any]: else: some_nullable_number = (None, str(self.some_nullable_number).encode(), "text/plain") - some_int_array: Union[Unset, Tuple[None, bytes, str]] = UNSET + some_int_array: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.some_int_array, Unset): _temp_some_int_array = [] for some_int_array_item_data in self.some_int_array: @@ -222,7 +222,7 @@ def to_multipart(self) -> Dict[str, Any]: _temp_some_int_array.append(some_int_array_item) some_int_array = (None, json.dumps(_temp_some_int_array).encode(), "application/json") - some_array: Union[Tuple[None, bytes, str], Unset] + some_array: Union[Unset, tuple[None, bytes, str]] if isinstance(self.some_array, Unset): some_array = UNSET @@ -235,15 +235,15 @@ def to_multipart(self) -> Dict[str, Any]: else: some_array = (None, str(self.some_array).encode(), "text/plain") - some_optional_object: Union[Unset, Tuple[None, bytes, str]] = UNSET + some_optional_object: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.some_optional_object, Unset): some_optional_object = (None, json.dumps(self.some_optional_object.to_dict()).encode(), "application/json") - some_enum: Union[Unset, Tuple[None, bytes, str]] = UNSET + some_enum: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.some_enum, Unset): some_enum = (None, str(self.some_enum.value).encode(), "text/plain") - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = (None, json.dumps(prop.to_dict()).encode(), "application/json") field_dict.update( @@ -278,7 +278,7 @@ def to_multipart(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.a_form_data import AFormData from ..models.body_upload_file_tests_upload_post_additional_property import ( BodyUploadFileTestsUploadPostAdditionalProperty, @@ -304,9 +304,9 @@ def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploa try: if not isinstance(data, dict): raise TypeError() - some_nullable_object_type_0 = BodyUploadFileTestsUploadPostSomeNullableObject.from_dict(data) + some_nullable_object = BodyUploadFileTestsUploadPostSomeNullableObject.from_dict(data) - return some_nullable_object_type_0 + return some_nullable_object except: # noqa: E722 pass return cast(Union["BodyUploadFileTestsUploadPostSomeNullableObject", None], data) @@ -360,7 +360,7 @@ def _parse_some_int_array_item(data: object) -> Union[None, int]: some_int_array.append(some_int_array_item) - def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]: + def _parse_some_array(data: object) -> Union[None, Unset, list["AFormData"]]: if data is None: return data if isinstance(data, Unset): @@ -378,7 +378,7 @@ def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]: return some_array_type_0 except: # noqa: E722 pass - return cast(Union[List["AFormData"], None, Unset], data) + return cast(Union[None, Unset, list["AFormData"]], data) some_array = _parse_some_array(d.pop("some_array", UNSET)) @@ -423,7 +423,7 @@ def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]: return body_upload_file_tests_upload_post @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> "BodyUploadFileTestsUploadPostAdditionalProperty": diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_additional_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_additional_property.py index f855d9c61..47dde1338 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_additional_property.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_additional_property.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class BodyUploadFileTestsUploadPostAdditionalProperty: """ foo: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: foo = self.foo - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if foo is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() foo = d.pop("foo", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return body_upload_file_tests_upload_post_additional_property @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_nullable_object.py b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_nullable_object.py index 9762b7efa..817dfd789 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_nullable_object.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_nullable_object.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class BodyUploadFileTestsUploadPostSomeNullableObject: """ bar: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: bar = self.bar - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if bar is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() bar = d.pop("bar", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return body_upload_file_tests_upload_post_some_nullable_object @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_object.py b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_object.py index 25c2c0a6a..a074bb86f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_object.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_object.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,14 +16,14 @@ class BodyUploadFileTestsUploadPostSomeObject: num: float text: str - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: num = self.num text = self.text - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -35,7 +35,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() num = d.pop("num") @@ -50,7 +50,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return body_upload_file_tests_upload_post_some_object @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_optional_object.py b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_optional_object.py index 711b34e63..e0ba4b364 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_optional_object.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post_some_optional_object.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,12 +14,12 @@ class BodyUploadFileTestsUploadPostSomeOptionalObject: """ foo: str - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: foo = self.foo - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() foo = d.pop("foo") @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return body_upload_file_tests_upload_post_some_optional_object @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/extended.py b/end_to_end_tests/golden-record/my_test_api_client/models/extended.py index 324513d3a..6ba22347e 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/extended.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/extended.py @@ -1,5 +1,5 @@ import datetime -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from uuid import UUID from attrs import define as _attrs_define @@ -38,7 +38,7 @@ class Extended: nullable_model (Union['ModelWithUnionProperty', None]): any_value (Union[Unset, Any]): Default: 'default'. an_optional_allof_enum (Union[Unset, AnAllOfEnum]): - nested_list_of_enums (Union[Unset, List[List[DifferentEnum]]]): + nested_list_of_enums (Union[Unset, list[list[DifferentEnum]]]): a_not_required_date (Union[Unset, datetime.date]): a_not_required_uuid (Union[Unset, UUID]): attr_1_leading_digit (Union[Unset, str]): @@ -67,7 +67,7 @@ class Extended: a_nullable_uuid: Union[None, UUID] = UUID("07EF8B4D-AA09-4FFA-898D-C710796AFF41") any_value: Union[Unset, Any] = "default" an_optional_allof_enum: Union[Unset, AnAllOfEnum] = UNSET - nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET + nested_list_of_enums: Union[Unset, list[list[DifferentEnum]]] = UNSET a_not_required_date: Union[Unset, datetime.date] = UNSET a_not_required_uuid: Union[Unset, UUID] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET @@ -79,9 +79,9 @@ class Extended: not_required_model: Union[Unset, "ModelWithUnionProperty"] = UNSET not_required_nullable_model: Union["ModelWithUnionProperty", None, Unset] = UNSET from_extended: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: from ..models.free_form_model import FreeFormModel from ..models.model_with_union_property import ModelWithUnionProperty @@ -116,7 +116,7 @@ def to_dict(self) -> Dict[str, Any]: required_not_nullable = self.required_not_nullable - one_of_models: Union[Any, Dict[str, Any]] + one_of_models: Union[Any, dict[str, Any]] if isinstance(self.one_of_models, FreeFormModel): one_of_models = self.one_of_models.to_dict() elif isinstance(self.one_of_models, ModelWithUnionProperty): @@ -124,7 +124,7 @@ def to_dict(self) -> Dict[str, Any]: else: one_of_models = self.one_of_models - nullable_one_of_models: Union[Dict[str, Any], None] + nullable_one_of_models: Union[None, dict[str, Any]] if isinstance(self.nullable_one_of_models, FreeFormModel): nullable_one_of_models = self.nullable_one_of_models.to_dict() elif isinstance(self.nullable_one_of_models, ModelWithUnionProperty): @@ -134,7 +134,7 @@ def to_dict(self) -> Dict[str, Any]: model = self.model.to_dict() - nullable_model: Union[Dict[str, Any], None] + nullable_model: Union[None, dict[str, Any]] if isinstance(self.nullable_model, ModelWithUnionProperty): nullable_model = self.nullable_model.to_dict() else: @@ -146,7 +146,7 @@ def to_dict(self) -> Dict[str, Any]: if not isinstance(self.an_optional_allof_enum, Unset): an_optional_allof_enum = self.an_optional_allof_enum.value - nested_list_of_enums: Union[Unset, List[List[str]]] = UNSET + nested_list_of_enums: Union[Unset, list[list[str]]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] for nested_list_of_enums_item_data in self.nested_list_of_enums: @@ -177,7 +177,7 @@ def to_dict(self) -> Dict[str, Any]: not_required_not_nullable = self.not_required_not_nullable - not_required_one_of_models: Union[Dict[str, Any], Unset] + not_required_one_of_models: Union[Unset, dict[str, Any]] if isinstance(self.not_required_one_of_models, Unset): not_required_one_of_models = UNSET elif isinstance(self.not_required_one_of_models, FreeFormModel): @@ -185,7 +185,7 @@ def to_dict(self) -> Dict[str, Any]: else: not_required_one_of_models = self.not_required_one_of_models.to_dict() - not_required_nullable_one_of_models: Union[Dict[str, Any], None, Unset, str] + not_required_nullable_one_of_models: Union[None, Unset, dict[str, Any], str] if isinstance(self.not_required_nullable_one_of_models, Unset): not_required_nullable_one_of_models = UNSET elif isinstance(self.not_required_nullable_one_of_models, FreeFormModel): @@ -195,11 +195,11 @@ def to_dict(self) -> Dict[str, Any]: else: not_required_nullable_one_of_models = self.not_required_nullable_one_of_models - not_required_model: Union[Unset, Dict[str, Any]] = UNSET + not_required_model: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.not_required_model, Unset): not_required_model = self.not_required_model.to_dict() - not_required_nullable_model: Union[Dict[str, Any], None, Unset] + not_required_nullable_model: Union[None, Unset, dict[str, Any]] if isinstance(self.not_required_nullable_model, Unset): not_required_nullable_model = UNSET elif isinstance(self.not_required_nullable_model, ModelWithUnionProperty): @@ -209,7 +209,7 @@ def to_dict(self) -> Dict[str, Any]: from_extended = self.from_extended - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -260,7 +260,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.free_form_model import FreeFormModel from ..models.model_with_union_property import ModelWithUnionProperty @@ -381,9 +381,9 @@ def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None] try: if not isinstance(data, dict): raise TypeError() - nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) + nullable_model = ModelWithUnionProperty.from_dict(data) - return nullable_model_type_1 + return nullable_model except: # noqa: E722 pass return cast(Union["ModelWithUnionProperty", None], data) @@ -503,9 +503,9 @@ def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionPro try: if not isinstance(data, dict): raise TypeError() - not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) + not_required_nullable_model = ModelWithUnionProperty.from_dict(data) - return not_required_nullable_model_type_1 + return not_required_nullable_model except: # noqa: E722 pass return cast(Union["ModelWithUnionProperty", None, Unset], data) @@ -548,7 +548,7 @@ def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionPro return extended @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py index f757b10ae..10770503b 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,16 +10,16 @@ class FreeFormModel: """ """ - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() free_form_model = cls() @@ -27,7 +27,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return free_form_model @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/get_models_allof_response_200.py b/end_to_end_tests/golden-record/my_test_api_client/models/get_models_allof_response_200.py index 2662dc1f4..5e24734e3 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/get_models_allof_response_200.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/get_models_allof_response_200.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -25,22 +25,22 @@ class GetModelsAllofResponse200: aliased: Union[Unset, "AModel"] = UNSET extended: Union[Unset, "Extended"] = UNSET model: Union[Unset, "AModel"] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - aliased: Union[Unset, Dict[str, Any]] = UNSET + def to_dict(self) -> dict[str, Any]: + aliased: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.aliased, Unset): aliased = self.aliased.to_dict() - extended: Union[Unset, Dict[str, Any]] = UNSET + extended: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.extended, Unset): extended = self.extended.to_dict() - model: Union[Unset, Dict[str, Any]] = UNSET + model: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.model, Unset): model = self.model.to_dict() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if aliased is not UNSET: @@ -53,7 +53,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.a_model import AModel from ..models.extended import Extended @@ -89,7 +89,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return get_models_allof_response_200 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_0.py b/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_0.py index 972e1c765..18c1e9e64 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_0.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_0.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Type, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -12,24 +12,24 @@ class GetModelsOneofWithRequiredConstResponse200Type0: """ Attributes: - type (Literal['alpha']): + type_ (Literal['alpha']): color (Union[Unset, str]): """ - type: Literal["alpha"] + type_: Literal["alpha"] color: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - type = self.type + def to_dict(self) -> dict[str, Any]: + type_ = self.type_ color = self.color - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { - "type": type, + "type": type_, } ) if color is not UNSET: @@ -38,16 +38,16 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - type = cast(Literal["alpha"], d.pop("type")) - if type != "alpha": - raise ValueError(f"type must match const 'alpha', got '{type}'") + type_ = cast(Literal["alpha"], d.pop("type")) + if type_ != "alpha": + raise ValueError(f"type must match const 'alpha', got '{type_}'") color = d.pop("color", UNSET) get_models_oneof_with_required_const_response_200_type_0 = cls( - type=type, + type_=type_, color=color, ) @@ -55,7 +55,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return get_models_oneof_with_required_const_response_200_type_0 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_1.py b/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_1.py index 4596c3cc4..b1df54c32 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_1.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/get_models_oneof_with_required_const_response_200_type_1.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Type, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -12,24 +12,24 @@ class GetModelsOneofWithRequiredConstResponse200Type1: """ Attributes: - type (Literal['beta']): + type_ (Literal['beta']): texture (Union[Unset, str]): """ - type: Literal["beta"] + type_: Literal["beta"] texture: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - type = self.type + def to_dict(self) -> dict[str, Any]: + type_ = self.type_ texture = self.texture - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { - "type": type, + "type": type_, } ) if texture is not UNSET: @@ -38,16 +38,16 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - type = cast(Literal["beta"], d.pop("type")) - if type != "beta": - raise ValueError(f"type must match const 'beta', got '{type}'") + type_ = cast(Literal["beta"], d.pop("type")) + if type_ != "beta": + raise ValueError(f"type must match const 'beta', got '{type_}'") texture = d.pop("texture", UNSET) get_models_oneof_with_required_const_response_200_type_1 = cls( - type=type, + type_=type_, texture=texture, ) @@ -55,7 +55,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return get_models_oneof_with_required_const_response_200_type_1 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py b/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py index 1f04c29d0..a423c6d2f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define @@ -15,20 +15,20 @@ class HTTPValidationError: """ Attributes: - detail (Union[Unset, List['ValidationError']]): + detail (Union[Unset, list['ValidationError']]): """ - detail: Union[Unset, List["ValidationError"]] = UNSET + detail: Union[Unset, list["ValidationError"]] = UNSET - def to_dict(self) -> Dict[str, Any]: - detail: Union[Unset, List[Dict[str, Any]]] = UNSET + def to_dict(self) -> dict[str, Any]: + detail: Union[Unset, list[dict[str, Any]]] = UNSET if not isinstance(self.detail, Unset): detail = [] for detail_item_data in self.detail: detail_item = detail_item_data.to_dict() detail.append(detail_item) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update({}) if detail is not UNSET: field_dict["detail"] = detail @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.validation_error import ValidationError d = src_dict.copy() diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/import_.py b/end_to_end_tests/golden-record/my_test_api_client/models/import_.py index 85cc594e7..79788bf80 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/import_.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/import_.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,16 +10,16 @@ class Import: """ """ - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() import_ = cls() @@ -27,7 +27,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return import_ @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/json_like_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/json_like_body.py index 623dcd848..bb4a31010 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/json_like_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/json_like_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class JsonLikeBody: """ a: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a = self.a - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a = d.pop("a", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return json_like_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/mixed_case_response_200.py b/end_to_end_tests/golden-record/my_test_api_client/models/mixed_case_response_200.py index 21bdd918d..adb74459d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/mixed_case_response_200.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/mixed_case_response_200.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,14 +18,14 @@ class MixedCaseResponse200: mixed_case: Union[Unset, str] = UNSET mixedCase: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: mixed_case = self.mixed_case mixedCase = self.mixedCase - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if mixed_case is not UNSET: @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() mixed_case = d.pop("mixed_case", UNSET) @@ -51,7 +51,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return mixed_case_response_200 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_from_all_of.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_from_all_of.py index 6414b790d..a9fb59976 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_from_all_of.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_from_all_of.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -15,23 +15,23 @@ class ModelFromAllOf: """ Attributes: a_sub_property (Union[Unset, str]): - type (Union[Unset, AnotherAllOfSubModelType]): + type_ (Union[Unset, AnotherAllOfSubModelType]): type_enum (Union[Unset, AnotherAllOfSubModelTypeEnum]): another_sub_property (Union[Unset, str]): """ a_sub_property: Union[Unset, str] = UNSET - type: Union[Unset, AnotherAllOfSubModelType] = UNSET + type_: Union[Unset, AnotherAllOfSubModelType] = UNSET type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] = UNSET another_sub_property: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_sub_property = self.a_sub_property - type: Union[Unset, str] = UNSET - if not isinstance(self.type, Unset): - type = self.type.value + type_: Union[Unset, str] = UNSET + if not isinstance(self.type_, Unset): + type_ = self.type_.value type_enum: Union[Unset, int] = UNSET if not isinstance(self.type_enum, Unset): @@ -39,13 +39,13 @@ def to_dict(self) -> Dict[str, Any]: another_sub_property = self.another_sub_property - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a_sub_property is not UNSET: field_dict["a_sub_property"] = a_sub_property - if type is not UNSET: - field_dict["type"] = type + if type_ is not UNSET: + field_dict["type"] = type_ if type_enum is not UNSET: field_dict["type_enum"] = type_enum if another_sub_property is not UNSET: @@ -54,16 +54,16 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_sub_property = d.pop("a_sub_property", UNSET) - _type = d.pop("type", UNSET) - type: Union[Unset, AnotherAllOfSubModelType] - if isinstance(_type, Unset): - type = UNSET + _type_ = d.pop("type", UNSET) + type_: Union[Unset, AnotherAllOfSubModelType] + if isinstance(_type_, Unset): + type_ = UNSET else: - type = AnotherAllOfSubModelType(_type) + type_ = AnotherAllOfSubModelType(_type_) _type_enum = d.pop("type_enum", UNSET) type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] @@ -76,7 +76,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: model_from_all_of = cls( a_sub_property=a_sub_property, - type=type, + type_=type_, type_enum=type_enum, another_sub_property=another_sub_property, ) @@ -85,7 +85,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_from_all_of @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_name.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_name.py index 2a86db3a2..6f4eefc36 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_name.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_name.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,16 +10,16 @@ class ModelName: """ """ - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() model_name = cls() @@ -27,7 +27,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_name @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_reference_with_periods.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_reference_with_periods.py index a5ff5d211..004ed9b20 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_reference_with_periods.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_reference_with_periods.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,16 +10,16 @@ class ModelReferenceWithPeriods: """A Model with periods in its reference""" - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() model_reference_with_periods = cls() @@ -27,7 +27,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_reference_with_periods @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py index 761a43e54..a0cdb4f6a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -22,14 +22,14 @@ class ModelWithAdditionalPropertiesInlined: """ a_number: Union[Unset, float] = UNSET - additional_properties: Dict[str, "ModelWithAdditionalPropertiesInlinedAdditionalProperty"] = _attrs_field( + additional_properties: dict[str, "ModelWithAdditionalPropertiesInlinedAdditionalProperty"] = _attrs_field( init=False, factory=dict ) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_number = self.a_number - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() field_dict.update({}) @@ -39,7 +39,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_with_additional_properties_inlined_additional_property import ( ModelWithAdditionalPropertiesInlinedAdditionalProperty, ) @@ -61,7 +61,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_additional_properties_inlined @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperty": diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py index e06a94bfc..3bfffaf2c 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class ModelWithAdditionalPropertiesInlinedAdditionalProperty: """ extra_props_prop: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: extra_props_prop = self.extra_props_prop - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if extra_props_prop is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() extra_props_prop = d.pop("extra_props_prop", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_additional_properties_inlined_additional_property @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py index b2500f68c..0b80a0076 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -12,17 +12,17 @@ class ModelWithAdditionalPropertiesRefed: """ """ - additional_properties: Dict[str, AnEnum] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, AnEnum] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.value return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() model_with_additional_properties_refed = cls() @@ -36,7 +36,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_additional_properties_refed @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> AnEnum: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py index 6e669914a..deb8aeea5 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py @@ -1,12 +1,10 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field if TYPE_CHECKING: - from ..models.model_with_any_json_properties_additional_property_type_0 import ( - ModelWithAnyJsonPropertiesAdditionalPropertyType0, - ) + from ..models.model_with_any_json_properties_additional_property import ModelWithAnyJsonPropertiesAdditionalProperty T = TypeVar("T", bound="ModelWithAnyJsonProperties") @@ -16,18 +14,18 @@ class ModelWithAnyJsonProperties: """ """ - additional_properties: Dict[ - str, Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", List[str], bool, float, int, str] + additional_properties: dict[ + str, Union["ModelWithAnyJsonPropertiesAdditionalProperty", bool, float, int, list[str], str] ] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - from ..models.model_with_any_json_properties_additional_property_type_0 import ( - ModelWithAnyJsonPropertiesAdditionalPropertyType0, + def to_dict(self) -> dict[str, Any]: + from ..models.model_with_any_json_properties_additional_property import ( + ModelWithAnyJsonPropertiesAdditionalProperty, ) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): - if isinstance(prop, ModelWithAnyJsonPropertiesAdditionalPropertyType0): + if isinstance(prop, ModelWithAnyJsonPropertiesAdditionalProperty): field_dict[prop_name] = prop.to_dict() elif isinstance(prop, list): field_dict[prop_name] = prop @@ -38,9 +36,9 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: - from ..models.model_with_any_json_properties_additional_property_type_0 import ( - ModelWithAnyJsonPropertiesAdditionalPropertyType0, + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: + from ..models.model_with_any_json_properties_additional_property import ( + ModelWithAnyJsonPropertiesAdditionalProperty, ) d = src_dict.copy() @@ -51,25 +49,25 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: def _parse_additional_property( data: object, - ) -> Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", List[str], bool, float, int, str]: + ) -> Union["ModelWithAnyJsonPropertiesAdditionalProperty", bool, float, int, list[str], str]: try: if not isinstance(data, dict): raise TypeError() - additional_property_type_0 = ModelWithAnyJsonPropertiesAdditionalPropertyType0.from_dict(data) + additional_property = ModelWithAnyJsonPropertiesAdditionalProperty.from_dict(data) - return additional_property_type_0 + return additional_property except: # noqa: E722 pass try: if not isinstance(data, list): raise TypeError() - additional_property_type_1 = cast(List[str], data) + additional_property_type_1 = cast(list[str], data) return additional_property_type_1 except: # noqa: E722 pass return cast( - Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", List[str], bool, float, int, str], data + Union["ModelWithAnyJsonPropertiesAdditionalProperty", bool, float, int, list[str], str], data ) additional_property = _parse_additional_property(prop_dict) @@ -80,18 +78,16 @@ def _parse_additional_property( return model_with_any_json_properties @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__( self, key: str - ) -> Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", List[str], bool, float, int, str]: + ) -> Union["ModelWithAnyJsonPropertiesAdditionalProperty", bool, float, int, list[str], str]: return self.additional_properties[key] def __setitem__( - self, - key: str, - value: Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", List[str], bool, float, int, str], + self, key: str, value: Union["ModelWithAnyJsonPropertiesAdditionalProperty", bool, float, int, list[str], str] ) -> None: self.additional_properties[key] = value diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property_type_0.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property.py similarity index 60% rename from end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property_type_0.py rename to end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property.py index 6ae70905e..e7e244eb7 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property_type_0.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property.py @@ -1,33 +1,33 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field -T = TypeVar("T", bound="ModelWithAnyJsonPropertiesAdditionalPropertyType0") +T = TypeVar("T", bound="ModelWithAnyJsonPropertiesAdditionalProperty") @_attrs_define -class ModelWithAnyJsonPropertiesAdditionalPropertyType0: +class ModelWithAnyJsonPropertiesAdditionalProperty: """ """ - additional_properties: Dict[str, str] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - model_with_any_json_properties_additional_property_type_0 = cls() + model_with_any_json_properties_additional_property = cls() - model_with_any_json_properties_additional_property_type_0.additional_properties = d - return model_with_any_json_properties_additional_property_type_0 + model_with_any_json_properties_additional_property.additional_properties = d + return model_with_any_json_properties_additional_property @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> str: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_backslash_in_description.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_backslash_in_description.py index 5de43ddb9..0b5dfc3b5 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_backslash_in_description.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_backslash_in_description.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -12,16 +12,16 @@ class ModelWithBackslashInDescription: """ - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() model_with_backslash_in_description = cls() @@ -29,7 +29,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_backslash_in_description @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_a.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_a.py index 73cfb1287..3253c520b 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_a.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_a.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -20,14 +20,14 @@ class ModelWithCircularRefA: """ circular: Union[Unset, "ModelWithCircularRefB"] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - circular: Union[Unset, Dict[str, Any]] = UNSET + def to_dict(self) -> dict[str, Any]: + circular: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.circular, Unset): circular = self.circular.to_dict() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if circular is not UNSET: @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_with_circular_ref_b import ModelWithCircularRefB d = src_dict.copy() @@ -55,7 +55,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_circular_ref_a @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_b.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_b.py index 0628d89ae..89c3a064c 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_b.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_b.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -20,14 +20,14 @@ class ModelWithCircularRefB: """ circular: Union[Unset, "ModelWithCircularRefA"] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - circular: Union[Unset, Dict[str, Any]] = UNSET + def to_dict(self) -> dict[str, Any]: + circular: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.circular, Unset): circular = self.circular.to_dict() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if circular is not UNSET: @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_with_circular_ref_a import ModelWithCircularRefA d = src_dict.copy() @@ -55,7 +55,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_circular_ref_b @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_a.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_a.py index 4f1d59c57..32cb687c7 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_a.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_a.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,19 +14,19 @@ class ModelWithCircularRefInAdditionalPropertiesA: """ """ - additional_properties: Dict[str, "ModelWithCircularRefInAdditionalPropertiesB"] = _attrs_field( + additional_properties: dict[str, "ModelWithCircularRefInAdditionalPropertiesB"] = _attrs_field( init=False, factory=dict ) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_with_circular_ref_in_additional_properties_b import ( ModelWithCircularRefInAdditionalPropertiesB, ) @@ -44,7 +44,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_circular_ref_in_additional_properties_a @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> "ModelWithCircularRefInAdditionalPropertiesB": diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_b.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_b.py index 3f55584e5..d134a94b9 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_b.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_circular_ref_in_additional_properties_b.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,19 +14,19 @@ class ModelWithCircularRefInAdditionalPropertiesB: """ """ - additional_properties: Dict[str, "ModelWithCircularRefInAdditionalPropertiesA"] = _attrs_field( + additional_properties: dict[str, "ModelWithCircularRefInAdditionalPropertiesA"] = _attrs_field( init=False, factory=dict ) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_with_circular_ref_in_additional_properties_a import ( ModelWithCircularRefInAdditionalPropertiesA, ) @@ -44,7 +44,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_circular_ref_in_additional_properties_b @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> "ModelWithCircularRefInAdditionalPropertiesA": diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_date_time_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_date_time_property.py index 658b2352d..f503af00a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_date_time_property.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_date_time_property.py @@ -1,5 +1,5 @@ import datetime -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,14 +18,14 @@ class ModelWithDateTimeProperty: """ datetime_: Union[Unset, datetime.datetime] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: datetime_: Union[Unset, str] = UNSET if not isinstance(self.datetime_, Unset): datetime_ = self.datetime_.isoformat() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if datetime_ is not UNSET: @@ -34,7 +34,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() _datetime_ = d.pop("datetime", UNSET) datetime_: Union[Unset, datetime.datetime] @@ -51,7 +51,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_date_time_property @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_discriminated_union.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_discriminated_union.py deleted file mode 100644 index e03a6e698..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_discriminated_union.py +++ /dev/null @@ -1,103 +0,0 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast - -from attrs import define as _attrs_define -from attrs import field as _attrs_field - -from ..types import UNSET, Unset - -if TYPE_CHECKING: - from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1 - from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2 - - -T = TypeVar("T", bound="ModelWithDiscriminatedUnion") - - -@_attrs_define -class ModelWithDiscriminatedUnion: - """ - Attributes: - discriminated_union (Union['ADiscriminatedUnionType1', 'ADiscriminatedUnionType2', None, Unset]): - """ - - discriminated_union: Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1 - from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2 - - discriminated_union: Union[Dict[str, Any], None, Unset] - if isinstance(self.discriminated_union, Unset): - discriminated_union = UNSET - elif isinstance(self.discriminated_union, ADiscriminatedUnionType1): - discriminated_union = self.discriminated_union.to_dict() - elif isinstance(self.discriminated_union, ADiscriminatedUnionType2): - discriminated_union = self.discriminated_union.to_dict() - else: - discriminated_union = self.discriminated_union - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if discriminated_union is not UNSET: - field_dict["discriminated_union"] = discriminated_union - - return field_dict - - @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: - from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1 - from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2 - - d = src_dict.copy() - - def _parse_discriminated_union( - data: object, - ) -> Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset]: - if data is None: - return data - if isinstance(data, Unset): - return data - try: - if not isinstance(data, dict): - raise TypeError() - componentsschemas_a_discriminated_union_type_0 = ADiscriminatedUnionType1.from_dict(data) - - return componentsschemas_a_discriminated_union_type_0 - except: # noqa: E722 - pass - try: - if not isinstance(data, dict): - raise TypeError() - componentsschemas_a_discriminated_union_type_1 = ADiscriminatedUnionType2.from_dict(data) - - return componentsschemas_a_discriminated_union_type_1 - except: # noqa: E722 - pass - return cast(Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset], data) - - discriminated_union = _parse_discriminated_union(d.pop("discriminated_union", UNSET)) - - model_with_discriminated_union = cls( - discriminated_union=discriminated_union, - ) - - model_with_discriminated_union.additional_properties = d - return model_with_discriminated_union - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties.py index bcf1efa88..765d107d8 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties.py @@ -1,5 +1,5 @@ import datetime -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -28,9 +28,9 @@ class ModelWithMergedProperties: string_to_date: Union[Unset, datetime.date] = UNSET number_to_int: Union[Unset, int] = UNSET any_to_string: Union[Unset, str] = "x" - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: simple_string = self.simple_string string_to_enum: Union[Unset, str] = UNSET @@ -45,7 +45,7 @@ def to_dict(self) -> Dict[str, Any]: any_to_string = self.any_to_string - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if simple_string is not UNSET: @@ -62,7 +62,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() simple_string = d.pop("simpleString", UNSET) @@ -96,7 +96,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_merged_properties @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_no_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_no_properties.py index 506239f32..24e718e6d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_no_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_no_properties.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define @@ -9,13 +9,13 @@ class ModelWithNoProperties: """ """ - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: model_with_no_properties = cls() return model_with_no_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py index 94afa7653..db13972e9 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -22,14 +22,14 @@ class ModelWithPrimitiveAdditionalProperties: """ a_date_holder: Union[Unset, "ModelWithPrimitiveAdditionalPropertiesADateHolder"] = UNSET - additional_properties: Dict[str, str] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - a_date_holder: Union[Unset, Dict[str, Any]] = UNSET + def to_dict(self) -> dict[str, Any]: + a_date_holder: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.a_date_holder, Unset): a_date_holder = self.a_date_holder.to_dict() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a_date_holder is not UNSET: @@ -38,7 +38,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_with_primitive_additional_properties_a_date_holder import ( ModelWithPrimitiveAdditionalPropertiesADateHolder, ) @@ -59,7 +59,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_primitive_additional_properties @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> str: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py index b9920fc60..f53f968ac 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py @@ -1,5 +1,5 @@ import datetime -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -12,17 +12,17 @@ class ModelWithPrimitiveAdditionalPropertiesADateHolder: """ """ - additional_properties: Dict[str, datetime.datetime] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, datetime.datetime] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.isoformat() return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() model_with_primitive_additional_properties_a_date_holder = cls() @@ -36,7 +36,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_primitive_additional_properties_a_date_holder @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> datetime.datetime: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py index f54afdee8..d8ef017e0 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -20,14 +20,14 @@ class ModelWithPropertyRef: """ inner: Union[Unset, "ModelName"] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - inner: Union[Unset, Dict[str, Any]] = UNSET + def to_dict(self) -> dict[str, Any]: + inner: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.inner, Unset): inner = self.inner.to_dict() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if inner is not UNSET: @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_name import ModelName d = src_dict.copy() @@ -55,7 +55,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_property_ref @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref.py index 578bca7e0..f7370d6f1 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,14 +16,14 @@ class ModelWithRecursiveRef: """ recursive: Union[Unset, "ModelWithRecursiveRef"] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - recursive: Union[Unset, Dict[str, Any]] = UNSET + def to_dict(self) -> dict[str, Any]: + recursive: Union[Unset, dict[str, Any]] = UNSET if not isinstance(self.recursive, Unset): recursive = self.recursive.to_dict() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if recursive is not UNSET: @@ -32,7 +32,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() _recursive = d.pop("recursive", UNSET) recursive: Union[Unset, ModelWithRecursiveRef] @@ -49,7 +49,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_recursive_ref @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref_in_additional_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref_in_additional_properties.py index 2ed2526f5..961b82697 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref_in_additional_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_recursive_ref_in_additional_properties.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,19 +10,19 @@ class ModelWithRecursiveRefInAdditionalProperties: """ """ - additional_properties: Dict[str, "ModelWithRecursiveRefInAdditionalProperties"] = _attrs_field( + additional_properties: dict[str, "ModelWithRecursiveRefInAdditionalProperties"] = _attrs_field( init=False, factory=dict ) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() model_with_recursive_ref_in_additional_properties = cls() @@ -36,7 +36,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_recursive_ref_in_additional_properties @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> "ModelWithRecursiveRefInAdditionalProperties": diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py index 890010b78..e818fc69b 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define @@ -18,7 +18,7 @@ class ModelWithUnionProperty: a_property: Union[AnEnum, AnIntEnum, Unset] = UNSET - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_property: Union[Unset, int, str] if isinstance(self.a_property, Unset): a_property = UNSET @@ -27,7 +27,7 @@ def to_dict(self) -> Dict[str, Any]: else: a_property = self.a_property.value - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property @@ -35,7 +35,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py index 2a832e21a..659496eb6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union from attrs import define as _attrs_define @@ -21,10 +21,10 @@ class ModelWithUnionPropertyInlined: fruit: Union["ModelWithUnionPropertyInlinedFruitType0", "ModelWithUnionPropertyInlinedFruitType1", Unset] = UNSET - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: from ..models.model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0 - fruit: Union[Dict[str, Any], Unset] + fruit: Union[Unset, dict[str, Any]] if isinstance(self.fruit, Unset): fruit = UNSET elif isinstance(self.fruit, ModelWithUnionPropertyInlinedFruitType0): @@ -32,7 +32,7 @@ def to_dict(self) -> Dict[str, Any]: else: fruit = self.fruit.to_dict() - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update({}) if fruit is not UNSET: field_dict["fruit"] = fruit @@ -40,7 +40,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0 from ..models.model_with_union_property_inlined_fruit_type_1 import ModelWithUnionPropertyInlinedFruitType1 diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_0.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_0.py index b0f25360a..4678b4cef 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_0.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_0.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class ModelWithUnionPropertyInlinedFruitType0: """ apples: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: apples = self.apples - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if apples is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() apples = d.pop("apples", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_union_property_inlined_fruit_type_0 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_1.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_1.py index 1a32f2445..d70e54234 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_1.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_1.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class ModelWithUnionPropertyInlinedFruitType1: """ bananas: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: bananas = self.bananas - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if bananas is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() bananas = d.pop("bananas", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return model_with_union_property_inlined_fruit_type_1 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/none.py b/end_to_end_tests/golden-record/my_test_api_client/models/none.py index 3510497bf..23cb7d679 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/none.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/none.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,16 +10,16 @@ class None_: """ """ - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() none = cls() @@ -27,7 +27,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return none @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_data_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_data_body.py index adc78cd6f..ba36efef2 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_data_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_data_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class PostBodiesMultipleDataBody: """ a: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a = self.a - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a = d.pop("a", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_bodies_multiple_data_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_files_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_files_body.py index c81dc7636..188abf39e 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_files_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_files_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class PostBodiesMultipleFilesBody: """ a: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a = self.a - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a is not UNSET: @@ -29,10 +29,10 @@ def to_dict(self) -> Dict[str, Any]: return field_dict - def to_multipart(self) -> Dict[str, Any]: + def to_multipart(self) -> dict[str, Any]: a = self.a if isinstance(self.a, Unset) else (None, str(self.a).encode(), "text/plain") - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = (None, str(prop).encode(), "text/plain") @@ -43,7 +43,7 @@ def to_multipart(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a = d.pop("a", UNSET) @@ -55,7 +55,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_bodies_multiple_files_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_json_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_json_body.py index 88e5ec6f9..f4e7ffaa6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_json_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_bodies_multiple_json_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,12 +16,12 @@ class PostBodiesMultipleJsonBody: """ a: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a = self.a - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if a is not UNSET: @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a = d.pop("a", UNSET) @@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_bodies_multiple_json_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_body.py index 08a7bbc3a..07c3b2648 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,14 +18,14 @@ class PostFormDataInlineBody: a_required_field: str an_optional_field: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_required_field = self.a_required_field an_optional_field = self.an_optional_field - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -38,7 +38,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_required_field = d.pop("a_required_field") @@ -53,7 +53,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_form_data_inline_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_body.py index ed2f8efa1..100e84dc9 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,14 +18,14 @@ class PostNamingPropertyConflictWithImportBody: field: Union[Unset, str] = UNSET define: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: field = self.field define = self.define - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if field is not UNSET: @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() field = d.pop("Field", UNSET) @@ -51,7 +51,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_naming_property_conflict_with_import_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_response_200.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_response_200.py index 9bdd79a02..91c550749 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_response_200.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_naming_property_conflict_with_import_response_200.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,14 +18,14 @@ class PostNamingPropertyConflictWithImportResponse200: field: Union[Unset, str] = UNSET define: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: field = self.field define = self.define - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if field is not UNSET: @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() field = d.pop("Field", UNSET) @@ -51,7 +51,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_naming_property_conflict_with_import_response_200 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200.py index 0b6a29243..51f6dade2 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200.py @@ -1,11 +1,11 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field if TYPE_CHECKING: - from ..models.post_responses_unions_simple_before_complex_response_200a_type_1 import ( - PostResponsesUnionsSimpleBeforeComplexResponse200AType1, + from ..models.post_responses_unions_simple_before_complex_response_200a import ( + PostResponsesUnionsSimpleBeforeComplexResponse200A, ) @@ -16,24 +16,24 @@ class PostResponsesUnionsSimpleBeforeComplexResponse200: """ Attributes: - a (Union['PostResponsesUnionsSimpleBeforeComplexResponse200AType1', str]): + a (Union['PostResponsesUnionsSimpleBeforeComplexResponse200A', str]): """ - a: Union["PostResponsesUnionsSimpleBeforeComplexResponse200AType1", str] - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + a: Union["PostResponsesUnionsSimpleBeforeComplexResponse200A", str] + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - from ..models.post_responses_unions_simple_before_complex_response_200a_type_1 import ( - PostResponsesUnionsSimpleBeforeComplexResponse200AType1, + def to_dict(self) -> dict[str, Any]: + from ..models.post_responses_unions_simple_before_complex_response_200a import ( + PostResponsesUnionsSimpleBeforeComplexResponse200A, ) - a: Union[Dict[str, Any], str] - if isinstance(self.a, PostResponsesUnionsSimpleBeforeComplexResponse200AType1): + a: Union[dict[str, Any], str] + if isinstance(self.a, PostResponsesUnionsSimpleBeforeComplexResponse200A): a = self.a.to_dict() else: a = self.a - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -44,23 +44,23 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: - from ..models.post_responses_unions_simple_before_complex_response_200a_type_1 import ( - PostResponsesUnionsSimpleBeforeComplexResponse200AType1, + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: + from ..models.post_responses_unions_simple_before_complex_response_200a import ( + PostResponsesUnionsSimpleBeforeComplexResponse200A, ) d = src_dict.copy() - def _parse_a(data: object) -> Union["PostResponsesUnionsSimpleBeforeComplexResponse200AType1", str]: + def _parse_a(data: object) -> Union["PostResponsesUnionsSimpleBeforeComplexResponse200A", str]: try: if not isinstance(data, dict): raise TypeError() - a_type_1 = PostResponsesUnionsSimpleBeforeComplexResponse200AType1.from_dict(data) + a = PostResponsesUnionsSimpleBeforeComplexResponse200A.from_dict(data) - return a_type_1 + return a except: # noqa: E722 pass - return cast(Union["PostResponsesUnionsSimpleBeforeComplexResponse200AType1", str], data) + return cast(Union["PostResponsesUnionsSimpleBeforeComplexResponse200A", str], data) a = _parse_a(d.pop("a")) @@ -72,7 +72,7 @@ def _parse_a(data: object) -> Union["PostResponsesUnionsSimpleBeforeComplexRespo return post_responses_unions_simple_before_complex_response_200 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200a_type_1.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200a.py similarity index 68% rename from end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200a_type_1.py rename to end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200a.py index 601d17cf8..be97429f5 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200a_type_1.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_responses_unions_simple_before_complex_response_200a.py @@ -1,33 +1,33 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field -T = TypeVar("T", bound="PostResponsesUnionsSimpleBeforeComplexResponse200AType1") +T = TypeVar("T", bound="PostResponsesUnionsSimpleBeforeComplexResponse200A") @_attrs_define -class PostResponsesUnionsSimpleBeforeComplexResponse200AType1: +class PostResponsesUnionsSimpleBeforeComplexResponse200A: """ """ - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - field_dict: Dict[str, Any] = {} + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - post_responses_unions_simple_before_complex_response_200a_type_1 = cls() + post_responses_unions_simple_before_complex_response_200a = cls() - post_responses_unions_simple_before_complex_response_200a_type_1.additional_properties = d - return post_responses_unions_simple_before_complex_response_200a_type_1 + post_responses_unions_simple_before_complex_response_200a.additional_properties = d + return post_responses_unions_simple_before_complex_response_200a @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_body.py index 8c1843b41..721011eed 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define @@ -16,10 +16,10 @@ class TestInlineObjectsBody: a_property: Union[Unset, str] = UNSET - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_property = self.a_property - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property @@ -27,7 +27,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_property = d.pop("a_property", UNSET) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py index 6a0ade77f..2cf353587 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define @@ -16,10 +16,10 @@ class TestInlineObjectsResponse200: a_property: Union[Unset, str] = UNSET - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_property = self.a_property - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property @@ -27,7 +27,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_property = d.pop("a_property", UNSET) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py b/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py index 6ff5d4790..112808e62 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, cast +from typing import Any, TypeVar, cast from attrs import define as _attrs_define @@ -9,46 +9,46 @@ class ValidationError: """ Attributes: - loc (List[str]): + loc (list[str]): msg (str): - type (str): + type_ (str): """ - loc: List[str] + loc: list[str] msg: str - type: str + type_: str - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: loc = self.loc msg = self.msg - type = self.type + type_ = self.type_ - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update( { "loc": loc, "msg": msg, - "type": type, + "type": type_, } ) return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - loc = cast(List[str], d.pop("loc")) + loc = cast(list[str], d.pop("loc")) msg = d.pop("msg") - type = d.pop("type") + type_ = d.pop("type") validation_error = cls( loc=loc, msg=msg, - type=type, + type_=type_, ) return validation_error diff --git a/end_to_end_tests/golden-record/my_test_api_client/types.py b/end_to_end_tests/golden-record/my_test_api_client/types.py index 21fac106f..b9ed58b8a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/types.py +++ b/end_to_end_tests/golden-record/my_test_api_client/types.py @@ -1,7 +1,8 @@ """Contains some shared types for properties""" +from collections.abc import MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, MutableMapping, Optional, Tuple, TypeVar +from typing import BinaryIO, Generic, Literal, Optional, TypeVar from attrs import define @@ -13,7 +14,7 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]] +FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] @define @@ -42,4 +43,4 @@ class Response(Generic[T]): parsed: Optional[T] -__all__ = ["File", "Response", "FileJsonType", "Unset", "UNSET"] +__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] diff --git a/end_to_end_tests/golden-record/pyproject.toml b/end_to_end_tests/golden-record/pyproject.toml index 526beacf6..feca06dbd 100644 --- a/end_to_end_tests/golden-record/pyproject.toml +++ b/end_to_end_tests/golden-record/pyproject.toml @@ -11,9 +11,9 @@ include = ["CHANGELOG.md", "my_test_api_client/py.typed"] [tool.poetry.dependencies] -python = "^3.8" -httpx = ">=0.20.0,<0.28.0" -attrs = ">=21.3.0" +python = "^3.9" +httpx = ">=0.20.0,<0.29.0" +attrs = ">=22.2.0" python-dateutil = "^2.8.0" [build-system] diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/bool_enum_tests_bool_enum_post.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/bool_enum_tests_bool_enum_post.py index 851cdf385..52385855c 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/bool_enum_tests_bool_enum_post.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/bool_enum_tests_bool_enum_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -11,14 +11,14 @@ def _get_kwargs( *, bool_enum: bool, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} params["bool_enum"] = bool_enum params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/enum/bool", "params": params, diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/int_enum_tests_int_enum_post.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/int_enum_tests_int_enum_post.py index 5f9f7f8e5..af4c4ca22 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/int_enum_tests_int_enum_post.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/enums/int_enum_tests_int_enum_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -12,15 +12,15 @@ def _get_kwargs( *, int_enum: AnIntEnum, -) -> Dict[str, Any]: - params: Dict[str, Any] = {} +) -> dict[str, Any]: + params: dict[str, Any] = {} json_int_enum: int = int_enum params["int_enum"] = json_int_enum params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/enum/int", "params": params, diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/get_user_list.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/get_user_list.py index ab60a4610..00bc801d9 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/get_user_list.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/get_user_list.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union import httpx @@ -17,20 +17,20 @@ def _get_kwargs( *, - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], int_enum_header: Union[Unset, GetUserListIntEnumHeader] = UNSET, string_enum_header: Union[Unset, GetUserListStringEnumHeader] = UNSET, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} if not isinstance(int_enum_header, Unset): headers["Int-Enum-Header"] = str(int_enum_header) if not isinstance(string_enum_header, Unset): headers["String-Enum-Header"] = str(string_enum_header) - params: Dict[str, Any] = {} + params: dict[str, Any] = {} json_an_enum_value = [] for an_enum_value_item_data in an_enum_value: @@ -56,7 +56,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/", "params": params, @@ -68,7 +68,7 @@ def _get_kwargs( def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[List["AModel"]]: +) -> Optional[list["AModel"]]: if response.status_code == 200: response_200 = [] _response_200 = response.json() @@ -86,7 +86,7 @@ def _parse_response( def _build_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[List["AModel"]]: +) -> Response[list["AModel"]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -98,20 +98,20 @@ def _build_response( def sync_detailed( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], int_enum_header: Union[Unset, GetUserListIntEnumHeader] = UNSET, string_enum_header: Union[Unset, GetUserListStringEnumHeader] = UNSET, -) -> Response[List["AModel"]]: +) -> Response[list["AModel"]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): int_enum_header (Union[Unset, GetUserListIntEnumHeader]): string_enum_header (Union[Unset, GetUserListStringEnumHeader]): @@ -120,7 +120,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['AModel']] + Response[list['AModel']] """ kwargs = _get_kwargs( @@ -141,20 +141,20 @@ def sync_detailed( def sync( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], int_enum_header: Union[Unset, GetUserListIntEnumHeader] = UNSET, string_enum_header: Union[Unset, GetUserListStringEnumHeader] = UNSET, -) -> Optional[List["AModel"]]: +) -> Optional[list["AModel"]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): int_enum_header (Union[Unset, GetUserListIntEnumHeader]): string_enum_header (Union[Unset, GetUserListStringEnumHeader]): @@ -163,7 +163,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['AModel'] + list['AModel'] """ return sync_detailed( @@ -179,20 +179,20 @@ def sync( async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], int_enum_header: Union[Unset, GetUserListIntEnumHeader] = UNSET, string_enum_header: Union[Unset, GetUserListStringEnumHeader] = UNSET, -) -> Response[List["AModel"]]: +) -> Response[list["AModel"]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): int_enum_header (Union[Unset, GetUserListIntEnumHeader]): string_enum_header (Union[Unset, GetUserListStringEnumHeader]): @@ -201,7 +201,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['AModel']] + Response[list['AModel']] """ kwargs = _get_kwargs( @@ -220,20 +220,20 @@ async def asyncio_detailed( async def asyncio( *, client: Union[AuthenticatedClient, Client], - an_enum_value: List[AnEnum], - an_enum_value_with_null: List[Union[AnEnumWithNull, None]], - an_enum_value_with_only_null: List[None], + an_enum_value: list[AnEnum], + an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_only_null: list[None], int_enum_header: Union[Unset, GetUserListIntEnumHeader] = UNSET, string_enum_header: Union[Unset, GetUserListStringEnumHeader] = UNSET, -) -> Optional[List["AModel"]]: +) -> Optional[list["AModel"]]: """Get List Get a list of things Args: - an_enum_value (List[AnEnum]): - an_enum_value_with_null (List[Union[AnEnumWithNull, None]]): - an_enum_value_with_only_null (List[None]): + an_enum_value (list[AnEnum]): + an_enum_value_with_null (list[Union[AnEnumWithNull, None]]): + an_enum_value_with_only_null (list[None]): int_enum_header (Union[Unset, GetUserListIntEnumHeader]): string_enum_header (Union[Unset, GetUserListStringEnumHeader]): @@ -242,7 +242,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['AModel'] + list['AModel'] """ return ( diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/post_user_list.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/post_user_list.py index 3cbdeddf8..82df1a8f8 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/post_user_list.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/api/tests/post_user_list.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union import httpx @@ -13,10 +13,10 @@ def _get_kwargs( *, body: PostUserListBody, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/tests/", } @@ -31,7 +31,7 @@ def _get_kwargs( def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[List["AModel"]]: +) -> Optional[list["AModel"]]: if response.status_code == 200: response_200 = [] _response_200 = response.json() @@ -49,7 +49,7 @@ def _parse_response( def _build_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[List["AModel"]]: +) -> Response[list["AModel"]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -62,7 +62,7 @@ def sync_detailed( *, client: Union[AuthenticatedClient, Client], body: PostUserListBody, -) -> Response[List["AModel"]]: +) -> Response[list["AModel"]]: """Post List Post a list of things @@ -75,7 +75,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['AModel']] + Response[list['AModel']] """ kwargs = _get_kwargs( @@ -93,7 +93,7 @@ def sync( *, client: Union[AuthenticatedClient, Client], body: PostUserListBody, -) -> Optional[List["AModel"]]: +) -> Optional[list["AModel"]]: """Post List Post a list of things @@ -106,7 +106,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['AModel'] + list['AModel'] """ return sync_detailed( @@ -119,7 +119,7 @@ async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], body: PostUserListBody, -) -> Response[List["AModel"]]: +) -> Response[list["AModel"]]: """Post List Post a list of things @@ -132,7 +132,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['AModel']] + Response[list['AModel']] """ kwargs = _get_kwargs( @@ -148,7 +148,7 @@ async def asyncio( *, client: Union[AuthenticatedClient, Client], body: PostUserListBody, -) -> Optional[List["AModel"]]: +) -> Optional[list["AModel"]]: """Post List Post a list of things @@ -161,7 +161,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['AModel'] + list['AModel'] """ return ( diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/client.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/client.py index 0f6d15e84..e80446f10 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/client.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/client.py @@ -1,5 +1,5 @@ import ssl -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx from attrs import define, evolve, field @@ -36,16 +36,16 @@ class Client: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) - def with_headers(self, headers: Dict[str, str]) -> "Client": + def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -53,7 +53,7 @@ def with_headers(self, headers: Dict[str, str]) -> "Client": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "Client": + def with_cookies(self, cookies: dict[str, str]) -> "Client": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) @@ -166,12 +166,12 @@ class AuthenticatedClient: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) @@ -179,7 +179,7 @@ class AuthenticatedClient: prefix: str = "Bearer" auth_header_name: str = "Authorization" - def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": + def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -187,7 +187,7 @@ def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "AuthenticatedClient": + def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/a_model.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/a_model.py index e05fdaa6d..7050c1a3c 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/a_model.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/a_model.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define @@ -19,16 +19,16 @@ class AModel: an_allof_enum_with_overridden_default (AnAllOfEnum): Default: 'overridden_default'. any_value (Union[Unset, Any]): an_optional_allof_enum (Union[Unset, AnAllOfEnum]): - nested_list_of_enums (Union[Unset, List[List[DifferentEnum]]]): + nested_list_of_enums (Union[Unset, list[list[DifferentEnum]]]): """ an_enum_value: AnEnum an_allof_enum_with_overridden_default: AnAllOfEnum = "overridden_default" any_value: Union[Unset, Any] = UNSET an_optional_allof_enum: Union[Unset, AnAllOfEnum] = UNSET - nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET + nested_list_of_enums: Union[Unset, list[list[DifferentEnum]]] = UNSET - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: an_enum_value: str = self.an_enum_value an_allof_enum_with_overridden_default: str = self.an_allof_enum_with_overridden_default @@ -39,7 +39,7 @@ def to_dict(self) -> Dict[str, Any]: if not isinstance(self.an_optional_allof_enum, Unset): an_optional_allof_enum = self.an_optional_allof_enum - nested_list_of_enums: Union[Unset, List[List[str]]] = UNSET + nested_list_of_enums: Union[Unset, list[list[str]]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] for nested_list_of_enums_item_data in self.nested_list_of_enums: @@ -50,7 +50,7 @@ def to_dict(self) -> Dict[str, Any]: nested_list_of_enums.append(nested_list_of_enums_item) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update( { "an_enum_value": an_enum_value, @@ -67,7 +67,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() an_enum_value = check_an_enum(d.pop("an_enum_value")) diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_all_of_enum.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_all_of_enum.py index e238b15a9..3455e04d0 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_all_of_enum.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_all_of_enum.py @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast AnAllOfEnum = Literal["a_default", "bar", "foo", "overridden_default"] -AN_ALL_OF_ENUM_VALUES: Set[AnAllOfEnum] = { +AN_ALL_OF_ENUM_VALUES: set[AnAllOfEnum] = { "a_default", "bar", "foo", diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum.py index 608b22fc4..27b5c45f9 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum.py @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast AnEnum = Literal["FIRST_VALUE", "SECOND_VALUE"] -AN_ENUM_VALUES: Set[AnEnum] = { +AN_ENUM_VALUES: set[AnEnum] = { "FIRST_VALUE", "SECOND_VALUE", } diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum_with_null.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum_with_null.py index 1519ec27c..4203876de 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum_with_null.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_enum_with_null.py @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast AnEnumWithNull = Literal["FIRST_VALUE", "SECOND_VALUE"] -AN_ENUM_WITH_NULL_VALUES: Set[AnEnumWithNull] = { +AN_ENUM_WITH_NULL_VALUES: set[AnEnumWithNull] = { "FIRST_VALUE", "SECOND_VALUE", } diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_int_enum.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_int_enum.py index a3c1108ea..9d0abd942 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_int_enum.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/an_int_enum.py @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast AnIntEnum = Literal[-1, 1, 2] -AN_INT_ENUM_VALUES: Set[AnIntEnum] = { +AN_INT_ENUM_VALUES: set[AnIntEnum] = { -1, 1, 2, diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/different_enum.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/different_enum.py index d40045c50..e672a9821 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/different_enum.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/different_enum.py @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast DifferentEnum = Literal["DIFFERENT", "OTHER"] -DIFFERENT_ENUM_VALUES: Set[DifferentEnum] = { +DIFFERENT_ENUM_VALUES: set[DifferentEnum] = { "DIFFERENT", "OTHER", } diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_int_enum_header.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_int_enum_header.py index 50e8114ae..845d6c2a0 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_int_enum_header.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_int_enum_header.py @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast GetUserListIntEnumHeader = Literal[1, 2, 3] -GET_USER_LIST_INT_ENUM_HEADER_VALUES: Set[GetUserListIntEnumHeader] = { +GET_USER_LIST_INT_ENUM_HEADER_VALUES: set[GetUserListIntEnumHeader] = { 1, 2, 3, diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_string_enum_header.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_string_enum_header.py index d73cea6a6..55dbbad62 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_string_enum_header.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/get_user_list_string_enum_header.py @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast GetUserListStringEnumHeader = Literal["one", "three", "two"] -GET_USER_LIST_STRING_ENUM_HEADER_VALUES: Set[GetUserListStringEnumHeader] = { +GET_USER_LIST_STRING_ENUM_HEADER_VALUES: set[GetUserListStringEnumHeader] = { "one", "three", "two", diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/post_user_list_body.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/post_user_list_body.py index e61cb4183..6af995f66 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/post_user_list_body.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/models/post_user_list_body.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List, Tuple, Type, TypeVar, Union, cast +from typing import Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,31 +17,31 @@ class PostUserListBody: """ Attributes: - an_enum_value (Union[Unset, List[AnEnum]]): - an_enum_value_with_null (Union[Unset, List[Union[AnEnumWithNull, None]]]): - an_enum_value_with_only_null (Union[Unset, List[None]]): + an_enum_value (Union[Unset, list[AnEnum]]): + an_enum_value_with_null (Union[Unset, list[Union[AnEnumWithNull, None]]]): + an_enum_value_with_only_null (Union[Unset, list[None]]): an_allof_enum_with_overridden_default (Union[Unset, AnAllOfEnum]): Default: 'overridden_default'. an_optional_allof_enum (Union[Unset, AnAllOfEnum]): - nested_list_of_enums (Union[Unset, List[List[DifferentEnum]]]): + nested_list_of_enums (Union[Unset, list[list[DifferentEnum]]]): """ - an_enum_value: Union[Unset, List[AnEnum]] = UNSET - an_enum_value_with_null: Union[Unset, List[Union[AnEnumWithNull, None]]] = UNSET - an_enum_value_with_only_null: Union[Unset, List[None]] = UNSET + an_enum_value: Union[Unset, list[AnEnum]] = UNSET + an_enum_value_with_null: Union[Unset, list[Union[AnEnumWithNull, None]]] = UNSET + an_enum_value_with_only_null: Union[Unset, list[None]] = UNSET an_allof_enum_with_overridden_default: Union[Unset, AnAllOfEnum] = "overridden_default" an_optional_allof_enum: Union[Unset, AnAllOfEnum] = UNSET - nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + nested_list_of_enums: Union[Unset, list[list[DifferentEnum]]] = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - an_enum_value: Union[Unset, List[str]] = UNSET + def to_dict(self) -> dict[str, Any]: + an_enum_value: Union[Unset, list[str]] = UNSET if not isinstance(self.an_enum_value, Unset): an_enum_value = [] for an_enum_value_item_data in self.an_enum_value: an_enum_value_item: str = an_enum_value_item_data an_enum_value.append(an_enum_value_item) - an_enum_value_with_null: Union[Unset, List[Union[None, str]]] = UNSET + an_enum_value_with_null: Union[Unset, list[Union[None, str]]] = UNSET if not isinstance(self.an_enum_value_with_null, Unset): an_enum_value_with_null = [] for an_enum_value_with_null_item_data in self.an_enum_value_with_null: @@ -52,7 +52,7 @@ def to_dict(self) -> Dict[str, Any]: an_enum_value_with_null_item = an_enum_value_with_null_item_data an_enum_value_with_null.append(an_enum_value_with_null_item) - an_enum_value_with_only_null: Union[Unset, List[None]] = UNSET + an_enum_value_with_only_null: Union[Unset, list[None]] = UNSET if not isinstance(self.an_enum_value_with_only_null, Unset): an_enum_value_with_only_null = self.an_enum_value_with_only_null @@ -64,7 +64,7 @@ def to_dict(self) -> Dict[str, Any]: if not isinstance(self.an_optional_allof_enum, Unset): an_optional_allof_enum = self.an_optional_allof_enum - nested_list_of_enums: Union[Unset, List[List[str]]] = UNSET + nested_list_of_enums: Union[Unset, list[list[str]]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] for nested_list_of_enums_item_data in self.nested_list_of_enums: @@ -75,7 +75,7 @@ def to_dict(self) -> Dict[str, Any]: nested_list_of_enums.append(nested_list_of_enums_item) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if an_enum_value is not UNSET: @@ -93,8 +93,8 @@ def to_dict(self) -> Dict[str, Any]: return field_dict - def to_multipart(self) -> Dict[str, Any]: - an_enum_value: Union[Unset, Tuple[None, bytes, str]] = UNSET + def to_multipart(self) -> dict[str, Any]: + an_enum_value: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.an_enum_value, Unset): _temp_an_enum_value = [] for an_enum_value_item_data in self.an_enum_value: @@ -102,7 +102,7 @@ def to_multipart(self) -> Dict[str, Any]: _temp_an_enum_value.append(an_enum_value_item) an_enum_value = (None, json.dumps(_temp_an_enum_value).encode(), "application/json") - an_enum_value_with_null: Union[Unset, Tuple[None, bytes, str]] = UNSET + an_enum_value_with_null: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.an_enum_value_with_null, Unset): _temp_an_enum_value_with_null = [] for an_enum_value_with_null_item_data in self.an_enum_value_with_null: @@ -114,7 +114,7 @@ def to_multipart(self) -> Dict[str, Any]: _temp_an_enum_value_with_null.append(an_enum_value_with_null_item) an_enum_value_with_null = (None, json.dumps(_temp_an_enum_value_with_null).encode(), "application/json") - an_enum_value_with_only_null: Union[Unset, Tuple[None, bytes, str]] = UNSET + an_enum_value_with_only_null: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.an_enum_value_with_only_null, Unset): _temp_an_enum_value_with_only_null = self.an_enum_value_with_only_null an_enum_value_with_only_null = ( @@ -123,7 +123,7 @@ def to_multipart(self) -> Dict[str, Any]: "application/json", ) - an_allof_enum_with_overridden_default: Union[Unset, Tuple[None, bytes, str]] = UNSET + an_allof_enum_with_overridden_default: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.an_allof_enum_with_overridden_default, Unset): an_allof_enum_with_overridden_default = ( None, @@ -131,11 +131,11 @@ def to_multipart(self) -> Dict[str, Any]: "text/plain", ) - an_optional_allof_enum: Union[Unset, Tuple[None, bytes, str]] = UNSET + an_optional_allof_enum: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.an_optional_allof_enum, Unset): an_optional_allof_enum = (None, str(self.an_optional_allof_enum).encode(), "text/plain") - nested_list_of_enums: Union[Unset, Tuple[None, bytes, str]] = UNSET + nested_list_of_enums: Union[Unset, tuple[None, bytes, str]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): _temp_nested_list_of_enums = [] for nested_list_of_enums_item_data in self.nested_list_of_enums: @@ -147,7 +147,7 @@ def to_multipart(self) -> Dict[str, Any]: _temp_nested_list_of_enums.append(nested_list_of_enums_item) nested_list_of_enums = (None, json.dumps(_temp_nested_list_of_enums).encode(), "application/json") - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = (None, str(prop).encode(), "text/plain") @@ -168,7 +168,7 @@ def to_multipart(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() an_enum_value = [] _an_enum_value = d.pop("an_enum_value", UNSET) @@ -187,9 +187,9 @@ def _parse_an_enum_value_with_null_item(data: object) -> Union[AnEnumWithNull, N try: if not isinstance(data, str): raise TypeError() - componentsschemas_an_enum_with_null_type_1 = check_an_enum_with_null(data) + componentsschemas_an_enum_with_null = check_an_enum_with_null(data) - return componentsschemas_an_enum_with_null_type_1 + return componentsschemas_an_enum_with_null except: # noqa: E722 pass return cast(Union[AnEnumWithNull, None], data) @@ -198,7 +198,7 @@ def _parse_an_enum_value_with_null_item(data: object) -> Union[AnEnumWithNull, N an_enum_value_with_null.append(an_enum_value_with_null_item) - an_enum_value_with_only_null = cast(List[None], d.pop("an_enum_value_with_only_null", UNSET)) + an_enum_value_with_only_null = cast(list[None], d.pop("an_enum_value_with_only_null", UNSET)) _an_allof_enum_with_overridden_default = d.pop("an_allof_enum_with_overridden_default", UNSET) an_allof_enum_with_overridden_default: Union[Unset, AnAllOfEnum] @@ -239,7 +239,7 @@ def _parse_an_enum_value_with_null_item(data: object) -> Union[AnEnumWithNull, N return post_user_list_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/types.py b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/types.py index 21fac106f..b9ed58b8a 100644 --- a/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/types.py +++ b/end_to_end_tests/literal-enums-golden-record/my_enum_api_client/types.py @@ -1,7 +1,8 @@ """Contains some shared types for properties""" +from collections.abc import MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, MutableMapping, Optional, Tuple, TypeVar +from typing import BinaryIO, Generic, Literal, Optional, TypeVar from attrs import define @@ -13,7 +14,7 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]] +FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] @define @@ -42,4 +43,4 @@ class Response(Generic[T]): parsed: Optional[T] -__all__ = ["File", "Response", "FileJsonType", "Unset", "UNSET"] +__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] diff --git a/end_to_end_tests/literal-enums-golden-record/pyproject.toml b/end_to_end_tests/literal-enums-golden-record/pyproject.toml index d32c2d72c..2c4d6b4e3 100644 --- a/end_to_end_tests/literal-enums-golden-record/pyproject.toml +++ b/end_to_end_tests/literal-enums-golden-record/pyproject.toml @@ -11,9 +11,9 @@ include = ["CHANGELOG.md", "my_enum_api_client/py.typed"] [tool.poetry.dependencies] -python = "^3.8" -httpx = ">=0.20.0,<0.28.0" -attrs = ">=21.3.0" +python = "^3.9" +httpx = ">=0.20.0,<0.29.0" +attrs = ">=22.2.0" python-dateutil = "^2.8.0" [build-system] diff --git a/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml b/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml index fddcea97f..c1f8a2a2b 100644 --- a/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml +++ b/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml @@ -4,10 +4,10 @@ version = "0.1.0" description = "A client library for accessing Test 3.1 Features" authors = [] readme = "README.md" -requires-python = ">=3.8,<4.0" +requires-python = ">=3.9,<4.0" dependencies = [ - "httpx>=0.20.0,<0.28.0", - "attrs>=21.3.0", + "httpx>=0.20.0,<0.29.0", + "attrs>=22.2.0", "python-dateutil>=2.8.0", ] diff --git a/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml b/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml index f9a1becf8..2e8cd6c04 100644 --- a/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml +++ b/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml @@ -11,9 +11,9 @@ include = ["CHANGELOG.md", "test_3_1_features_client/py.typed"] [tool.poetry.dependencies] -python = "^3.8" -httpx = ">=0.20.0,<0.28.0" -attrs = ">=21.3.0" +python = "^3.9" +httpx = ">=0.20.0,<0.29.0" +attrs = ">=22.2.0" python-dateutil = "^2.8.0" [build-system] diff --git a/end_to_end_tests/metadata_snapshots/setup.py b/end_to_end_tests/metadata_snapshots/setup.py index 6350b8c4c..6c7a58b97 100644 --- a/end_to_end_tests/metadata_snapshots/setup.py +++ b/end_to_end_tests/metadata_snapshots/setup.py @@ -12,7 +12,7 @@ long_description=long_description, long_description_content_type="text/markdown", packages=find_packages(), - python_requires=">=3.8, <4", - install_requires=["httpx >= 0.20.0, < 0.28.0", "attrs >= 21.3.0", "python-dateutil >= 2.8.0, < 3"], + python_requires=">=3.9, <4", + install_requires=["httpx >= 0.20.0, < 0.29.0", "attrs >= 22.2.0", "python-dateutil >= 2.8.0, < 3"], package_data={"test_3_1_features_client": ["py.typed"]}, ) diff --git a/end_to_end_tests/test-3-1-golden-record/pyproject.toml b/end_to_end_tests/test-3-1-golden-record/pyproject.toml index f9a1becf8..2e8cd6c04 100644 --- a/end_to_end_tests/test-3-1-golden-record/pyproject.toml +++ b/end_to_end_tests/test-3-1-golden-record/pyproject.toml @@ -11,9 +11,9 @@ include = ["CHANGELOG.md", "test_3_1_features_client/py.typed"] [tool.poetry.dependencies] -python = "^3.8" -httpx = ">=0.20.0,<0.28.0" -attrs = ">=21.3.0" +python = "^3.9" +httpx = ">=0.20.0,<0.29.0" +attrs = ">=22.2.0" python-dateutil = "^2.8.0" [build-system] diff --git a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/const/post_const_path.py b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/const/post_const_path.py index 9a8181e26..0d05b8189 100644 --- a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/const/post_const_path.py +++ b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/const/post_const_path.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Literal, Optional, Union, cast +from typing import Any, Literal, Optional, Union, cast import httpx @@ -15,10 +15,10 @@ def _get_kwargs( body: PostConstPathBody, required_query: Literal["this always goes in the query"], optional_query: Union[Literal["this sometimes goes in the query"], Unset] = UNSET, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - params: Dict[str, Any] = {} + params: dict[str, Any] = {} params["required query"] = required_query @@ -26,7 +26,7 @@ def _get_kwargs( params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": f"/const/{path}", "params": params, diff --git a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/prefix_items/post_prefix_items.py b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/prefix_items/post_prefix_items.py index 03afbcb0e..f12d53f18 100644 --- a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/prefix_items/post_prefix_items.py +++ b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/api/prefix_items/post_prefix_items.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -12,10 +12,10 @@ def _get_kwargs( *, body: PostPrefixItemsBody, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/prefixItems", } diff --git a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/client.py b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/client.py index 0f6d15e84..e80446f10 100644 --- a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/client.py +++ b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/client.py @@ -1,5 +1,5 @@ import ssl -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx from attrs import define, evolve, field @@ -36,16 +36,16 @@ class Client: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) - def with_headers(self, headers: Dict[str, str]) -> "Client": + def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -53,7 +53,7 @@ def with_headers(self, headers: Dict[str, str]) -> "Client": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "Client": + def with_cookies(self, cookies: dict[str, str]) -> "Client": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) @@ -166,12 +166,12 @@ class AuthenticatedClient: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) @@ -179,7 +179,7 @@ class AuthenticatedClient: prefix: str = "Bearer" auth_header_name: str = "Authorization" - def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": + def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -187,7 +187,7 @@ def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "AuthenticatedClient": + def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) diff --git a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_const_path_body.py b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_const_path_body.py index 9ac2f9102..15734903c 100644 --- a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_const_path_body.py +++ b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_const_path_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Type, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -20,9 +20,9 @@ class PostConstPathBody: required: Literal["this always goes in the body"] nullable: Union[Literal["this or null goes in the body"], None] optional: Union[Literal["this sometimes goes in the body"], Unset] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: required = self.required nullable: Union[Literal["this or null goes in the body"], None] @@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]: optional = self.optional - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -44,7 +44,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() required = cast(Literal["this always goes in the body"], d.pop("required")) if required != "this always goes in the body": @@ -77,7 +77,7 @@ def _parse_nullable(data: object) -> Union[Literal["this or null goes in the bod return post_const_path_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_prefix_items_body.py b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_prefix_items_body.py index f3edd841d..a578b3091 100644 --- a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_prefix_items_body.py +++ b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/models/post_prefix_items_body.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Type, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -12,16 +12,16 @@ class PostPrefixItemsBody: """ Attributes: - prefix_items_and_items (Union[Unset, List[Union[Literal['prefix'], float, str]]]): - prefix_items_only (Union[Unset, List[Union[float, str]]]): + prefix_items_and_items (Union[Unset, list[Union[Literal['prefix'], float, str]]]): + prefix_items_only (Union[Unset, list[Union[float, str]]]): """ - prefix_items_and_items: Union[Unset, List[Union[Literal["prefix"], float, str]]] = UNSET - prefix_items_only: Union[Unset, List[Union[float, str]]] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + prefix_items_and_items: Union[Unset, list[Union[Literal["prefix"], float, str]]] = UNSET + prefix_items_only: Union[Unset, list[Union[float, str]]] = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - prefix_items_and_items: Union[Unset, List[Union[Literal["prefix"], float, str]]] = UNSET + def to_dict(self) -> dict[str, Any]: + prefix_items_and_items: Union[Unset, list[Union[Literal["prefix"], float, str]]] = UNSET if not isinstance(self.prefix_items_and_items, Unset): prefix_items_and_items = [] for prefix_items_and_items_item_data in self.prefix_items_and_items: @@ -29,7 +29,7 @@ def to_dict(self) -> Dict[str, Any]: prefix_items_and_items_item = prefix_items_and_items_item_data prefix_items_and_items.append(prefix_items_and_items_item) - prefix_items_only: Union[Unset, List[Union[float, str]]] = UNSET + prefix_items_only: Union[Unset, list[Union[float, str]]] = UNSET if not isinstance(self.prefix_items_only, Unset): prefix_items_only = [] for prefix_items_only_item_data in self.prefix_items_only: @@ -37,7 +37,7 @@ def to_dict(self) -> Dict[str, Any]: prefix_items_only_item = prefix_items_only_item_data prefix_items_only.append(prefix_items_only_item) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if prefix_items_and_items is not UNSET: @@ -48,7 +48,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() prefix_items_and_items = [] _prefix_items_and_items = d.pop("prefixItemsAndItems", UNSET) @@ -87,7 +87,7 @@ def _parse_prefix_items_only_item(data: object) -> Union[float, str]: return post_prefix_items_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/types.py b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/types.py index 21fac106f..b9ed58b8a 100644 --- a/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/types.py +++ b/end_to_end_tests/test-3-1-golden-record/test_3_1_features_client/types.py @@ -1,7 +1,8 @@ """Contains some shared types for properties""" +from collections.abc import MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, MutableMapping, Optional, Tuple, TypeVar +from typing import BinaryIO, Generic, Literal, Optional, TypeVar from attrs import define @@ -13,7 +14,7 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]] +FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] @define @@ -42,4 +43,4 @@ class Response(Generic[T]): parsed: Optional[T] -__all__ = ["File", "Response", "FileJsonType", "Unset", "UNSET"] +__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] diff --git a/end_to_end_tests/test_custom_templates/api_init.py.jinja b/end_to_end_tests/test_custom_templates/api_init.py.jinja index 0c5720bf2..78e473147 100644 --- a/end_to_end_tests/test_custom_templates/api_init.py.jinja +++ b/end_to_end_tests/test_custom_templates/api_init.py.jinja @@ -1,6 +1,5 @@ """ Contains methods for accessing the API """ -from typing import Type {% for tag in endpoint_collections_by_tag.keys() %} from .{{ tag }} import {{ class_name(tag) }}Endpoints {% endfor %} @@ -8,6 +7,6 @@ from .{{ tag }} import {{ class_name(tag) }}Endpoints class {{ class_name(package_name) }}Api: {% for tag in endpoint_collections_by_tag.keys() %} @classmethod - def {{ tag }}(cls) -> Type[{{ class_name(tag) }}Endpoints]: + def {{ tag }}(cls) -> type[{{ class_name(tag) }}Endpoints]: return {{ class_name(tag) }}Endpoints {% endfor %} diff --git a/end_to_end_tests/test_end_to_end.py b/end_to_end_tests/test_end_to_end.py index a448a0698..ae86a2514 100644 --- a/end_to_end_tests/test_end_to_end.py +++ b/end_to_end_tests/test_end_to_end.py @@ -1,21 +1,24 @@ import shutil from filecmp import cmpfiles, dircmp from pathlib import Path -from typing import Dict, List, Optional, Set +from typing import Optional import pytest from click.testing import Result from typer.testing import CliRunner +from end_to_end_tests.generated_client import ( + _run_command, generate_client, generate_client_from_inline_spec, +) from openapi_python_client.cli import app def _compare_directories( record: Path, test_subject: Path, - expected_differences: Dict[Path, str], - expected_missing: Optional[Set[str]] = None, - ignore: List[str] = None, + expected_differences: Optional[dict[Path, str]] = None, + expected_missing: Optional[set[str]] = None, + ignore: list[str] = None, depth=0, ): """ @@ -78,56 +81,31 @@ def _compare_directories( def run_e2e_test( openapi_document: str, - extra_args: List[str], - expected_differences: Optional[Dict[Path, str]] = None, + extra_args: list[str], + expected_differences: Optional[dict[Path, str]] = None, golden_record_path: str = "golden-record", output_path: str = "my-test-api-client", - expected_missing: Optional[Set[str]] = None, + expected_missing: Optional[set[str]] = None, + specify_output_path_explicitly: bool = True, ) -> Result: - output_path = Path.cwd() / output_path - shutil.rmtree(output_path, ignore_errors=True) - result = generate(extra_args, openapi_document) - gr_path = Path(__file__).parent / golden_record_path - - expected_differences = expected_differences or {} - # Use absolute paths for expected differences for easier comparisons - expected_differences = { - output_path.joinpath(key): value for key, value in expected_differences.items() - } - _compare_directories( - gr_path, output_path, expected_differences=expected_differences, expected_missing=expected_missing - ) - - import mypy.api - - out, err, status = mypy.api.run([str(output_path), "--strict"]) - assert status == 0, f"Type checking client failed: {out}" - - shutil.rmtree(output_path) - return result - + with generate_client(openapi_document, extra_args, output_path, specify_output_path_explicitly=specify_output_path_explicitly) as g: + gr_path = Path(__file__).parent / golden_record_path + + expected_differences = expected_differences or {} + # Use absolute paths for expected differences for easier comparisons + expected_differences = { + g.output_path.joinpath(key): value for key, value in expected_differences.items() + } + _compare_directories( + gr_path, g.output_path, expected_differences=expected_differences, expected_missing=expected_missing + ) -def generate(extra_args: Optional[List[str]], openapi_document: str) -> Result: - """Generate a client from an OpenAPI document and return the path to the generated code""" - _run_command("generate", extra_args, openapi_document) + import mypy.api + out, err, status = mypy.api.run([str(g.output_path), "--strict"]) + assert status == 0, f"Type checking client failed: {out}" -def _run_command(command: str, extra_args: Optional[List[str]] = None, openapi_document: Optional[str] = None, url: Optional[str] = None, config_path: Optional[Path] = None) -> Result: - """Generate a client from an OpenAPI document and return the path to the generated code""" - runner = CliRunner() - if openapi_document is not None: - openapi_path = Path(__file__).parent / openapi_document - source_arg = f"--path={openapi_path}" - else: - source_arg = f"--url={url}" - config_path = config_path or (Path(__file__).parent / "config.yml") - args = [command, f"--config={config_path}", source_arg] - if extra_args: - args.extend(extra_args) - result = runner.invoke(app, args) - if result.exit_code != 0: - raise Exception(result.stdout) - return result + return g.generator_result def test_baseline_end_to_end_3_0(): @@ -168,18 +146,17 @@ def test_literal_enums_end_to_end(): ) ) def test_meta(meta: str, generated_file: Optional[str], expected_file: Optional[str]): - output_path = Path.cwd() / "test-3-1-features-client" - shutil.rmtree(output_path, ignore_errors=True) - generate([f"--meta={meta}"], "3.1_specific.openapi.yaml") - - if generated_file and expected_file: - assert (output_path / generated_file).exists() - assert ( - (output_path / generated_file).read_text() == - (Path(__file__).parent / "metadata_snapshots" / expected_file).read_text() - ) - - shutil.rmtree(output_path) + with generate_client( + "3.1_specific.openapi.yaml", + extra_args=[f"--meta={meta}"], + output_path="test-3-1-features-client", + ) as g: + if generated_file and expected_file: + assert (g.output_path / generated_file).exists() + assert ( + (g.output_path / generated_file).read_text() == + (Path(__file__).parent / "metadata_snapshots" / expected_file).read_text() + ) def test_none_meta(): @@ -189,6 +166,7 @@ def test_none_meta(): golden_record_path="test-3-1-golden-record/test_3_1_features_client", output_path="test_3_1_features_client", expected_missing={"py.typed"}, + specify_output_path_explicitly=False, ) @@ -238,55 +216,41 @@ def test_bad_url(): @pytest.mark.parametrize("document", ERROR_DOCUMENTS, ids=[path.stem for path in ERROR_DOCUMENTS]) def test_documents_with_errors(snapshot, document): - runner = CliRunner() - output_path = Path.cwd() / "test-documents-with-errors" - shutil.rmtree(output_path, ignore_errors=True) - result = runner.invoke(app, ["generate", f"--path={document}", "--fail-on-warning", f"--output-path={output_path}"]) - assert result.exit_code == 1 - assert result.stdout.replace(str(output_path), "/test-documents-with-errors") == snapshot - shutil.rmtree(output_path, ignore_errors=True) + with generate_client( + document, + extra_args=["--fail-on-warning"], + output_path="test-documents-with-errors", + raise_on_error=False, + ) as g: + result = g.generator_result + assert result.exit_code == 1 + output = result.stdout.replace(str(g.output_path), "/test-documents-with-errors") + assert output == snapshot def test_custom_post_hooks(): - shutil.rmtree(Path.cwd() / "my-test-api-client", ignore_errors=True) - runner = CliRunner() - openapi_document = Path(__file__).parent / "baseline_openapi_3.0.json" config_path = Path(__file__).parent / "custom_post_hooks.config.yml" - result = runner.invoke(app, ["generate", f"--path={openapi_document}", f"--config={config_path}"]) - assert result.exit_code == 1 - assert "this should fail" in result.stdout - shutil.rmtree(Path.cwd() / "my-test-api-client", ignore_errors=True) + with generate_client( + "baseline_openapi_3.0.json", + [f"--config={config_path}"], + raise_on_error=False, + ) as g: + assert g.generator_result.exit_code == 1 + assert "this should fail" in g.generator_result.stdout def test_generate_dir_already_exists(): project_dir = Path.cwd() / "my-test-api-client" if not project_dir.exists(): project_dir.mkdir() - runner = CliRunner() - openapi_document = Path(__file__).parent / "baseline_openapi_3.0.json" - result = runner.invoke(app, ["generate", f"--path={openapi_document}"]) - assert result.exit_code == 1 - assert "Directory already exists" in result.stdout - shutil.rmtree(Path.cwd() / "my-test-api-client", ignore_errors=True) - - -@pytest.mark.parametrize( - ("file_name", "content", "expected_error"), - ( - ("invalid_openapi.yaml", "not a valid openapi document", "Failed to parse OpenAPI document"), - ("invalid_json.json", "Invalid JSON", "Invalid JSON"), - ("invalid_yaml.yaml", "{", "Invalid YAML"), - ), - ids=("invalid_openapi", "invalid_json", "invalid_yaml") -) -def test_invalid_openapi_document(file_name, content, expected_error): - runner = CliRunner() - openapi_document = Path.cwd() / file_name - openapi_document.write_text(content) - result = runner.invoke(app, ["generate", f"--path={openapi_document}"]) - assert result.exit_code == 1 - assert expected_error in result.stdout - openapi_document.unlink() + try: + runner = CliRunner() + openapi_document = Path(__file__).parent / "baseline_openapi_3.0.json" + result = runner.invoke(app, ["generate", f"--path={openapi_document}"]) + assert result.exit_code == 1 + assert "Directory already exists" in result.stdout + finally: + shutil.rmtree(Path.cwd() / "my-test-api-client", ignore_errors=True) def test_update_integration_tests(): @@ -294,17 +258,21 @@ def test_update_integration_tests(): source_path = Path(__file__).parent.parent / "integration-tests" temp_dir = Path.cwd() / "test_update_integration_tests" shutil.rmtree(temp_dir, ignore_errors=True) - shutil.copytree(source_path, temp_dir) - config_path = source_path / "config.yaml" - _run_command( - "generate", - extra_args=["--meta=none", "--overwrite", f"--output-path={source_path / 'integration_tests'}"], - url=url, - config_path=config_path - ) - _compare_directories(temp_dir, source_path, expected_differences={}) - import mypy.api - out, err, status = mypy.api.run([str(temp_dir), "--strict"]) - assert status == 0, f"Type checking client failed: {out}" - shutil.rmtree(temp_dir) + try: + shutil.copytree(source_path, temp_dir) + config_path = source_path / "config.yaml" + _run_command( + "generate", + extra_args=["--meta=none", "--overwrite", f"--output-path={source_path / 'integration_tests'}"], + url=url, + config_path=config_path + ) + _compare_directories(temp_dir, source_path, expected_differences={}) + import mypy.api + + out, err, status = mypy.api.run([str(temp_dir), "--strict"]) + assert status == 0, f"Type checking client failed: {out}" + + finally: + shutil.rmtree(temp_dir) diff --git a/integration-tests/.gitignore b/integration-tests/.gitignore index ed29cb977..79a2c3d73 100644 --- a/integration-tests/.gitignore +++ b/integration-tests/.gitignore @@ -20,4 +20,4 @@ dmypy.json .idea/ /coverage.xml -/.coverage \ No newline at end of file +/.coverage diff --git a/integration-tests/integration_tests/api/body/post_body_multipart.py b/integration-tests/integration_tests/api/body/post_body_multipart.py index bfef23f2e..13c943ad7 100644 --- a/integration-tests/integration_tests/api/body/post_body_multipart.py +++ b/integration-tests/integration_tests/api/body/post_body_multipart.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -14,10 +14,10 @@ def _get_kwargs( *, body: PostBodyMultipartBody, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/body/multipart", } diff --git a/integration-tests/integration_tests/api/parameters/post_parameters_header.py b/integration-tests/integration_tests/api/parameters/post_parameters_header.py index 90858f9bb..0981585cc 100644 --- a/integration-tests/integration_tests/api/parameters/post_parameters_header.py +++ b/integration-tests/integration_tests/api/parameters/post_parameters_header.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx @@ -16,8 +16,8 @@ def _get_kwargs( string_header: str, number_header: float, integer_header: int, -) -> Dict[str, Any]: - headers: Dict[str, Any] = {} +) -> dict[str, Any]: + headers: dict[str, Any] = {} headers["Boolean-Header"] = "true" if boolean_header else "false" headers["String-Header"] = string_header @@ -26,7 +26,7 @@ def _get_kwargs( headers["Integer-Header"] = str(integer_header) - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "post", "url": "/parameters/header", } diff --git a/integration-tests/integration_tests/client.py b/integration-tests/integration_tests/client.py index 0f6d15e84..e80446f10 100644 --- a/integration-tests/integration_tests/client.py +++ b/integration-tests/integration_tests/client.py @@ -1,5 +1,5 @@ import ssl -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import httpx from attrs import define, evolve, field @@ -36,16 +36,16 @@ class Client: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) - def with_headers(self, headers: Dict[str, str]) -> "Client": + def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -53,7 +53,7 @@ def with_headers(self, headers: Dict[str, str]) -> "Client": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "Client": + def with_cookies(self, cookies: dict[str, str]) -> "Client": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) @@ -166,12 +166,12 @@ class AuthenticatedClient: raise_on_unexpected_status: bool = field(default=False, kw_only=True) _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) @@ -179,7 +179,7 @@ class AuthenticatedClient: prefix: str = "Bearer" auth_header_name: str = "Authorization" - def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": + def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -187,7 +187,7 @@ def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "AuthenticatedClient": + def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) diff --git a/integration-tests/integration_tests/models/post_body_multipart_body.py b/integration-tests/integration_tests/models/post_body_multipart_body.py index f0272a34e..13174200d 100644 --- a/integration-tests/integration_tests/models/post_body_multipart_body.py +++ b/integration-tests/integration_tests/models/post_body_multipart_body.py @@ -1,5 +1,5 @@ from io import BytesIO -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -22,16 +22,16 @@ class PostBodyMultipartBody: a_string: str file: File description: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_string = self.a_string file = self.file.to_tuple() description = self.description - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -44,7 +44,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict - def to_multipart(self) -> Dict[str, Any]: + def to_multipart(self) -> dict[str, Any]: a_string = (None, str(self.a_string).encode(), "text/plain") file = self.file.to_tuple() @@ -55,7 +55,7 @@ def to_multipart(self) -> Dict[str, Any]: else (None, str(self.description).encode(), "text/plain") ) - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = (None, str(prop).encode(), "text/plain") @@ -71,7 +71,7 @@ def to_multipart(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_string = d.pop("a_string") @@ -89,7 +89,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_body_multipart_body @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/integration-tests/integration_tests/models/post_body_multipart_response_200.py b/integration-tests/integration_tests/models/post_body_multipart_response_200.py index 79359ec41..65f38c082 100644 --- a/integration-tests/integration_tests/models/post_body_multipart_response_200.py +++ b/integration-tests/integration_tests/models/post_body_multipart_response_200.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -22,9 +22,9 @@ class PostBodyMultipartResponse200: description: str file_name: str file_content_type: str - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: a_string = self.a_string file_data = self.file_data @@ -35,7 +35,7 @@ def to_dict(self) -> Dict[str, Any]: file_content_type = self.file_content_type - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -50,7 +50,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() a_string = d.pop("a_string") @@ -74,7 +74,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_body_multipart_response_200 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/integration-tests/integration_tests/models/post_parameters_header_response_200.py b/integration-tests/integration_tests/models/post_parameters_header_response_200.py index 03e688ba1..ff44f5644 100644 --- a/integration-tests/integration_tests/models/post_parameters_header_response_200.py +++ b/integration-tests/integration_tests/models/post_parameters_header_response_200.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -20,9 +20,9 @@ class PostParametersHeaderResponse200: string: str number: float integer: int - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: boolean = self.boolean string = self.string @@ -31,7 +31,7 @@ def to_dict(self) -> Dict[str, Any]: integer = self.integer - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { @@ -45,7 +45,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() boolean = d.pop("boolean") @@ -66,7 +66,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return post_parameters_header_response_200 @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/integration-tests/integration_tests/models/problem.py b/integration-tests/integration_tests/models/problem.py index bde5b6d37..f61f42b5d 100644 --- a/integration-tests/integration_tests/models/problem.py +++ b/integration-tests/integration_tests/models/problem.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Type, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,14 +18,14 @@ class Problem: parameter_name: Union[Unset, str] = UNSET description: Union[Unset, str] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: parameter_name = self.parameter_name description = self.description - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if parameter_name is not UNSET: @@ -36,7 +36,7 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() parameter_name = d.pop("parameter_name", UNSET) @@ -51,7 +51,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return problem @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/integration-tests/integration_tests/models/public_error.py b/integration-tests/integration_tests/models/public_error.py index 993bd8ad3..5e9d53c26 100644 --- a/integration-tests/integration_tests/models/public_error.py +++ b/integration-tests/integration_tests/models/public_error.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,39 +16,39 @@ class PublicError: """ Attributes: - errors (Union[Unset, List[str]]): - extra_parameters (Union[Unset, List[str]]): - invalid_parameters (Union[Unset, List['Problem']]): - missing_parameters (Union[Unset, List[str]]): + errors (Union[Unset, list[str]]): + extra_parameters (Union[Unset, list[str]]): + invalid_parameters (Union[Unset, list['Problem']]): + missing_parameters (Union[Unset, list[str]]): """ - errors: Union[Unset, List[str]] = UNSET - extra_parameters: Union[Unset, List[str]] = UNSET - invalid_parameters: Union[Unset, List["Problem"]] = UNSET - missing_parameters: Union[Unset, List[str]] = UNSET - additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + errors: Union[Unset, list[str]] = UNSET + extra_parameters: Union[Unset, list[str]] = UNSET + invalid_parameters: Union[Unset, list["Problem"]] = UNSET + missing_parameters: Union[Unset, list[str]] = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - def to_dict(self) -> Dict[str, Any]: - errors: Union[Unset, List[str]] = UNSET + def to_dict(self) -> dict[str, Any]: + errors: Union[Unset, list[str]] = UNSET if not isinstance(self.errors, Unset): errors = self.errors - extra_parameters: Union[Unset, List[str]] = UNSET + extra_parameters: Union[Unset, list[str]] = UNSET if not isinstance(self.extra_parameters, Unset): extra_parameters = self.extra_parameters - invalid_parameters: Union[Unset, List[Dict[str, Any]]] = UNSET + invalid_parameters: Union[Unset, list[dict[str, Any]]] = UNSET if not isinstance(self.invalid_parameters, Unset): invalid_parameters = [] for invalid_parameters_item_data in self.invalid_parameters: invalid_parameters_item = invalid_parameters_item_data.to_dict() invalid_parameters.append(invalid_parameters_item) - missing_parameters: Union[Unset, List[str]] = UNSET + missing_parameters: Union[Unset, list[str]] = UNSET if not isinstance(self.missing_parameters, Unset): missing_parameters = self.missing_parameters - field_dict: Dict[str, Any] = {} + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) if errors is not UNSET: @@ -63,13 +63,13 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.problem import Problem d = src_dict.copy() - errors = cast(List[str], d.pop("errors", UNSET)) + errors = cast(list[str], d.pop("errors", UNSET)) - extra_parameters = cast(List[str], d.pop("extra_parameters", UNSET)) + extra_parameters = cast(list[str], d.pop("extra_parameters", UNSET)) invalid_parameters = [] _invalid_parameters = d.pop("invalid_parameters", UNSET) @@ -78,7 +78,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: invalid_parameters.append(invalid_parameters_item) - missing_parameters = cast(List[str], d.pop("missing_parameters", UNSET)) + missing_parameters = cast(list[str], d.pop("missing_parameters", UNSET)) public_error = cls( errors=errors, @@ -91,7 +91,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: return public_error @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: diff --git a/integration-tests/integration_tests/py.typed b/integration-tests/integration_tests/py.typed new file mode 100644 index 000000000..1aad32711 --- /dev/null +++ b/integration-tests/integration_tests/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 \ No newline at end of file diff --git a/integration-tests/integration_tests/types.py b/integration-tests/integration_tests/types.py index 21fac106f..b9ed58b8a 100644 --- a/integration-tests/integration_tests/types.py +++ b/integration-tests/integration_tests/types.py @@ -1,7 +1,8 @@ """Contains some shared types for properties""" +from collections.abc import MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, MutableMapping, Optional, Tuple, TypeVar +from typing import BinaryIO, Generic, Literal, Optional, TypeVar from attrs import define @@ -13,7 +14,7 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]] +FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] @define @@ -42,4 +43,4 @@ class Response(Generic[T]): parsed: Optional[T] -__all__ = ["File", "Response", "FileJsonType", "Unset", "UNSET"] +__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] diff --git a/integration-tests/pdm.lock b/integration-tests/pdm.lock index 6ace3d70a..a854a988f 100644 --- a/integration-tests/pdm.lock +++ b/integration-tests/pdm.lock @@ -5,51 +5,48 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:23d53455c7fb390a7c1f417cee321de488e65815e6420ae5968172119fac835d" +content_hash = "sha256:a575c5fc1f04f52530c52becbcc2f40498a54612d0eeeaddf6701fe5336986ac" [[metadata.targets]] -requires_python = "~=3.8" +requires_python = "~=3.9" [[package]] name = "anyio" -version = "4.5.2" -requires_python = ">=3.8" +version = "4.7.0" +requires_python = ">=3.9" summary = "High level compatibility layer for multiple asynchronous event loop implementations" groups = ["default"] dependencies = [ "exceptiongroup>=1.0.2; python_version < \"3.11\"", "idna>=2.8", "sniffio>=1.1", - "typing-extensions>=4.1; python_version < \"3.11\"", + "typing-extensions>=4.5; python_version < \"3.13\"", ] files = [ - {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, - {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [[package]] name = "attrs" -version = "24.2.0" -requires_python = ">=3.7" +version = "24.3.0" +requires_python = ">=3.8" summary = "Classes Without Boilerplate" groups = ["default"] -dependencies = [ - "importlib-metadata; python_version < \"3.8\"", -] files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, ] [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." groups = ["default"] files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] [[package]] @@ -92,7 +89,7 @@ files = [ [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." groups = ["default"] @@ -101,13 +98,13 @@ dependencies = [ "h11<0.15,>=0.13", ] files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" requires_python = ">=3.8" summary = "The next generation HTTP client." groups = ["default"] @@ -116,11 +113,10 @@ dependencies = [ "certifi", "httpcore==1.*", "idna", - "sniffio", ] files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [[package]] @@ -147,7 +143,7 @@ files = [ [[package]] name = "mypy" -version = "1.12.1" +version = "1.14.0" requires_python = ">=3.8" summary = "Optional static typing for Python" groups = ["dev"] @@ -157,38 +153,33 @@ dependencies = [ "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"}, - {file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"}, - {file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"}, - {file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"}, - {file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"}, - {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, - {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, - {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, - {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, - {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, - {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, - {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, - {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, - {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, - {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, - {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, - {file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"}, - {file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"}, - {file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"}, - {file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"}, - {file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"}, - {file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"}, - {file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"}, - {file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"}, - {file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"}, - {file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"}, - {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, - {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, + {file = "mypy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e971c1c667007f9f2b397ffa80fa8e1e0adccff336e5e77e74cb5f22868bee87"}, + {file = "mypy-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e86aaeaa3221a278c66d3d673b297232947d873773d61ca3ee0e28b2ff027179"}, + {file = "mypy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1628c5c3ce823d296e41e2984ff88c5861499041cb416a8809615d0c1f41740e"}, + {file = "mypy-1.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fadb29b77fc14a0dd81304ed73c828c3e5cde0016c7e668a86a3e0dfc9f3af3"}, + {file = "mypy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:3fa76988dc760da377c1e5069200a50d9eaaccf34f4ea18428a3337034ab5a44"}, + {file = "mypy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e73c8a154eed31db3445fe28f63ad2d97b674b911c00191416cf7f6459fd49a"}, + {file = "mypy-1.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:273e70fcb2e38c5405a188425aa60b984ffdcef65d6c746ea5813024b68c73dc"}, + {file = "mypy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1daca283d732943731a6a9f20fdbcaa927f160bc51602b1d4ef880a6fb252015"}, + {file = "mypy-1.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7e68047bedb04c1c25bba9901ea46ff60d5eaac2d71b1f2161f33107e2b368eb"}, + {file = "mypy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:7a52f26b9c9b1664a60d87675f3bae00b5c7f2806e0c2800545a32c325920bcc"}, + {file = "mypy-1.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d5326ab70a6db8e856d59ad4cb72741124950cbbf32e7b70e30166ba7bbf61dd"}, + {file = "mypy-1.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf4ec4980bec1e0e24e5075f449d014011527ae0055884c7e3abc6a99cd2c7f1"}, + {file = "mypy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:390dfb898239c25289495500f12fa73aa7f24a4c6d90ccdc165762462b998d63"}, + {file = "mypy-1.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e026d55ddcd76e29e87865c08cbe2d0104e2b3153a523c529de584759379d3d"}, + {file = "mypy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:585ed36031d0b3ee362e5107ef449a8b5dfd4e9c90ccbe36414ee405ee6b32ba"}, + {file = "mypy-1.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9f6f4c0b27401d14c483c622bc5105eff3911634d576bbdf6695b9a7c1ba741"}, + {file = "mypy-1.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b2280cedcb312c7a79f5001ae5325582d0d339bce684e4a529069d0e7ca1e7"}, + {file = "mypy-1.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:342de51c48bab326bfc77ce056ba08c076d82ce4f5a86621f972ed39970f94d8"}, + {file = "mypy-1.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00df23b42e533e02a6f0055e54de9a6ed491cd8b7ea738647364fd3a39ea7efc"}, + {file = "mypy-1.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:e8c8387e5d9dff80e7daf961df357c80e694e942d9755f3ad77d69b0957b8e3f"}, + {file = "mypy-1.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14117b9da3305b39860d0aa34b8f1ff74d209a368829a584eb77524389a9c13e"}, + {file = "mypy-1.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af98c5a958f9c37404bd4eef2f920b94874507e146ed6ee559f185b8809c44cc"}, + {file = "mypy-1.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0b343a1d3989547024377c2ba0dca9c74a2428ad6ed24283c213af8dbb0710b"}, + {file = "mypy-1.14.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cdb5563c1726c85fb201be383168f8c866032db95e1095600806625b3a648cb7"}, + {file = "mypy-1.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:74e925649c1ee0a79aa7448baf2668d81cc287dc5782cff6a04ee93f40fb8d3f"}, + {file = "mypy-1.14.0-py3-none-any.whl", hash = "sha256:2238d7f93fc4027ed1efc944507683df3ba406445a2b6c96e79666a045aadfab"}, + {file = "mypy-1.14.0.tar.gz", hash = "sha256:822dbd184d4a9804df5a7d5335a68cf7662930e70b8c1bc976645d1509f9a9d6"}, ] [[package]] @@ -204,13 +195,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" requires_python = ">=3.8" summary = "Core utilities for Python packages" groups = ["dev"] files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -226,7 +217,7 @@ files = [ [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" requires_python = ">=3.8" summary = "pytest: simple powerful testing with Python" groups = ["dev"] @@ -239,22 +230,22 @@ dependencies = [ "tomli>=1; python_version < \"3.11\"", ] files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [[package]] name = "pytest-asyncio" -version = "0.24.0" -requires_python = ">=3.8" +version = "0.25.0" +requires_python = ">=3.9" summary = "Pytest support for asyncio" groups = ["dev"] dependencies = [ "pytest<9,>=8.2", ] files = [ - {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, - {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, + {file = "pytest_asyncio-0.25.0-py3-none-any.whl", hash = "sha256:db5432d18eac6b7e28b46dcd9b69921b55c3b1086e85febfe04e70b18d9e81b3"}, + {file = "pytest_asyncio-0.25.0.tar.gz", hash = "sha256:8c0610303c9e0442a5db8604505fc0f545456ba1528824842b37b4a626cbf609"}, ] [[package]] @@ -273,13 +264,13 @@ files = [ [[package]] name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.17.0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Python 2 and 3 compatibility utilities" groups = ["default"] files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -295,14 +286,44 @@ files = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" requires_python = ">=3.8" summary = "A lil' TOML parser" groups = ["dev"] marker = "python_version < \"3.11\"" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] diff --git a/integration-tests/pyproject.toml b/integration-tests/pyproject.toml index 67b0d7f52..cbcff74c6 100644 --- a/integration-tests/pyproject.toml +++ b/integration-tests/pyproject.toml @@ -4,12 +4,12 @@ version = "0.0.1" description = "A client library for accessing OpenAPI Test Server" authors = [] readme = "README.md" +requires-python = ">=3.9,<4.0" dependencies = [ - "httpx>=0.20.0,<0.28.0", - "attrs>=21.3.0", + "httpx>=0.20.0,<0.29.0", + "attrs>=22.2.0", "python-dateutil>=2.8.0", ] -requires-python = ">=3.8,<4.0" [tool.pdm] distribution = true diff --git a/openapi_python_client/__init__.py b/openapi_python_client/__init__.py index f2cfb40ec..e42af56f5 100644 --- a/openapi_python_client/__init__.py +++ b/openapi_python_client/__init__.py @@ -4,10 +4,11 @@ import mimetypes import shutil import subprocess +from collections.abc import Sequence from importlib.metadata import version from pathlib import Path from subprocess import CalledProcessError -from typing import Any, Dict, List, Optional, Sequence, Union +from typing import Any, Optional, Union import httpcore import httpx @@ -22,7 +23,12 @@ from .parser.errors import ErrorLevel, GeneratorError from .parser.properties import LiteralEnumProperty -__version__ = version(__package__) +# __version__ = version(__package__) +# Benchling-specific change: the original line above won't work because the installed package name of +# our fork (benchling-openapi-python-client) deliberately does not match the base module name (see +# pyproject.toml) +INSTALLED_PACKAGE = "benchling-openapi-python-client" +__version__ = version(INSTALLED_PACKAGE) TEMPLATE_FILTERS = { @@ -89,6 +95,7 @@ def __init__( self.env.filters.update(TEMPLATE_FILTERS) self.env.globals.update( + config=config, utils=utils, python_identifier=lambda x: utils.PythonIdentifier(x, config.field_prefix), class_name=lambda x: utils.ClassName(x, config.field_prefix), @@ -101,19 +108,17 @@ def __init__( openapi=self.openapi, endpoint_collections_by_tag=self.openapi.endpoint_collections_by_tag, ) - self.errors: List[GeneratorError] = [] + self.errors: list[GeneratorError] = [] def build(self) -> Sequence[GeneratorError]: """Create the project from templates""" print(f"Generating {self.project_dir}") - if self.config.overwrite: - shutil.rmtree(self.project_dir, ignore_errors=True) - try: self.project_dir.mkdir() except FileExistsError: - return [GeneratorError(detail="Directory already exists. Delete it or use the --overwrite option.")] + if not self.config.overwrite: + return [GeneratorError(detail="Directory already exists. Delete it or use the --overwrite option.")] self._create_package() self._build_metadata() self._build_models() @@ -147,8 +152,8 @@ def _run_command(self, cmd: str) -> None: ) ) - def _get_errors(self) -> List[GeneratorError]: - errors: List[GeneratorError] = [] + def _get_errors(self) -> list[GeneratorError]: + errors: list[GeneratorError] = [] for collection in self.openapi.endpoint_collections_by_tag.values(): errors.extend(collection.parse_errors) errors.extend(self.openapi.errors) @@ -157,7 +162,7 @@ def _get_errors(self) -> List[GeneratorError]: def _create_package(self) -> None: if self.package_dir != self.project_dir: - self.package_dir.mkdir() + self.package_dir.mkdir(exist_ok=True) # Package __init__.py package_init = self.package_dir / "__init__.py" @@ -213,6 +218,7 @@ def _build_setup_py(self) -> None: def _build_models(self) -> None: # Generate models models_dir = self.package_dir / "models" + shutil.rmtree(models_dir, ignore_errors=True) models_dir.mkdir() models_init = models_dir / "__init__.py" imports = [] @@ -258,6 +264,7 @@ def _build_api(self) -> None: # Generate endpoints api_dir = self.package_dir / "api" + shutil.rmtree(api_dir, ignore_errors=True) api_dir.mkdir() api_init_path = api_dir / "__init__.py" api_init_template = self.env.get_template("api_init.py.jinja") @@ -325,7 +332,7 @@ def generate( return project.build() -def _load_yaml_or_json(data: bytes, content_type: Optional[str]) -> Union[Dict[str, Any], GeneratorError]: +def _load_yaml_or_json(data: bytes, content_type: Optional[str]) -> Union[dict[str, Any], GeneratorError]: if content_type == "application/json": try: return json.loads(data.decode()) @@ -339,7 +346,7 @@ def _load_yaml_or_json(data: bytes, content_type: Optional[str]) -> Union[Dict[s return GeneratorError(header=f"Invalid YAML from provided source: {err}") -def _get_document(*, source: Union[str, Path], timeout: int) -> Union[Dict[str, Any], GeneratorError]: +def _get_document(*, source: Union[str, Path], timeout: int) -> Union[dict[str, Any], GeneratorError]: yaml_bytes: bytes content_type: Optional[str] if isinstance(source, str): diff --git a/openapi_python_client/cli.py b/openapi_python_client/cli.py index e8fd50f9e..92d91f943 100644 --- a/openapi_python_client/cli.py +++ b/openapi_python_client/cli.py @@ -1,7 +1,8 @@ import codecs +from collections.abc import Sequence from pathlib import Path from pprint import pformat -from typing import Optional, Sequence, Union +from typing import Optional, Union import typer @@ -9,7 +10,7 @@ from openapi_python_client.config import Config, ConfigFile from openapi_python_client.parser.errors import ErrorLevel, GeneratorError, ParseError -app = typer.Typer() +app = typer.Typer(name="openapi-python-client") def _version_callback(value: bool) -> None: @@ -62,7 +63,7 @@ def _process_config( # noinspection PyUnusedLocal -@app.callback(name="openapi-python-client") +@app.callback() def cli( version: bool = typer.Option(False, "--version", callback=_version_callback, help="Print the version and exit"), ) -> None: diff --git a/openapi_python_client/config.py b/openapi_python_client/config.py index 6625bda1f..1616ac785 100644 --- a/openapi_python_client/config.py +++ b/openapi_python_client/config.py @@ -2,7 +2,7 @@ import mimetypes from enum import Enum from pathlib import Path -from typing import Dict, List, Optional, Union +from typing import Optional, Union from attr import define from pydantic import BaseModel @@ -34,14 +34,16 @@ class ConfigFile(BaseModel): See https://github.com/openapi-generators/openapi-python-client#configuration """ - class_overrides: Optional[Dict[str, ClassOverride]] = None - content_type_overrides: Optional[Dict[str, str]] = None + class_overrides: Optional[dict[str, ClassOverride]] = None + content_type_overrides: Optional[dict[str, str]] = None project_name_override: Optional[str] = None package_name_override: Optional[str] = None package_version_override: Optional[str] = None use_path_prefixes_for_title_model_names: bool = True - post_hooks: Optional[List[str]] = None + post_hooks: Optional[list[str]] = None + docstrings_on_attributes: bool = False field_prefix: str = "field_" + generate_all_tags: bool = False http_timeout: int = 5 literal_enums: bool = False @@ -63,18 +65,20 @@ class Config: """Contains all the config values for the generator, from files, defaults, and CLI arguments.""" meta_type: MetaType - class_overrides: Dict[str, ClassOverride] + class_overrides: dict[str, ClassOverride] project_name_override: Optional[str] package_name_override: Optional[str] package_version_override: Optional[str] use_path_prefixes_for_title_model_names: bool - post_hooks: List[str] + post_hooks: list[str] + docstrings_on_attributes: bool field_prefix: str + generate_all_tags: bool http_timeout: int literal_enums: bool document_source: Union[Path, str] file_encoding: str - content_type_overrides: Dict[str, str] + content_type_overrides: dict[str, str] overwrite: bool output_path: Optional[Path] @@ -109,7 +113,9 @@ def from_sources( package_version_override=config_file.package_version_override, use_path_prefixes_for_title_model_names=config_file.use_path_prefixes_for_title_model_names, post_hooks=post_hooks, + docstrings_on_attributes=config_file.docstrings_on_attributes, field_prefix=config_file.field_prefix, + generate_all_tags=config_file.generate_all_tags, http_timeout=config_file.http_timeout, literal_enums=config_file.literal_enums, document_source=document_source, diff --git a/openapi_python_client/parser/bodies.py b/openapi_python_client/parser/bodies.py index 8515ad7cc..7d0b12954 100644 --- a/openapi_python_client/parser/bodies.py +++ b/openapi_python_client/parser/bodies.py @@ -1,5 +1,5 @@ import sys -from typing import Dict, List, Tuple, Union +from typing import Union import attr @@ -9,6 +9,7 @@ Schemas, property_from_data, ) +from openapi_python_client.parser.properties.schemas import get_reference_simple_name from .. import schema as oai from ..config import Config @@ -44,10 +45,10 @@ def body_from_data( *, data: oai.Operation, schemas: Schemas, - request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]], + request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]], config: Config, endpoint_name: str, -) -> Tuple[List[Union[Body, ParseError]], Schemas]: +) -> tuple[list[Union[Body, ParseError]], Schemas]: """Adds form or JSON body to Endpoint if included in data""" body = _resolve_reference(data.request_body, request_bodies) if isinstance(body, ParseError): @@ -55,7 +56,7 @@ def body_from_data( if body is None: return [], schemas - bodies: List[Union[Body, ParseError]] = [] + bodies: list[Union[Body, ParseError]] = [] body_content = body.content prefix_type_names = len(body_content) > 1 @@ -131,14 +132,14 @@ def body_from_data( def _resolve_reference( - body: Union[oai.RequestBody, oai.Reference, None], request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]] + body: Union[oai.RequestBody, oai.Reference, None], request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]] ) -> Union[oai.RequestBody, ParseError, None]: if body is None: return None references_seen = [] while isinstance(body, oai.Reference) and body.ref not in references_seen: references_seen.append(body.ref) - body = request_bodies.get(body.ref.split("/")[-1]) + body = request_bodies.get(get_reference_simple_name(body.ref)) if isinstance(body, oai.Reference): return ParseError(detail="Circular $ref in request body", data=body) if body is None and references_seen: diff --git a/openapi_python_client/parser/errors.py b/openapi_python_client/parser/errors.py index 76a795b24..36322f0cf 100644 --- a/openapi_python_client/parser/errors.py +++ b/openapi_python_client/parser/errors.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Optional -__all__ = ["ErrorLevel", "GeneratorError", "ParseError", "PropertyError", "ParameterError"] +__all__ = ["ErrorLevel", "GeneratorError", "ParameterError", "ParseError", "PropertyError"] from pydantic import BaseModel diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index acc8998cd..e427cc18d 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -1,8 +1,9 @@ import re +from collections.abc import Iterator from copy import deepcopy from dataclasses import dataclass, field from http import HTTPStatus -from typing import Any, Dict, Iterator, List, Optional, Protocol, Set, Tuple, Union +from typing import Any, Optional, Protocol, Union from pydantic import ValidationError @@ -40,20 +41,21 @@ class EndpointCollection: """A bunch of endpoints grouped under a tag that will become a module""" tag: str - endpoints: List["Endpoint"] = field(default_factory=list) - parse_errors: List[ParseError] = field(default_factory=list) + endpoints: list["Endpoint"] = field(default_factory=list) + parse_errors: list[ParseError] = field(default_factory=list) @staticmethod def from_data( *, - data: Dict[str, oai.PathItem], + data: dict[str, oai.PathItem], schemas: Schemas, parameters: Parameters, - request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]], + request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]], + responses: dict[str, Union[oai.Response, oai.Reference]], config: Config, - ) -> Tuple[Dict[utils.PythonIdentifier, "EndpointCollection"], Schemas, Parameters]: + ) -> tuple[dict[utils.PythonIdentifier, "EndpointCollection"], Schemas, Parameters]: """Parse the openapi paths data to get EndpointCollections by tag""" - endpoints_by_tag: Dict[utils.PythonIdentifier, EndpointCollection] = {} + endpoints_by_tag: dict[utils.PythonIdentifier, EndpointCollection] = {} methods = ["get", "put", "post", "delete", "options", "head", "patch", "trace"] @@ -62,16 +64,22 @@ def from_data( operation: Optional[oai.Operation] = getattr(path_data, method) if operation is None: continue - tag = utils.PythonIdentifier(value=(operation.tags or ["default"])[0], prefix="tag") - collection = endpoints_by_tag.setdefault(tag, EndpointCollection(tag=tag)) + + tags = [utils.PythonIdentifier(value=tag, prefix="tag") for tag in operation.tags or ["default"]] + if not config.generate_all_tags: + tags = tags[:1] + + collections = [endpoints_by_tag.setdefault(tag, EndpointCollection(tag=tag)) for tag in tags] + endpoint, schemas, parameters = Endpoint.from_data( data=operation, path=path, method=method, - tag=tag, + tags=tags, schemas=schemas, parameters=parameters, request_bodies=request_bodies, + responses=responses, config=config, ) # Add `PathItem` parameters @@ -86,15 +94,16 @@ def from_data( if not isinstance(endpoint, ParseError): endpoint = Endpoint.sort_parameters(endpoint=endpoint) if isinstance(endpoint, ParseError): - endpoint.header = ( - f"WARNING parsing {method.upper()} {path} within {tag}. Endpoint will not be generated." - ) - collection.parse_errors.append(endpoint) + endpoint.header = f"WARNING parsing {method.upper()} {path} within {'/'.join(tags)}. Endpoint will not be generated." + for collection in collections: + collection.parse_errors.append(endpoint) continue for error in endpoint.errors: - error.header = f"WARNING parsing {method.upper()} {path} within {tag}." - collection.parse_errors.append(error) - collection.endpoints.append(endpoint) + error.header = f"WARNING parsing {method.upper()} {path} within {'/'.join(tags)}." + for collection in collections: + collection.parse_errors.append(error) + for collection in collections: + collection.endpoints.append(endpoint) return endpoints_by_tag, schemas, parameters @@ -117,7 +126,7 @@ class RequestBodyParser(Protocol): def __call__( self, *, body: oai.RequestBody, schemas: Schemas, parent_name: str, config: Config - ) -> Tuple[Union[Property, PropertyError, None], Schemas]: ... # pragma: no cover + ) -> tuple[Union[Property, PropertyError, None], Schemas]: ... # pragma: no cover @dataclass @@ -131,21 +140,26 @@ class Endpoint: description: Optional[str] name: str requires_security: bool - tag: str + tags: list[PythonIdentifier] summary: Optional[str] = "" - relative_imports: Set[str] = field(default_factory=set) - query_parameters: List[Property] = field(default_factory=list) - path_parameters: List[Property] = field(default_factory=list) - header_parameters: List[Property] = field(default_factory=list) - cookie_parameters: List[Property] = field(default_factory=list) - responses: List[Response] = field(default_factory=list) - bodies: List[Body] = field(default_factory=list) - errors: List[ParseError] = field(default_factory=list) + relative_imports: set[str] = field(default_factory=set) + query_parameters: list[Property] = field(default_factory=list) + path_parameters: list[Property] = field(default_factory=list) + header_parameters: list[Property] = field(default_factory=list) + cookie_parameters: list[Property] = field(default_factory=list) + responses: list[Response] = field(default_factory=list) + bodies: list[Body] = field(default_factory=list) + errors: list[ParseError] = field(default_factory=list) @staticmethod def _add_responses( - *, endpoint: "Endpoint", data: oai.Responses, schemas: Schemas, config: Config - ) -> Tuple["Endpoint", Schemas]: + *, + endpoint: "Endpoint", + data: oai.Responses, + schemas: Schemas, + responses: dict[str, Union[oai.Response, oai.Reference]], + config: Config, + ) -> tuple["Endpoint", Schemas]: endpoint = deepcopy(endpoint) for code, response_data in data.items(): status_code: HTTPStatus @@ -167,6 +181,7 @@ def _add_responses( status_code=status_code, data=response_data, schemas=schemas, + responses=responses, parent_name=endpoint.name, config=config, ) @@ -197,7 +212,7 @@ def add_parameters( schemas: Schemas, parameters: Parameters, config: Config, - ) -> Tuple[Union["Endpoint", ParseError], Schemas, Parameters]: + ) -> tuple[Union["Endpoint", ParseError], Schemas, Parameters]: """Process the defined `parameters` for an Endpoint. Any existing parameters will be ignored, so earlier instances of a parameter take precedence. PathItem @@ -226,8 +241,8 @@ def add_parameters( endpoint = deepcopy(endpoint) - unique_parameters: Set[Tuple[str, oai.ParameterLocation]] = set() - parameters_by_location: Dict[str, List[Property]] = { + unique_parameters: set[tuple[str, oai.ParameterLocation]] = set() + parameters_by_location: dict[str, list[Property]] = { oai.ParameterLocation.QUERY: endpoint.query_parameters, oai.ParameterLocation.PATH: endpoint.path_parameters, oai.ParameterLocation.HEADER: endpoint.header_parameters, @@ -304,7 +319,7 @@ def _check_parameters_for_conflicts( self, *, config: Config, - previously_modified_params: Optional[Set[Tuple[oai.ParameterLocation, str]]] = None, + previously_modified_params: Optional[set[tuple[oai.ParameterLocation, str]]] = None, ) -> Union["Endpoint", ParseError]: """Check for conflicting parameters @@ -316,7 +331,7 @@ def _check_parameters_for_conflicts( unique python_name. """ modified_params = previously_modified_params or set() - used_python_names: Dict[PythonIdentifier, Tuple[oai.ParameterLocation, Property]] = {} + used_python_names: dict[PythonIdentifier, tuple[oai.ParameterLocation, Property]] = {} reserved_names = ["client", "url"] for parameter in self.iter_all_parameters(): location, prop = parameter @@ -392,12 +407,13 @@ def from_data( data: oai.Operation, path: str, method: str, - tag: str, + tags: list[PythonIdentifier], schemas: Schemas, parameters: Parameters, - request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]], + request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]], + responses: dict[str, Union[oai.Response, oai.Reference]], config: Config, - ) -> Tuple[Union["Endpoint", ParseError], Schemas, Parameters]: + ) -> tuple[Union["Endpoint", ParseError], Schemas, Parameters]: """Construct an endpoint from the OpenAPI data""" if data.operationId is None: @@ -412,7 +428,7 @@ def from_data( description=utils.remove_string_escapes(data.description) if data.description else "", name=name, requires_security=bool(data.security), - tag=tag, + tags=tags, ) result, schemas, parameters = Endpoint.add_parameters( @@ -424,7 +440,13 @@ def from_data( ) if isinstance(result, ParseError): return result, schemas, parameters - result, schemas = Endpoint._add_responses(endpoint=result, data=data.responses, schemas=schemas, config=config) + result, schemas = Endpoint._add_responses( + endpoint=result, + data=data.responses, + schemas=schemas, + responses=responses, + config=config, + ) if isinstance(result, ParseError): return result, schemas, parameters bodies, schemas = body_from_data( @@ -461,15 +483,15 @@ def response_type(self) -> str: return self.responses[0].prop.get_type_string(quoted=False) return f"Union[{', '.join(types)}]" - def iter_all_parameters(self) -> Iterator[Tuple[oai.ParameterLocation, Property]]: + def iter_all_parameters(self) -> Iterator[tuple[oai.ParameterLocation, Property]]: """Iterate through all the parameters of this endpoint""" yield from ((oai.ParameterLocation.PATH, param) for param in self.path_parameters) yield from ((oai.ParameterLocation.QUERY, param) for param in self.query_parameters) yield from ((oai.ParameterLocation.HEADER, param) for param in self.header_parameters) yield from ((oai.ParameterLocation.COOKIE, param) for param in self.cookie_parameters) - def list_all_parameters(self) -> List[Property]: - """Return a List of all the parameters of this endpoint""" + def list_all_parameters(self) -> list[Property]: + """Return a list of all the parameters of this endpoint""" return ( self.path_parameters + self.query_parameters @@ -487,12 +509,12 @@ class GeneratorData: description: Optional[str] version: str models: Iterator[ModelProperty] - errors: List[ParseError] - endpoint_collections_by_tag: Dict[utils.PythonIdentifier, EndpointCollection] + errors: list[ParseError] + endpoint_collections_by_tag: dict[utils.PythonIdentifier, EndpointCollection] enums: Iterator[Union[EnumProperty, LiteralEnumProperty]] @staticmethod - def from_dict(data: Dict[str, Any], *, config: Config) -> Union["GeneratorData", GeneratorError]: + def from_dict(data: dict[str, Any], *, config: Config) -> Union["GeneratorData", GeneratorError]: """Create an OpenAPI from dict""" try: openapi = oai.OpenAPI.model_validate(data) @@ -514,8 +536,14 @@ def from_dict(data: Dict[str, Any], *, config: Config) -> Union["GeneratorData", config=config, ) request_bodies = (openapi.components and openapi.components.requestBodies) or {} + responses = (openapi.components and openapi.components.responses) or {} endpoint_collections_by_tag, schemas, parameters = EndpointCollection.from_data( - data=openapi.paths, schemas=schemas, parameters=parameters, request_bodies=request_bodies, config=config + data=openapi.paths, + schemas=schemas, + parameters=parameters, + request_bodies=request_bodies, + responses=responses, + config=config, ) enums = ( diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 94c6e3d08..ba667347b 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -9,12 +9,12 @@ "Parameters", "Property", "Schemas", - "build_schemas", "build_parameters", + "build_schemas", "property_from_data", ] -from typing import Iterable +from collections.abc import Iterable from attrs import evolve diff --git a/openapi_python_client/parser/properties/enum_property.py b/openapi_python_client/parser/properties/enum_property.py index 29609864f..c47d60961 100644 --- a/openapi_python_client/parser/properties/enum_property.py +++ b/openapi_python_client/parser/properties/enum_property.py @@ -2,7 +2,7 @@ __all__ = ["EnumProperty", "ValueType"] -from typing import Any, ClassVar, List, Union, cast +from typing import Any, ClassVar, Union, cast from attr import evolve from attrs import define @@ -12,7 +12,7 @@ from ...schema import DataType from ..errors import PropertyError from .none import NoneProperty -from .protocol import PropertyProtocol, Value +from .protocol import HasNamedClass, PropertyProtocol, Value from .schemas import Class, Schemas from .union import UnionProperty @@ -20,7 +20,7 @@ @define -class EnumProperty(PropertyProtocol): +class EnumProperty(PropertyProtocol, HasNamedClass): """A property that should use an enum""" name: str @@ -99,7 +99,7 @@ def build( # noqa: PLR0911 if value_type not in (str, int): return PropertyError(header=f"Unsupported enum type {value_type}", data=data), schemas value_list = cast( - Union[List[int], List[str]], unchecked_value_list + Union[list[int], list[str]], unchecked_value_list ) # We checked this with all the value_types stuff if len(value_list) < len(enum): # Only one of the values was None, that becomes a union diff --git a/openapi_python_client/parser/properties/list_property.py b/openapi_python_client/parser/properties/list_property.py index 47a52cda3..bf0756fe3 100644 --- a/openapi_python_client/parser/properties/list_property.py +++ b/openapi_python_client/parser/properties/list_property.py @@ -106,10 +106,10 @@ def convert_value(self, value: Any) -> Value | None | PropertyError: return None # pragma: no cover def get_base_type_string(self, *, quoted: bool = False) -> str: - return f"List[{self.inner_property.get_type_string(quoted=not self.inner_property.is_base_type)}]" + return f"list[{self.inner_property.get_type_string(quoted=not self.inner_property.is_base_type)}]" def get_base_json_type_string(self, *, quoted: bool = False) -> str: - return f"List[{self.inner_property.get_type_string(json=True, quoted=not self.inner_property.is_base_type)}]" + return f"list[{self.inner_property.get_type_string(json=True, quoted=not self.inner_property.is_base_type)}]" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" @@ -125,7 +125,7 @@ def get_imports(self, *, prefix: str) -> set[str]: """ imports = super().get_imports(prefix=prefix) imports.update(self.inner_property.get_imports(prefix=prefix)) - imports.add("from typing import cast, List") + imports.add("from typing import cast") return imports def get_lazy_imports(self, *, prefix: str) -> set[str]: @@ -151,7 +151,7 @@ def get_type_string( if json: type_string = self.get_base_json_type_string() elif multipart: - type_string = "Tuple[None, bytes, str]" + type_string = "tuple[None, bytes, str]" else: type_string = self.get_base_type_string() diff --git a/openapi_python_client/parser/properties/literal_enum_property.py b/openapi_python_client/parser/properties/literal_enum_property.py index c305a9a41..669b62f58 100644 --- a/openapi_python_client/parser/properties/literal_enum_property.py +++ b/openapi_python_client/parser/properties/literal_enum_property.py @@ -2,7 +2,7 @@ __all__ = ["LiteralEnumProperty"] -from typing import Any, ClassVar, List, Union, cast +from typing import Any, ClassVar, Union, cast from attr import evolve from attrs import define @@ -98,7 +98,7 @@ def build( # noqa: PLR0911 if value_type not in (str, int): return PropertyError(header=f"Unsupported enum type {value_type}", data=data), schemas value_list = cast( - Union[List[int], List[str]], unchecked_value_list + Union[list[int], list[str]], unchecked_value_list ) # We checked this with all the value_types stuff if len(value_list) < len(enum): # Only one of the values was None, that becomes a union diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 0dc13be54..875a4a5f8 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -10,12 +10,12 @@ from ...utils import PythonIdentifier from ..errors import ParseError, PropertyError from .any import AnyProperty -from .protocol import PropertyProtocol, Value +from .protocol import HasNamedClass, PropertyProtocol, Value from .schemas import Class, ReferencePath, Schemas, parse_reference_path @define -class ModelProperty(PropertyProtocol): +class ModelProperty(PropertyProtocol, HasNamedClass): """A property which refers to another Schema""" name: str @@ -32,7 +32,8 @@ class ModelProperty(PropertyProtocol): relative_imports: set[str] | None lazy_imports: set[str] | None additional_properties: Property | None - _json_type_string: ClassVar[str] = "Dict[str, Any]" + ref_path: ReferencePath | None = None + _json_type_string: ClassVar[str] = "dict[str, Any]" template: ClassVar[str] = "model_property.py.jinja" json_is_dict: ClassVar[bool] = True @@ -154,7 +155,6 @@ def get_imports(self, *, prefix: str) -> set[str]: imports = super().get_imports(prefix=prefix) imports.update( { - "from typing import Dict", "from typing import cast", } ) @@ -203,7 +203,7 @@ def get_type_string( if json: type_string = self.get_base_json_type_string() elif multipart: - type_string = "Tuple[None, bytes, str]" + type_string = "tuple[None, bytes, str]" else: type_string = self.get_base_type_string() diff --git a/openapi_python_client/parser/properties/protocol.py b/openapi_python_client/parser/properties/protocol.py index c9555949d..1315172b7 100644 --- a/openapi_python_client/parser/properties/protocol.py +++ b/openapi_python_client/parser/properties/protocol.py @@ -1,15 +1,18 @@ from __future__ import annotations +from openapi_python_client.parser.properties.schemas import ReferencePath + __all__ = ["PropertyProtocol", "Value"] from abc import abstractmethod from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, ClassVar, Protocol, TypeVar +from typing import TYPE_CHECKING, Any, ClassVar, Protocol, TypeVar, runtime_checkable from ... import Config from ... import schema as oai from ...utils import PythonIdentifier from ..errors import ParseError, PropertyError +from .schemas import Class if TYPE_CHECKING: # pragma: no cover from .model_property import ModelProperty @@ -116,7 +119,7 @@ def get_type_string( if json: type_string = self.get_base_json_type_string(quoted=quoted) elif multipart: - type_string = "Tuple[None, bytes, str]" + type_string = "tuple[None, bytes, str]" else: type_string = self.get_base_type_string(quoted=quoted) @@ -185,3 +188,16 @@ def is_base_type(self) -> bool: ListProperty.__name__, UnionProperty.__name__, } + + def get_ref_path(self) -> ReferencePath | None: + return self.ref_path if hasattr(self, "ref_path") else None + + +@runtime_checkable +class HasNamedClass(Protocol): + """ + This protocol is implemented by any property types that will have a corresponding Python + class in the generated code. Currently that is ModelProperty and UnionProperty. + """ + + class_info: Class diff --git a/openapi_python_client/parser/properties/schemas.py b/openapi_python_client/parser/properties/schemas.py index dad89a572..d1f4aeb3f 100644 --- a/openapi_python_client/parser/properties/schemas.py +++ b/openapi_python_client/parser/properties/schemas.py @@ -1,16 +1,16 @@ __all__ = [ "Class", - "Schemas", "Parameters", "ReferencePath", + "Schemas", + "parameter_from_data", + "parameter_from_reference", "parse_reference_path", - "update_schemas_with_data", "update_parameters_with_data", - "parameter_from_reference", - "parameter_from_data", + "update_schemas_with_data", ] -from typing import TYPE_CHECKING, Dict, List, NewType, Set, Tuple, Union, cast +from typing import TYPE_CHECKING, NewType, Union, cast from urllib.parse import urlparse from attrs import define, evolve, field @@ -46,6 +46,13 @@ def parse_reference_path(ref_path_raw: str) -> Union[ReferencePath, ParseError]: return cast(ReferencePath, parsed.fragment) +def get_reference_simple_name(ref_path: str) -> str: + """ + Takes a path like `/components/schemas/NameOfThing` and returns a string like `NameOfThing`. + """ + return ref_path.split("/", 3)[-1] + + @define class Class: """Represents Python class which will be generated from an OpenAPI schema""" @@ -56,7 +63,7 @@ class Class: @staticmethod def from_string(*, string: str, config: Config) -> "Class": """Get a Class from an arbitrary string""" - class_name = string.split("/")[-1] # Get rid of ref path stuff + class_name = get_reference_simple_name(string) # Get rid of ref path stuff class_name = ClassName(class_name, config.field_prefix) override = config.class_overrides.get(class_name) @@ -76,13 +83,13 @@ def from_string(*, string: str, config: Config) -> "Class": class Schemas: """Structure for containing all defined, shareable, and reusable schemas (attr classes and Enums)""" - classes_by_reference: Dict[ReferencePath, Property] = field(factory=dict) - dependencies: Dict[ReferencePath, Set[Union[ReferencePath, ClassName]]] = field(factory=dict) - classes_by_name: Dict[ClassName, Property] = field(factory=dict) - models_to_process: List[ModelProperty] = field(factory=list) - errors: List[ParseError] = field(factory=list) + classes_by_reference: dict[ReferencePath, Property] = field(factory=dict) + dependencies: dict[ReferencePath, set[Union[ReferencePath, ClassName]]] = field(factory=dict) + classes_by_name: dict[ClassName, Property] = field(factory=dict) + models_to_process: list[ModelProperty] = field(factory=list) + errors: list[ParseError] = field(factory=list) - def add_dependencies(self, ref_path: ReferencePath, roots: Set[Union[ReferencePath, ClassName]]) -> None: + def add_dependencies(self, ref_path: ReferencePath, roots: set[Union[ReferencePath, ClassName]]) -> None: """Record new dependencies on the given ReferencePath Args: @@ -135,6 +142,15 @@ def update_schemas_with_data( ) return prop + # Save the original path (/components/schemas/X) in the property. This is important because: + # 1. There are some contexts (such as a union with a discriminator) where we have a Property + # instance and we want to know what its path is, instead of the other way round. + # 2. Even though we did set prop.name to be the same as ref_path when we created it above, + # whenever there's a $ref to this property, we end up making a copy of it and changing + # the name. So we can't rely on prop.name always being the path. + if hasattr(prop, "ref_path"): + prop.ref_path = ref_path + schemas = evolve(schemas, classes_by_reference={ref_path: prop, **schemas.classes_by_reference}) return schemas @@ -143,9 +159,9 @@ def update_schemas_with_data( class Parameters: """Structure for containing all defined, shareable, and reusable parameters""" - classes_by_reference: Dict[ReferencePath, Parameter] = field(factory=dict) - classes_by_name: Dict[ClassName, Parameter] = field(factory=dict) - errors: List[ParseError] = field(factory=list) + classes_by_reference: dict[ReferencePath, Parameter] = field(factory=dict) + classes_by_name: dict[ClassName, Parameter] = field(factory=dict) + errors: list[ParseError] = field(factory=list) def parameter_from_data( @@ -154,7 +170,7 @@ def parameter_from_data( data: Union[oai.Reference, oai.Parameter], parameters: Parameters, config: Config, -) -> Tuple[Union[Parameter, ParameterError], Parameters]: +) -> tuple[Union[Parameter, ParameterError], Parameters]: """Generates parameters from an OpenAPI Parameter spec.""" if isinstance(data, oai.Reference): diff --git a/openapi_python_client/parser/properties/union.py b/openapi_python_client/parser/properties/union.py index 8b7b02a48..2cb9260ea 100644 --- a/openapi_python_client/parser/properties/union.py +++ b/openapi_python_client/parser/properties/union.py @@ -1,7 +1,7 @@ from __future__ import annotations from itertools import chain -from typing import Any, ClassVar, cast +from typing import Any, Callable, ClassVar, Mapping, OrderedDict, cast from attr import define, evolve @@ -9,8 +9,37 @@ from ... import schema as oai from ...utils import PythonIdentifier from ..errors import ParseError, PropertyError -from .protocol import PropertyProtocol, Value -from .schemas import Schemas +from .protocol import HasNamedClass, PropertyProtocol, Value +from .schemas import Schemas, get_reference_simple_name, parse_reference_path + + +@define +class DiscriminatorDefinition: + """Represents a discriminator that can optionally be specified for a union type. + + Normally, a UnionProperty has either zero or one of these. However, a nested union + could have more than one, as we accumulate all the discriminators when we flatten + out the nested schemas. For example: + + anyOf: + - anyOf: + - $ref: "#/components/schemas/Cat" + - $ref: "#/components/schemas/Dog" + discriminator: + propertyName: mammalType + - anyOf: + - $ref: "#/components/schemas/Condor" + - $ref: "#/components/schemas/Chicken" + discriminator: + propertyName: birdType + + In this example there are four schemas and two discriminators. The deserializer + logic will check for the mammalType property first, then birdType. + """ + + property_name: str + value_to_model_map: Mapping[str, PropertyProtocol] + # Every value in the map is really a ModelProperty, but this avoids circular imports @define @@ -24,6 +53,7 @@ class UnionProperty(PropertyProtocol): description: str | None example: str | None inner_properties: list[PropertyProtocol] + discriminators: list[DiscriminatorDefinition] | None = None template: ClassVar[str] = "union_property.py.jinja" @classmethod @@ -47,36 +77,96 @@ def build( """ from . import property_from_data - sub_properties: list[PropertyProtocol] = [] - type_list_data = [] - if isinstance(data.type, list): + if isinstance(data.type, list) and not (data.anyOf or data.oneOf): + # The schema specifies "type:" with a list of allowable types. If there is *not* also an "anyOf" + # or "oneOf", then we should treat that as a shorthand for a oneOf where each variant is just + # a single "type:". For example: + # {"type": ["string", "int"]} becomes + # {"oneOf": [{"type": "string"}, {"type": "int"}]} + # However, if there *is* also an "anyOf" or "oneOf" list, then the information from "type:" is + # redundant since every allowable variant type is already fully described in the list. for _type in data.type: type_list_data.append(data.model_copy(update={"type": _type, "default": None})) + # Here we're copying properties from the top-level union schema that might apply to one + # of the type variants, like "format" for a string. But we don't copy "default" because + # default values will be handled at the top level by the UnionProperty. - for i, sub_prop_data in enumerate(chain(data.anyOf, data.oneOf, type_list_data)): - sub_prop, schemas = property_from_data( - name=f"{name}_type_{i}", - required=True, - data=sub_prop_data, - schemas=schemas, - parent_name=parent_name, - config=config, - ) - if isinstance(sub_prop, PropertyError): - return PropertyError(detail=f"Invalid property in union {name}", data=sub_prop_data), schemas - sub_properties.append(sub_prop) + def _add_index_suffix_to_variant_names(index: int) -> str: + return f"{name}_type_{index}" + + def process_items( + variant_name_from_index_func: Callable[[int], str] = _add_index_suffix_to_variant_names, + ) -> tuple[list[PropertyProtocol] | PropertyError, Schemas]: + props: list[PropertyProtocol] = [] + new_schemas = schemas + for i, sub_prop_data in enumerate(chain(data.anyOf, data.oneOf, type_list_data)): + sub_prop_name = variant_name_from_index_func(i) + + # The sub_prop_name logic is what makes this a bit complicated. That value is used only + # if sub_prop is an *inline* schema and needs us to make up a name for it. For instance, + # in the following schema-- + # + # MyModel: + # properties: + # unionThing: + # oneOf: + # - type: object + # properties: ... + # - type: object + # properties: ... + # + # --both of the variants under oneOf are inline schemas. And since they're objects, we + # will be creating model classes for them, which need names. Inline schemas are named by + # concatenating names of parents; so, when we're in UnionProperty.build() for unionThing, + # the value of "name" is "my_model_union_thing", and then we set sub_prop_name to + # "my_model_union_thing_type_0" and "my_model_union_thing_type_1" for the two variants, + # and their model classes will be MyModelUnionThingType0 and MyModelUnionThingType1. + # + # However, in this example, if the second variant was just a scalar type instead of an + # object (like "type: null" or "type: string"), so that the first variant is the only + # one that needs a class... then it would be friendlier to call the first variant's + # class just MyModelUnionThing, not MyModelUnionThingType0. We'll check for that special + # case below; we can't know if that's the situation until after we've processed them all. + + sub_prop, new_schemas = property_from_data( + name=sub_prop_name, + required=True, + data=sub_prop_data, + schemas=new_schemas, + parent_name=parent_name, + config=config, + ) + if isinstance(sub_prop, PropertyError): + return PropertyError(detail=f"Invalid property in union {name}", data=sub_prop_data), new_schemas + props.append(sub_prop) + + return props, new_schemas + + sub_properties, new_schemas = process_items() + # Here's the check for the special case described above. If just one of the variants is + # an inline schema whose name matters, then we'll re-process them to simplify the naming. + # Unfortunately we do have to re-process them all; we can't just modify that one variant + # in place, because new_schemas already contains several references to its old name. + if ( + not isinstance(sub_properties, PropertyError) + and len([p for p in sub_properties if isinstance(p, HasNamedClass)]) == 1 + ): + already_processed_props = sub_properties + + def _use_same_name_as_parent_for_that_one_variant(index: int) -> str: + for i, p in enumerate(already_processed_props): + if i == index and isinstance(p, HasNamedClass): + return name + return _add_index_suffix_to_variant_names(index) - def flatten_union_properties(sub_properties: list[PropertyProtocol]) -> list[PropertyProtocol]: - flattened = [] - for sub_prop in sub_properties: - if isinstance(sub_prop, UnionProperty): - flattened.extend(flatten_union_properties(sub_prop.inner_properties)) - else: - flattened.append(sub_prop) - return flattened + sub_properties, new_schemas = process_items(_use_same_name_as_parent_for_that_one_variant) - sub_properties = flatten_union_properties(sub_properties) + if isinstance(sub_properties, PropertyError): + return sub_properties, schemas + schemas = new_schemas + + sub_properties, discriminators_from_nested_unions = _flatten_union_properties(sub_properties) prop = UnionProperty( name=name, @@ -92,6 +182,16 @@ def flatten_union_properties(sub_properties: list[PropertyProtocol]) -> list[Pro default_or_error.data = data return default_or_error, schemas prop = evolve(prop, default=default_or_error) + + all_discriminators = discriminators_from_nested_unions + if data.discriminator: + discriminator_or_error = _parse_discriminator(data.discriminator, sub_properties, schemas) + if isinstance(discriminator_or_error, PropertyError): + return discriminator_or_error, schemas + all_discriminators = [discriminator_or_error, *all_discriminators] + if all_discriminators: + prop = evolve(prop, discriminators=all_discriminators) + return prop, schemas def convert_value(self, value: Any) -> Value | None | PropertyError: @@ -189,3 +289,98 @@ def validate_location(self, location: oai.ParameterLocation) -> ParseError | Non if evolve(cast(Property, inner_prop), required=self.required).validate_location(location) is not None: return ParseError(detail=f"{self.get_type_string()} is not allowed in {location}") return None + + +def _flatten_union_properties( + sub_properties: list[PropertyProtocol], +) -> tuple[list[PropertyProtocol], list[DiscriminatorDefinition]]: + flattened = [] + discriminators = [] + for sub_prop in sub_properties: + if isinstance(sub_prop, UnionProperty): + if sub_prop.discriminators: + discriminators.extend(sub_prop.discriminators) + new_props, new_discriminators = _flatten_union_properties(sub_prop.inner_properties) + flattened.extend(new_props) + discriminators.extend(new_discriminators) + else: + flattened.append(sub_prop) + return flattened, discriminators + + +def _parse_discriminator( + data: oai.Discriminator, + subtypes: list[PropertyProtocol], + schemas: Schemas, +) -> DiscriminatorDefinition | PropertyError: + from .model_property import ModelProperty + + # See: https://spec.openapis.org/oas/v3.1.0.html#discriminator-object + + # Conditions that must be true when there is a discriminator: + # 1. Every type in the anyOf/oneOf list must be a $ref to a named schema, such as + # #/components/schemas/X, rather than an inline schema. This is important because + # we may need to use the schema's simple name (X). + # 2. There must be a propertyName, representing a property that exists in every + # schema in that list (although we can't currently enforce the latter condition, + # because those properties haven't been parsed yet at this point.) + # + # There *may* also be a mapping of lookup values (the possible values of the property) + # to schemas. Schemas can be referenced either by a full path or a name: + # mapping: + # value_for_a: "#/components/schemas/ModelA" + # value_for_b: ModelB # equivalent to "#/components/schemas/ModelB" + # + # For any type that isn't specified in the mapping (or if the whole mapping is omitted) + # the default lookup value for each schema is the same as the schema name. So this-- + # mapping: + # value_for_a: "#/components/schemas/ModelA" + # --is exactly equivalent to this: + # discriminator: + # propertyName: modelType + # mapping: + # value_for_a: "#/components/schemas/ModelA" + # ModelB: "#/components/schemas/ModelB" + + def _get_model_name(model: ModelProperty) -> str | None: + return get_reference_simple_name(model.ref_path) if model.ref_path else None + + model_types_by_name: dict[str, ModelProperty] = {} + for model in subtypes: + # Note, model here can never be a UnionProperty, because we've already done + # flatten_union_properties() before this point. + if not isinstance(model, ModelProperty): + return PropertyError( + detail="All schema variants must be objects when using a discriminator", + ) + name = _get_model_name(model) + if not name: + return PropertyError( + detail="Inline schema declarations are not allowed when using a discriminator", + ) + model_types_by_name[name] = model + + mapping: dict[str, ModelProperty] = OrderedDict() # use ordered dict for test determinacy + unspecified_models = list(model_types_by_name.values()) + if data.mapping: + for discriminator_value, model_ref in data.mapping.items(): + if "/" in model_ref: + ref_path = parse_reference_path(model_ref) + if isinstance(ref_path, ParseError) or ref_path not in schemas.classes_by_reference: + return PropertyError(detail=f'Invalid reference "{model_ref}" in discriminator mapping') + name = get_reference_simple_name(ref_path) + else: + name = model_ref + mapped_model = model_types_by_name.get(name) + if not mapped_model: + return PropertyError( + detail=f'Discriminator mapping referred to "{name}" which is not one of the schema variants', + ) + mapping[discriminator_value] = mapped_model + if mapped_model in unspecified_models: + # could've already been removed if more than one value is mapped to the same model + unspecified_models.remove(mapped_model) + for model in unspecified_models: + if name := _get_model_name(model): + mapping[name] = model + return DiscriminatorDefinition(property_name=data.propertyName, value_to_model_map=mapping) diff --git a/openapi_python_client/parser/responses.py b/openapi_python_client/parser/responses.py index 32412fd35..ec0f6136b 100644 --- a/openapi_python_client/parser/responses.py +++ b/openapi_python_client/parser/responses.py @@ -1,11 +1,12 @@ __all__ = ["Response", "response_from_data"] from http import HTTPStatus -from typing import Optional, Tuple, TypedDict, Union +from typing import Optional, TypedDict, Union from attrs import define from openapi_python_client import utils +from openapi_python_client.parser.properties.schemas import get_reference_simple_name, parse_reference_path from .. import Config from .. import schema as oai @@ -79,27 +80,30 @@ def empty_response( ) -def response_from_data( +def response_from_data( # noqa: PLR0911 *, status_code: HTTPStatus, data: Union[oai.Response, oai.Reference], schemas: Schemas, + responses: dict[str, Union[oai.Response, oai.Reference]], parent_name: str, config: Config, -) -> Tuple[Union[Response, ParseError], Schemas]: +) -> tuple[Union[Response, ParseError], Schemas]: """Generate a Response from the OpenAPI dictionary representation of it""" response_name = f"response_{status_code}" if isinstance(data, oai.Reference): - return ( - empty_response( - status_code=status_code, - response_name=response_name, - config=config, - data=data, - ), - schemas, - ) + ref_path = parse_reference_path(data.ref) + if isinstance(ref_path, ParseError): + return ref_path, schemas + if not ref_path.startswith("/components/responses/"): + return ParseError(data=data, detail=f"$ref to {data.ref} not allowed in responses"), schemas + resp_data = responses.get(get_reference_simple_name(ref_path), None) + if not resp_data: + return ParseError(data=data, detail=f"Could not find reference: {data.ref}"), schemas + if not isinstance(resp_data, oai.Response): + return ParseError(data=data, detail="Top-level $ref inside components/responses is not supported"), schemas + data = resp_data content = data.content if not content: diff --git a/openapi_python_client/schema/__init__.py b/openapi_python_client/schema/__init__.py index d3de0e493..021007ade 100644 --- a/openapi_python_client/schema/__init__.py +++ b/openapi_python_client/schema/__init__.py @@ -1,12 +1,13 @@ __all__ = [ + "DataType", + "Discriminator", "MediaType", "OpenAPI", "Operation", "Parameter", + "Parameter", "ParameterLocation", - "DataType", "PathItem", - "Parameter", "Reference", "RequestBody", "Response", @@ -17,6 +18,7 @@ from .data_type import DataType from .openapi_schema_pydantic import ( + Discriminator, MediaType, OpenAPI, Operation, diff --git a/openapi_python_client/schema/openapi_schema_pydantic/__init__.py b/openapi_python_client/schema/openapi_schema_pydantic/__init__.py index 6b02446a8..b61cefc66 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/__init__.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/__init__.py @@ -7,6 +7,8 @@ """ __all__ = [ + "XML", + "Callback", "Components", "Contact", "Discriminator", @@ -35,8 +37,6 @@ "Server", "ServerVariable", "Tag", - "XML", - "Callback", ] @@ -70,3 +70,14 @@ from .server_variable import ServerVariable from .tag import Tag from .xml import XML + +PathItem.model_rebuild() +Operation.model_rebuild() +Components.model_rebuild() +Encoding.model_rebuild() +MediaType.model_rebuild() +OpenAPI.model_rebuild() +Parameter.model_rebuild() +Header.model_rebuild() +RequestBody.model_rebuild() +Response.model_rebuild() diff --git a/openapi_python_client/schema/openapi_schema_pydantic/callback.py b/openapi_python_client/schema/openapi_schema_pydantic/callback.py index 22426d925..f4593cc8d 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/callback.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/callback.py @@ -1,11 +1,11 @@ -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING if TYPE_CHECKING: # pragma: no cover from .path_item import PathItem else: PathItem = "PathItem" -Callback = Dict[str, PathItem] +Callback = dict[str, PathItem] """ A map of possible out-of band callbacks related to the parent operation. Each value in the map is a [Path Item Object](#pathItemObject) diff --git a/openapi_python_client/schema/openapi_schema_pydantic/components.py b/openapi_python_client/schema/openapi_schema_pydantic/components.py index f366a2ec8..babe26265 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/components.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/components.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Union +from typing import Optional, Union from pydantic import BaseModel, ConfigDict @@ -25,16 +25,18 @@ class Components(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#componentsObject """ - schemas: Optional[Dict[str, Union[Schema, Reference]]] = None - responses: Optional[Dict[str, Union[Response, Reference]]] = None - parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None - examples: Optional[Dict[str, Union[Example, Reference]]] = None - requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None - headers: Optional[Dict[str, Union[Header, Reference]]] = None - securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None - links: Optional[Dict[str, Union[Link, Reference]]] = None - callbacks: Optional[Dict[str, Union[Callback, Reference]]] = None + schemas: Optional[dict[str, Union[Schema, Reference]]] = None + responses: Optional[dict[str, Union[Response, Reference]]] = None + parameters: Optional[dict[str, Union[Parameter, Reference]]] = None + examples: Optional[dict[str, Union[Example, Reference]]] = None + requestBodies: Optional[dict[str, Union[RequestBody, Reference]]] = None + headers: Optional[dict[str, Union[Header, Reference]]] = None + securitySchemes: Optional[dict[str, Union[SecurityScheme, Reference]]] = None + links: Optional[dict[str, Union[Link, Reference]]] = None + callbacks: Optional[dict[str, Union[Callback, Reference]]] = None model_config = ConfigDict( + # `Callback` contains an unresolvable forward reference, will rebuild in `__init__.py`: + defer_build=True, extra="allow", json_schema_extra={ "examples": [ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py b/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py index 95161d07a..9f36773ba 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict @@ -19,7 +19,7 @@ class Discriminator(BaseModel): """ propertyName: str - mapping: Optional[Dict[str, str]] = None + mapping: Optional[dict[str, str]] = None model_config = ConfigDict( extra="allow", json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/encoding.py b/openapi_python_client/schema/openapi_schema_pydantic/encoding.py index b7434c50c..ebf6295dc 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/encoding.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/encoding.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Dict, Optional, Union +from typing import TYPE_CHECKING, Optional, Union from pydantic import BaseModel, ConfigDict @@ -6,8 +6,6 @@ if TYPE_CHECKING: # pragma: no cover from .header import Header -else: - Header = "Header" class Encoding(BaseModel): @@ -19,11 +17,13 @@ class Encoding(BaseModel): """ contentType: Optional[str] = None - headers: Optional[Dict[str, Union[Header, Reference]]] = None + headers: Optional[dict[str, Union["Header", Reference]]] = None style: Optional[str] = None explode: bool = False allowReserved: bool = False model_config = ConfigDict( + # `Header` is an unresolvable forward reference, will rebuild in `__init__.py`: + defer_build=True, extra="allow", json_schema_extra={ "examples": [ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/header.py b/openapi_python_client/schema/openapi_schema_pydantic/header.py index 3223c199b..2deb6f390 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/header.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/header.py @@ -21,6 +21,8 @@ class Header(Parameter): name: str = Field(default="") param_in: ParameterLocation = Field(default=ParameterLocation.HEADER, alias="in") model_config = ConfigDict( + # `Parameter` is not build yet, will rebuild in `__init__.py`: + defer_build=True, extra="allow", populate_by_name=True, json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/link.py b/openapi_python_client/schema/openapi_schema_pydantic/link.py index 9f823c4a2..69cdf29c0 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/link.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/link.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any, Optional from pydantic import BaseModel, ConfigDict @@ -25,7 +25,7 @@ class Link(BaseModel): operationRef: Optional[str] = None operationId: Optional[str] = None - parameters: Optional[Dict[str, Any]] = None + parameters: Optional[dict[str, Any]] = None requestBody: Optional[Any] = None description: Optional[str] = None server: Optional[Server] = None diff --git a/openapi_python_client/schema/openapi_schema_pydantic/media_type.py b/openapi_python_client/schema/openapi_schema_pydantic/media_type.py index 1bda99560..95f9ede14 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/media_type.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/media_type.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union from pydantic import BaseModel, ConfigDict, Field @@ -18,9 +18,11 @@ class MediaType(BaseModel): media_type_schema: Optional[Union[Reference, Schema]] = Field(default=None, alias="schema") example: Optional[Any] = None - examples: Optional[Dict[str, Union[Example, Reference]]] = None - encoding: Optional[Dict[str, Encoding]] = None + examples: Optional[dict[str, Union[Example, Reference]]] = None + encoding: Optional[dict[str, Encoding]] = None model_config = ConfigDict( + # `Encoding` is not build yet, will rebuild in `__init__.py`: + defer_build=True, extra="allow", populate_by_name=True, json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py index c7485814f..16e366090 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict @@ -15,7 +15,7 @@ class OAuthFlow(BaseModel): authorizationUrl: Optional[str] = None tokenUrl: Optional[str] = None refreshUrl: Optional[str] = None - scopes: Dict[str, str] + scopes: dict[str, str] model_config = ConfigDict( extra="allow", json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/open_api.py b/openapi_python_client/schema/openapi_schema_pydantic/open_api.py index 6a1b5ae12..e66ea942c 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/open_api.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/open_api.py @@ -1,13 +1,10 @@ -from typing import List, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict, field_validator from .components import Components from .external_documentation import ExternalDocumentation from .info import Info - -# Required to update forward ref after object creation -from .path_item import PathItem # noqa: F401 from .paths import Paths from .security_requirement import SecurityRequirement from .server import Server @@ -25,14 +22,18 @@ class OpenAPI(BaseModel): """ info: Info - servers: List[Server] = [Server(url="/")] + servers: list[Server] = [Server(url="/")] paths: Paths components: Optional[Components] = None - security: Optional[List[SecurityRequirement]] = None - tags: Optional[List[Tag]] = None + security: Optional[list[SecurityRequirement]] = None + tags: Optional[list[Tag]] = None externalDocs: Optional[ExternalDocumentation] = None openapi: str - model_config = ConfigDict(extra="allow") + model_config = ConfigDict( + # `Components` is not build yet, will rebuild in `__init__.py`: + defer_build=True, + extra="allow", + ) @field_validator("openapi") @classmethod @@ -46,6 +47,3 @@ def check_openapi_version(cls, value: str) -> str: if int(parts[1]) > 1: raise ValueError(f"Only OpenAPI versions 3.1.* are supported, got {value}") return value - - -OpenAPI.model_rebuild() diff --git a/openapi_python_client/schema/openapi_schema_pydantic/operation.py b/openapi_python_client/schema/openapi_schema_pydantic/operation.py index 41f5e7100..ebf5e1faa 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/operation.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/operation.py @@ -1,14 +1,10 @@ -from typing import Dict, List, Optional, Union +from typing import Optional, Union from pydantic import BaseModel, ConfigDict, Field from .callback import Callback from .external_documentation import ExternalDocumentation -from .header import Header # noqa: F401 from .parameter import Parameter - -# Required to update forward ref after object creation, as this is not imported yet -from .path_item import PathItem # noqa: F401 from .reference import Reference from .request_body import RequestBody from .responses import Responses @@ -24,20 +20,22 @@ class Operation(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operationObject """ - tags: Optional[List[str]] = None + tags: Optional[list[str]] = None summary: Optional[str] = None description: Optional[str] = None externalDocs: Optional[ExternalDocumentation] = None operationId: Optional[str] = None - parameters: Optional[List[Union[Parameter, Reference]]] = None + parameters: Optional[list[Union[Parameter, Reference]]] = None request_body: Optional[Union[RequestBody, Reference]] = Field(None, alias="requestBody") responses: Responses - callbacks: Optional[Dict[str, Callback]] = None + callbacks: Optional[dict[str, Callback]] = None deprecated: bool = False - security: Optional[List[SecurityRequirement]] = None - servers: Optional[List[Server]] = None + security: Optional[list[SecurityRequirement]] = None + servers: Optional[list[Server]] = None model_config = ConfigDict( + # `Callback` contains an unresolvable forward reference, will rebuild in `__init__.py`: + defer_build=True, extra="allow", json_schema_extra={ "examples": [ @@ -89,7 +87,3 @@ class Operation(BaseModel): ] }, ) - - -# PathItem in Callback uses Operation, so we need to update forward refs due to circular dependency -Operation.model_rebuild() diff --git a/openapi_python_client/schema/openapi_schema_pydantic/parameter.py b/openapi_python_client/schema/openapi_schema_pydantic/parameter.py index 25ba819f1..6f6fe9342 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/parameter.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/parameter.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union from pydantic import BaseModel, ConfigDict, Field @@ -32,9 +32,11 @@ class Parameter(BaseModel): allowReserved: bool = False param_schema: Optional[Union[Reference, Schema]] = Field(default=None, alias="schema") example: Optional[Any] = None - examples: Optional[Dict[str, Union[Example, Reference]]] = None - content: Optional[Dict[str, MediaType]] = None + examples: Optional[dict[str, Union[Example, Reference]]] = None + content: Optional[dict[str, MediaType]] = None model_config = ConfigDict( + # `MediaType` is not build yet, will rebuild in `__init__.py`: + defer_build=True, extra="allow", populate_by_name=True, json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/path_item.py b/openapi_python_client/schema/openapi_schema_pydantic/path_item.py index 36edee0e3..8c1eab6ea 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/path_item.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/path_item.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Union +from typing import TYPE_CHECKING, Optional, Union from pydantic import BaseModel, ConfigDict, Field @@ -6,6 +6,9 @@ from .reference import Reference from .server import Server +if TYPE_CHECKING: + from .operation import Operation # pragma: no cover + class PathItem(BaseModel): """ @@ -30,9 +33,11 @@ class PathItem(BaseModel): head: Optional["Operation"] = None patch: Optional["Operation"] = None trace: Optional["Operation"] = None - servers: Optional[List[Server]] = None - parameters: Optional[List[Union[Parameter, Reference]]] = None + servers: Optional[list[Server]] = None + parameters: Optional[list[Union[Parameter, Reference]]] = None model_config = ConfigDict( + # `Operation` is an unresolvable forward reference, will rebuild in `__init__.py`: + defer_build=True, extra="allow", populate_by_name=True, json_schema_extra={ @@ -69,9 +74,3 @@ class PathItem(BaseModel): ] }, ) - - -# Operation uses PathItem via Callback, so we need late import and to update forward refs due to circular dependency -from .operation import Operation # noqa: E402 - -PathItem.model_rebuild() diff --git a/openapi_python_client/schema/openapi_schema_pydantic/paths.py b/openapi_python_client/schema/openapi_schema_pydantic/paths.py index d61ea7b18..86c1dfd19 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/paths.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/paths.py @@ -1,8 +1,6 @@ -from typing import Dict - from .path_item import PathItem -Paths = Dict[str, PathItem] +Paths = dict[str, PathItem] """ Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the [`Server Object`](#serverObject) in order to construct the full URL. diff --git a/openapi_python_client/schema/openapi_schema_pydantic/request_body.py b/openapi_python_client/schema/openapi_schema_pydantic/request_body.py index 6b1847215..8cd9bb527 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/request_body.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/request_body.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict @@ -14,9 +14,11 @@ class RequestBody(BaseModel): """ description: Optional[str] = None - content: Dict[str, MediaType] + content: dict[str, MediaType] required: bool = False model_config = ConfigDict( + # `MediaType` is not build yet, will rebuild in `__init__.py`: + defer_build=True, extra="allow", json_schema_extra={ "examples": [ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/response.py b/openapi_python_client/schema/openapi_schema_pydantic/response.py index a7c5d08ec..b7ec0d357 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/response.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/response.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Union +from typing import Optional, Union from pydantic import BaseModel, ConfigDict @@ -19,10 +19,12 @@ class Response(BaseModel): """ description: str - headers: Optional[Dict[str, Union[Header, Reference]]] = None - content: Optional[Dict[str, MediaType]] = None - links: Optional[Dict[str, Union[Link, Reference]]] = None + headers: Optional[dict[str, Union[Header, Reference]]] = None + content: Optional[dict[str, MediaType]] = None + links: Optional[dict[str, Union[Link, Reference]]] = None model_config = ConfigDict( + # `MediaType` is not build yet, will rebuild in `__init__.py`: + defer_build=True, extra="allow", json_schema_extra={ "examples": [ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/responses.py b/openapi_python_client/schema/openapi_schema_pydantic/responses.py index 53306ae1c..17ddc13fe 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/responses.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/responses.py @@ -1,9 +1,9 @@ -from typing import Dict, Union +from typing import Union from .reference import Reference from .response import Response -Responses = Dict[str, Union[Response, Reference]] +Responses = dict[str, Union[Response, Reference]] """ A container for the expected responses of an operation. The container maps a HTTP response code to the expected response. diff --git a/openapi_python_client/schema/openapi_schema_pydantic/schema.py b/openapi_python_client/schema/openapi_schema_pydantic/schema.py index d42ccc6ad..99c64eb51 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/schema.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/schema.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictFloat, StrictInt, StrictStr, model_validator @@ -34,17 +34,17 @@ class Schema(BaseModel): uniqueItems: Optional[bool] = None maxProperties: Optional[int] = Field(default=None, ge=0) minProperties: Optional[int] = Field(default=None, ge=0) - required: Optional[List[str]] = Field(default=None) - enum: Union[None, List[Any]] = Field(default=None, min_length=1) + required: Optional[list[str]] = Field(default=None) + enum: Union[None, list[Any]] = Field(default=None, min_length=1) const: Union[None, StrictStr, StrictInt, StrictFloat, StrictBool] = None - type: Union[DataType, List[DataType], None] = Field(default=None) - allOf: List[Union[Reference, "Schema"]] = Field(default_factory=list) - oneOf: List[Union[Reference, "Schema"]] = Field(default_factory=list) - anyOf: List[Union[Reference, "Schema"]] = Field(default_factory=list) + type: Union[DataType, list[DataType], None] = Field(default=None) + allOf: list[Union[Reference, "Schema"]] = Field(default_factory=list) + oneOf: list[Union[Reference, "Schema"]] = Field(default_factory=list) + anyOf: list[Union[Reference, "Schema"]] = Field(default_factory=list) schema_not: Optional[Union[Reference, "Schema"]] = Field(default=None, alias="not") items: Optional[Union[Reference, "Schema"]] = None - prefixItems: Optional[List[Union[Reference, "Schema"]]] = Field(default_factory=list) - properties: Optional[Dict[str, Union[Reference, "Schema"]]] = None + prefixItems: list[Union[Reference, "Schema"]] = Field(default_factory=list) + properties: Optional[dict[str, Union[Reference, "Schema"]]] = None additionalProperties: Optional[Union[bool, Reference, "Schema"]] = None description: Optional[str] = None schema_format: Optional[str] = Field(default=None, alias="format") @@ -206,6 +206,3 @@ def handle_nullable(self) -> "Schema": self.oneOf = [Schema(type=DataType.NULL), Schema(allOf=self.allOf)] self.allOf = [] return self - - -Schema.model_rebuild() diff --git a/openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py b/openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py index b3cca3b08..58a487dc7 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py @@ -1,6 +1,4 @@ -from typing import Dict, List - -SecurityRequirement = Dict[str, List[str]] +SecurityRequirement = dict[str, list[str]] """ Lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the diff --git a/openapi_python_client/schema/openapi_schema_pydantic/server.py b/openapi_python_client/schema/openapi_schema_pydantic/server.py index d573a93fe..6bc21766c 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/server.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/server.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict @@ -15,7 +15,7 @@ class Server(BaseModel): url: str description: Optional[str] = None - variables: Optional[Dict[str, ServerVariable]] = None + variables: Optional[dict[str, ServerVariable]] = None model_config = ConfigDict( extra="allow", json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py b/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py index 3b63c9ad2..8a869c40e 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict @@ -11,7 +11,7 @@ class ServerVariable(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#serverVariableObject """ - enum: Optional[List[str]] = None + enum: Optional[list[str]] = None default: str description: Optional[str] = None model_config = ConfigDict(extra="allow") diff --git a/openapi_python_client/templates/client.py.jinja b/openapi_python_client/templates/client.py.jinja index 4f224e6e8..cf0301a9a 100644 --- a/openapi_python_client/templates/client.py.jinja +++ b/openapi_python_client/templates/client.py.jinja @@ -1,10 +1,35 @@ import ssl -from typing import Any, Dict, Union, Optional +from typing import Any, Union, Optional from attrs import define, field, evolve import httpx +{% set attrs_info = { + "raise_on_unexpected_status": namespace( + type="bool", + default="field(default=False, kw_only=True)", + docstring="Whether or not to raise an errors.UnexpectedStatus if the API returns a status code" + " that was not documented in the source OpenAPI document. Can also be provided as a keyword" + " argument to the constructor." + ), + "token": namespace(type="str", default="", docstring="The token to use for authentication"), + "prefix": namespace(type="str", default='"Bearer"', docstring="The prefix to use for the Authorization header"), + "auth_header_name": namespace(type="str", default='"Authorization"', docstring="The name of the Authorization header"), +} %} + +{% macro attr_in_class_docstring(name) %} +{{ name }}: {{ attrs_info[name].docstring }} +{%- endmacro %} + +{% macro declare_attr(name) %} +{% set attr = attrs_info[name] %} +{{ name }}: {{ attr.type }}{% if attr.default %} = {{ attr.default }}{% endif %} +{% if attr.docstring and config.docstrings_on_attributes +%} +"""{{ attr.docstring }}""" +{%- endif %} +{% endmacro %} + @define class Client: """A class for keeping track of data related to the API @@ -29,26 +54,26 @@ class Client: ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor. {% endmacro %} {{ httpx_args_docstring() }} +{% if not config.docstrings_on_attributes %} Attributes: - raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a - status code that was not documented in the source OpenAPI document. Can also be provided as a keyword - argument to the constructor. + {{ attr_in_class_docstring("raise_on_unexpected_status") | wordwrap(101) | indent(12) }} +{% endif %} """ {% macro attributes() %} - raise_on_unexpected_status: bool = field(default=False, kw_only=True) + {{ declare_attr("raise_on_unexpected_status") | indent(4) }} _base_url: str = field(alias="base_url") - _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") - _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") - _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) {% endmacro %}{{ attributes() }} {% macro builders(self) %} - def with_headers(self, headers: Dict[str, str]) -> "{{ self }}": + def with_headers(self, headers: dict[str, str]) -> "{{ self }}": """Get a new client matching this one with additional headers""" if self._client is not None: self._client.headers.update(headers) @@ -56,7 +81,7 @@ class Client: self._async_client.headers.update(headers) return evolve(self, headers={**self._headers, **headers}) - def with_cookies(self, cookies: Dict[str, str]) -> "{{ self }}": + def with_cookies(self, cookies: dict[str, str]) -> "{{ self }}": """Get a new client matching this one with additional cookies""" if self._client is not None: self._client.cookies.update(cookies) @@ -147,20 +172,20 @@ class AuthenticatedClient: """A Client which has been authenticated for use on secured endpoints {{ httpx_args_docstring() }} +{% if not config.docstrings_on_attributes %} Attributes: - raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a - status code that was not documented in the source OpenAPI document. Can also be provided as a keyword - argument to the constructor. - token: The token to use for authentication - prefix: The prefix to use for the Authorization header - auth_header_name: The name of the Authorization header + {{ attr_in_class_docstring("raise_on_unexpected_status") | wordwrap(101) | indent(12) }} + {{ attr_in_class_docstring("token") | indent(8) }} + {{ attr_in_class_docstring("prefix") | indent(8) }} + {{ attr_in_class_docstring("auth_header_name") | indent(8) }} +{% endif %} """ {{ attributes() }} - token: str - prefix: str = "Bearer" - auth_header_name: str = "Authorization" + {{ declare_attr("token") | indent(4) }} + {{ declare_attr("prefix") | indent(4) }} + {{ declare_attr("auth_header_name") | indent(4) }} {{ builders("AuthenticatedClient") }} {{ httpx_stuff("AuthenticatedClient", "self._headers[self.auth_header_name] = f\"{self.prefix} {self.token}\" if self.prefix else self.token") }} diff --git a/openapi_python_client/templates/endpoint_macros.py.jinja b/openapi_python_client/templates/endpoint_macros.py.jinja index 79ca33d57..2da580af4 100644 --- a/openapi_python_client/templates/endpoint_macros.py.jinja +++ b/openapi_python_client/templates/endpoint_macros.py.jinja @@ -3,7 +3,7 @@ {% macro header_params(endpoint) %} {% if endpoint.header_parameters or endpoint.bodies | length > 0 %} -headers: Dict[str, Any] = {} +headers: dict[str, Any] = {} {% if endpoint.header_parameters %} {% for parameter in endpoint.header_parameters %} {% import "property_templates/" + parameter.template as param_template %} @@ -37,7 +37,7 @@ if {{ parameter.python_name }} is not UNSET: {% macro query_params(endpoint) %} {% if endpoint.query_parameters %} -params: Dict[str, Any] = {} +params: dict[str, Any] = {} {% for property in endpoint.query_parameters %} {% set destination = property.python_name %} diff --git a/openapi_python_client/templates/endpoint_module.py.jinja b/openapi_python_client/templates/endpoint_module.py.jinja index d89f09367..35090614c 100644 --- a/openapi_python_client/templates/endpoint_module.py.jinja +++ b/openapi_python_client/templates/endpoint_module.py.jinja @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Optional, Union, cast import httpx @@ -19,14 +19,14 @@ from ... import errors def _get_kwargs( {{ arguments(endpoint, include_client=False) | indent(4) }} -) -> Dict[str, Any]: +) -> dict[str, Any]: {{ header_params(endpoint) | indent(4) }} {{ cookie_params(endpoint) | indent(4) }} {{ query_params(endpoint) | indent(4) }} - _kwargs: Dict[str, Any] = { + _kwargs: dict[str, Any] = { "method": "{{ endpoint.method }}", {% if endpoint.path_parameters %} "url": "{{ endpoint.path }}".format( diff --git a/openapi_python_client/templates/helpers.jinja b/openapi_python_client/templates/helpers.jinja index 180613c02..fd5c3ec86 100644 --- a/openapi_python_client/templates/helpers.jinja +++ b/openapi_python_client/templates/helpers.jinja @@ -1,8 +1,10 @@ -{% macro safe_docstring(content) %} +{% macro safe_docstring(content, omit_if_empty=False) %} {# This macro returns the provided content as a docstring, set to a raw string if it contains a backslash #} +{% if (not omit_if_empty) or (content | trim) %} {% if '\\' in content -%} r""" {{ content }} """ {%- else -%} """ {{ content }} """ {%- endif -%} +{% endif %} {% endmacro %} \ No newline at end of file diff --git a/openapi_python_client/templates/literal_enum.py.jinja b/openapi_python_client/templates/literal_enum.py.jinja index df993adb7..72207efa3 100644 --- a/openapi_python_client/templates/literal_enum.py.jinja +++ b/openapi_python_client/templates/literal_enum.py.jinja @@ -1,8 +1,8 @@ -from typing import Literal, Set, cast +from typing import Literal, cast {{ enum.class_info.name }} = Literal{{ "%r" | format(enum.values|list|sort) }} -{{ enum.get_class_name_snake_case() | upper }}_VALUES: Set[{{ enum.class_info.name }}] = { {% for v in enum.values|list|sort %}{{"%r"|format(v)}}, {% endfor %} } +{{ enum.get_class_name_snake_case() | upper }}_VALUES: set[{{ enum.class_info.name }}] = { {% for v in enum.values|list|sort %}{{"%r"|format(v)}}, {% endfor %} } def check_{{ enum.get_class_name_snake_case() }}(value: {{ enum.get_instance_type_string() }}) -> {{ enum.class_info.name}}: if value in {{ enum.get_class_name_snake_case() | upper }}_VALUES: diff --git a/openapi_python_client/templates/model.py.jinja b/openapi_python_client/templates/model.py.jinja index 012201426..6cd3fca5e 100644 --- a/openapi_python_client/templates/model.py.jinja +++ b/openapi_python_client/templates/model.py.jinja @@ -1,9 +1,4 @@ -from typing import Any, Dict, Type, TypeVar, Tuple, Optional, BinaryIO, TextIO, TYPE_CHECKING - -{% if model.additional_properties %} -from typing import List - -{% endif %} +from typing import Any, TypeVar, Optional, BinaryIO, TextIO, TYPE_CHECKING from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -52,29 +47,38 @@ T = TypeVar("T", bound="{{ class_name }}") {{ model.example | string | wordwrap(112) | indent(12) }} {% endif %} - {% if model.required_properties or model.optional_properties %} + {% if (not config.docstrings_on_attributes) and (model.required_properties or model.optional_properties) %} Attributes: {% for property in model.required_properties + model.optional_properties %} {{ property.to_docstring() | wordwrap(112) | indent(12) }} {% endfor %}{% endif %} {% endmacro %} +{% macro declare_property(property) %} +{%- if config.docstrings_on_attributes and property.description -%} +{{ property.to_string() }} +{{ safe_docstring(property.description, omit_if_empty=True) | wordwrap(112) }} +{%- else -%} +{{ property.to_string() }} +{%- endif -%} +{% endmacro %} + @_attrs_define class {{ class_name }}: - {{ safe_docstring(class_docstring_content(model)) | indent(4) }} + {{ safe_docstring(class_docstring_content(model), omit_if_empty=config.docstrings_on_attributes) | indent(4) }} {% for property in model.required_properties + model.optional_properties %} {% if property.default is none and property.required %} - {{ property.to_string() }} + {{ declare_property(property) | indent(4) }} {% endif %} {% endfor %} {% for property in model.required_properties + model.optional_properties %} {% if property.default is not none or not property.required %} - {{ property.to_string() }} + {{ declare_property(property) | indent(4) }} {% endif %} {% endfor %} {% if model.additional_properties %} - additional_properties: Dict[str, {{ additional_property_type }}] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, {{ additional_property_type }}] = _attrs_field(init=False, factory=dict) {% endif %} {% macro _to_dict(multipart=False) %} @@ -90,7 +94,7 @@ class {{ class_name }}: {% endfor %} -field_dict: Dict[str, Any] = {} +field_dict: dict[str, Any] = {} {% if model.additional_properties %} {% import "property_templates/" + model.additional_properties.template as prop_template %} {% if multipart %} @@ -122,19 +126,19 @@ if {{ property.python_name }} is not UNSET: return field_dict {% endmacro %} - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: {% for lazy_import in model.lazy_imports %} {{ lazy_import }} {% endfor %} {{ _to_dict() | indent(8) }} {% if model.is_multipart_body %} - def to_multipart(self) -> Dict[str, Any]: + def to_multipart(self) -> dict[str, Any]: {{ _to_dict(multipart=True) | indent(8) }} {% endif %} @classmethod - def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: {% for lazy_import in model.lazy_imports %} {{ lazy_import }} {% endfor %} @@ -188,7 +192,7 @@ return field_dict {% if model.additional_properties %} @property - def additional_keys(self) -> List[str]: + def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> {{ additional_property_type }}: diff --git a/openapi_python_client/templates/property_templates/enum_property.py.jinja b/openapi_python_client/templates/property_templates/enum_property.py.jinja index ea9b66a51..c46538eac 100644 --- a/openapi_python_client/templates/property_templates/enum_property.py.jinja +++ b/openapi_python_client/templates/property_templates/enum_property.py.jinja @@ -24,7 +24,7 @@ if not isinstance({{ source }}, Unset): {% macro transform_multipart(property, source, destination) %} {% set transformed = "(None, str(" + source + ".value" + ").encode(), \"text/plain\")" %} -{% set type_string = "Union[Unset, Tuple[None, bytes, str]]" %} +{% set type_string = "Union[Unset, tuple[None, bytes, str]]" %} {% if property.required %} {{ destination }} = {{ transformed }} {%- else %} diff --git a/openapi_python_client/templates/property_templates/list_property.py.jinja b/openapi_python_client/templates/property_templates/list_property.py.jinja index c827b6d54..94a9c6d65 100644 --- a/openapi_python_client/templates/property_templates/list_property.py.jinja +++ b/openapi_python_client/templates/property_templates/list_property.py.jinja @@ -54,7 +54,7 @@ if not isinstance({{ source }}, Unset): {% macro transform_multipart(property, source, destination) %} {% set inner_property = property.inner_property %} -{% set type_string = "Union[Unset, Tuple[None, bytes, str]]" %} +{% set type_string = "Union[Unset, tuple[None, bytes, str]]" %} {% if property.required %} {{ _transform(property, source, destination, True, "to_dict") }} {% else %} diff --git a/openapi_python_client/templates/property_templates/literal_enum_property.py.jinja b/openapi_python_client/templates/property_templates/literal_enum_property.py.jinja index 680ebfabe..1506284d7 100644 --- a/openapi_python_client/templates/property_templates/literal_enum_property.py.jinja +++ b/openapi_python_client/templates/property_templates/literal_enum_property.py.jinja @@ -23,7 +23,7 @@ if not isinstance({{ source }}, Unset): {% macro transform_multipart(property, source, destination) %} {% set transformed = "(None, str(" + source + ").encode(), \"text/plain\")" %} -{% set type_string = "Union[Unset, Tuple[None, bytes, str]]" %} +{% set type_string = "Union[Unset, tuple[None, bytes, str]]" %} {% if property.required %} {{ destination }} = {{ transformed }} {%- else %} diff --git a/openapi_python_client/templates/property_templates/union_property.py.jinja b/openapi_python_client/templates/property_templates/union_property.py.jinja index dbf7ee9dc..89d6b6ffd 100644 --- a/openapi_python_client/templates/property_templates/union_property.py.jinja +++ b/openapi_python_client/templates/property_templates/union_property.py.jinja @@ -1,3 +1,36 @@ +{% macro construct_inner_property(inner_property) %} +{% import "property_templates/" + inner_property.template as inner_template %} +{% if inner_template.check_type_for_construct %} +if not {{ inner_template.check_type_for_construct(inner_property, "data") }}: + raise TypeError() +{% endif %} +{{ inner_template.construct(inner_property, "data") }} +return {{ inner_property.python_name }} +{%- endmacro %} + +{% macro construct_discriminator_lookup(property) %} +{% set _discriminator_properties = [] -%} +{% for discriminator in property.discriminators %} +{{- _discriminator_properties.append(discriminator.property_name) or "" -}} +if not isinstance(data, dict): + raise TypeError() +if "{{ discriminator.property_name }}" in data: + _discriminator_value = data["{{ discriminator.property_name }}"] + {% for value, model in discriminator.value_to_model_map.items() %} + def _parse_{{ loop.index }}(data: object) -> {{ model.get_type_string() }}: +{{ construct_inner_property(model) | indent(8, True) }} + {% endfor %} + _discriminator_mapping = { + {% for value, model in discriminator.value_to_model_map.items() %} + "{{ value }}": _parse_{{ loop.index }}, + {% endfor %} + } + if _parse_fn := _discriminator_mapping.get(_discriminator_value): + return cast({{ property.get_type_string() }}, _parse_fn(data)) +{% endfor %} +raise TypeError(f"unrecognized value for property {{ _discriminator_properties | join(' or ') }}") +{% endmacro %} + {% macro construct(property, source) %} def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() }}: {% if "None" in property.get_type_strings_in_union(json=True, multipart=False) %} @@ -8,6 +41,9 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri if isinstance(data, Unset): return data {% endif %} +{% if property.discriminators %} +{{ construct_discriminator_lookup(property) | indent(4, True) }} +{% else %} {% set ns = namespace(contains_unmodified_properties = false) %} {% for inner_property in property.inner_properties %} {% import "property_templates/" + inner_property.template as inner_template %} @@ -17,24 +53,17 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri {% endif %} {% if inner_template.check_type_for_construct and (not loop.last or ns.contains_unmodified_properties) %} try: - if not {{ inner_template.check_type_for_construct(inner_property, "data") }}: - raise TypeError() - {{ inner_template.construct(inner_property, "data") | indent(8) }} - return {{ inner_property.python_name }} +{{ construct_inner_property(inner_property) | indent(8, True) }} except: # noqa: E722 pass {% else %}{# Don't do try/except for the last one nor any properties with no type checking #} - {% if inner_template.check_type_for_construct %} - if not {{ inner_template.check_type_for_construct(inner_property, "data") }}: - raise TypeError() - {% endif %} - {{ inner_template.construct(inner_property, "data") | indent(4) }} - return {{ inner_property.python_name }} +{{ construct_inner_property(inner_property) | indent(4, True) }} {% endif %} {% endfor %} {% if ns.contains_unmodified_properties %} return cast({{ property.get_type_string() }}, data) {% endif %} +{% endif %} {{ property.python_name }} = _parse_{{ property.python_name }}({{ source }}) {% endmacro %} diff --git a/openapi_python_client/templates/pyproject.toml.jinja b/openapi_python_client/templates/pyproject.toml.jinja index 7f68d58e5..e9344b436 100644 --- a/openapi_python_client/templates/pyproject.toml.jinja +++ b/openapi_python_client/templates/pyproject.toml.jinja @@ -9,7 +9,7 @@ version = "{{ package_version }}" description = "{{ package_description }}" authors = [] readme = "README.md" -{% if pdm %}requires-python = ">=3.8,<4.0"{% endif %} +{% if pdm %}requires-python = ">=3.9,<4.0"{% endif %} {% if poetry %} packages = [ {include = "{{ package_name }}"}, @@ -19,8 +19,8 @@ include = ["CHANGELOG.md", "{{ package_name }}/py.typed"] {% if pdm %} dependencies = [ - "httpx>=0.20.0,<0.28.0", - "attrs>=21.3.0", + "httpx>=0.20.0,<0.29.0", + "attrs>=22.2.0", "python-dateutil>=2.8.0", ] @@ -30,9 +30,9 @@ distribution = true {% if poetry %} [tool.poetry.dependencies] -python = "^3.8" -httpx = ">=0.20.0,<0.28.0" -attrs = ">=21.3.0" +python = "^3.9" +httpx = ">=0.20.0,<0.29.0" +attrs = ">=22.2.0" python-dateutil = "^2.8.0" {% endif %} diff --git a/openapi_python_client/templates/setup.py.jinja b/openapi_python_client/templates/setup.py.jinja index 87c0cc063..c7c1a5a94 100644 --- a/openapi_python_client/templates/setup.py.jinja +++ b/openapi_python_client/templates/setup.py.jinja @@ -12,7 +12,7 @@ setup( long_description=long_description, long_description_content_type="text/markdown", packages=find_packages(), - python_requires=">=3.8, <4", - install_requires=["httpx >= 0.20.0, < 0.28.0", "attrs >= 21.3.0", "python-dateutil >= 2.8.0, < 3"], + python_requires=">=3.9, <4", + install_requires=["httpx >= 0.20.0, < 0.29.0", "attrs >= 22.2.0", "python-dateutil >= 2.8.0, < 3"], package_data={"{{ package_name }}": ["py.typed"]}, ) diff --git a/openapi_python_client/templates/types.py.jinja b/openapi_python_client/templates/types.py.jinja index cc151acb3..6e0d6206c 100644 --- a/openapi_python_client/templates/types.py.jinja +++ b/openapi_python_client/templates/types.py.jinja @@ -1,6 +1,8 @@ """ Contains some shared types for properties """ + +from collections.abc import MutableMapping from http import HTTPStatus -from typing import Any, BinaryIO, Generic, MutableMapping, Optional, Tuple, TypeVar, Literal +from typing import BinaryIO, Generic, Optional, TypeVar, Literal from attrs import define @@ -13,7 +15,7 @@ class Unset: UNSET: Unset = Unset() {# Used as `FileProperty._json_type_string` #} -FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]] +FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] @define @@ -42,4 +44,4 @@ class Response(Generic[T]): parsed: Optional[T] -__all__ = ["File", "Response", "FileJsonType", "Unset", "UNSET"] +__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] diff --git a/openapi_python_client/utils.py b/openapi_python_client/utils.py index 22a7bcfa8..15e8c9eec 100644 --- a/openapi_python_client/utils.py +++ b/openapi_python_client/utils.py @@ -57,7 +57,6 @@ def split_words(value: str) -> list[str]: RESERVED_WORDS = (set(dir(builtins)) | {"self", "true", "false", "datetime"}) - { - "type", "id", } diff --git a/pdm.lock b/pdm.lock deleted file mode 100644 index 3970e7752..000000000 --- a/pdm.lock +++ /dev/null @@ -1,1008 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default", "dev"] -strategy = ["inherit_metadata"] -lock_version = "4.5.0" -content_hash = "sha256:3b1d2a8edbe074a22af87e0a5174d19511c04e3ccee607161baa74a52a9177d9" - -[[metadata.targets]] -requires_python = ">=3.8.1,<4.0" - -[[package]] -name = "annotated-types" -version = "0.7.0" -requires_python = ">=3.8" -summary = "Reusable constraint types to use with typing.Annotated" -groups = ["default"] -dependencies = [ - "typing-extensions>=4.0.0; python_version < \"3.9\"", -] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.5.2" -requires_python = ">=3.8" -summary = "High level compatibility layer for multiple asynchronous event loop implementations" -groups = ["default"] -dependencies = [ - "exceptiongroup>=1.0.2; python_version < \"3.11\"", - "idna>=2.8", - "sniffio>=1.1", - "typing-extensions>=4.1; python_version < \"3.11\"", -] -files = [ - {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, - {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, -] - -[[package]] -name = "attrs" -version = "24.2.0" -requires_python = ">=3.7" -summary = "Classes Without Boilerplate" -groups = ["default"] -dependencies = [ - "importlib-metadata; python_version < \"3.8\"", -] -files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, -] - -[[package]] -name = "certifi" -version = "2024.8.30" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." -groups = ["default"] -files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, -] - -[[package]] -name = "click" -version = "8.1.7" -requires_python = ">=3.7" -summary = "Composable command line interface toolkit" -groups = ["default"] -dependencies = [ - "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", -] -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -groups = ["default", "dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.6.1" -requires_python = ">=3.8" -summary = "Code coverage measurement for Python" -groups = ["dev"] -files = [ - {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, - {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, - {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, - {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, - {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, - {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, - {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, - {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, - {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, - {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, - {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, - {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, - {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, - {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, - {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, - {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, - {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, - {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, -] - -[[package]] -name = "coverage" -version = "7.6.1" -extras = ["toml"] -requires_python = ">=3.8" -summary = "Code coverage measurement for Python" -groups = ["dev"] -dependencies = [ - "coverage==7.6.1", - "tomli; python_full_version <= \"3.11.0a6\"", -] -files = [ - {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, - {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, - {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, - {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, - {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, - {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, - {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, - {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, - {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, - {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, - {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, - {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, - {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, - {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, - {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, - {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, - {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, - {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.2" -requires_python = ">=3.7" -summary = "Backport of PEP 654 (exception groups)" -groups = ["default", "dev"] -marker = "python_version < \"3.11\"" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[[package]] -name = "h11" -version = "0.14.0" -requires_python = ">=3.7" -summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -groups = ["default"] -dependencies = [ - "typing-extensions; python_version < \"3.8\"", -] -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "httpcore" -version = "1.0.6" -requires_python = ">=3.8" -summary = "A minimal low-level HTTP client." -groups = ["default"] -dependencies = [ - "certifi", - "h11<0.15,>=0.13", -] -files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, -] - -[[package]] -name = "httpx" -version = "0.27.2" -requires_python = ">=3.8" -summary = "The next generation HTTP client." -groups = ["default"] -dependencies = [ - "anyio", - "certifi", - "httpcore==1.*", - "idna", - "sniffio", -] -files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, -] - -[[package]] -name = "idna" -version = "3.10" -requires_python = ">=3.6" -summary = "Internationalized Domain Names in Applications (IDNA)" -groups = ["default"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" -summary = "brain-dead simple config-ini parsing" -groups = ["dev"] -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "jinja2" -version = "3.1.4" -requires_python = ">=3.7" -summary = "A very fast and expressive template engine." -groups = ["default"] -dependencies = [ - "MarkupSafe>=2.0", -] -files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -requires_python = ">=3.8" -summary = "Python port of markdown-it. Markdown parsing, done right!" -groups = ["default"] -dependencies = [ - "mdurl~=0.1", -] -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[[package]] -name = "markupsafe" -version = "2.1.5" -requires_python = ">=3.7" -summary = "Safely add untrusted strings to HTML/XML markup." -groups = ["default"] -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -requires_python = ">=3.7" -summary = "Markdown URL utilities" -groups = ["default"] -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "mslex" -version = "1.3.0" -requires_python = ">=3.5" -summary = "shlex for windows" -groups = ["dev"] -marker = "sys_platform == \"win32\"" -files = [ - {file = "mslex-1.3.0-py3-none-any.whl", hash = "sha256:c7074b347201b3466fc077c5692fbce9b5f62a63a51f537a53fbbd02eff2eea4"}, - {file = "mslex-1.3.0.tar.gz", hash = "sha256:641c887d1d3db610eee2af37a8e5abda3f70b3006cdfd2d0d29dc0d1ae28a85d"}, -] - -[[package]] -name = "mypy" -version = "1.12.1" -requires_python = ">=3.8" -summary = "Optional static typing for Python" -groups = ["dev"] -dependencies = [ - "mypy-extensions>=1.0.0", - "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.6.0", -] -files = [ - {file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"}, - {file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"}, - {file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"}, - {file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"}, - {file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"}, - {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, - {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, - {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, - {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, - {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, - {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, - {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, - {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, - {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, - {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, - {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, - {file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"}, - {file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"}, - {file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"}, - {file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"}, - {file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"}, - {file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"}, - {file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"}, - {file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"}, - {file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"}, - {file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"}, - {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, - {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" -summary = "Type system extensions for programs checked with the mypy type checker." -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "packaging" -version = "24.1" -requires_python = ">=3.8" -summary = "Core utilities for Python packages" -groups = ["dev"] -files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -requires_python = ">=3.8" -summary = "plugin and hook calling mechanisms for python" -groups = ["dev"] -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[[package]] -name = "psutil" -version = "5.9.8" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -summary = "Cross-platform lib for process and system monitoring in Python." -groups = ["dev"] -files = [ - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, -] - -[[package]] -name = "pydantic" -version = "2.9.2" -requires_python = ">=3.8" -summary = "Data validation using Python type hints" -groups = ["default"] -dependencies = [ - "annotated-types>=0.6.0", - "pydantic-core==2.23.4", - "typing-extensions>=4.12.2; python_version >= \"3.13\"", - "typing-extensions>=4.6.1; python_version < \"3.13\"", -] -files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, -] - -[[package]] -name = "pydantic-core" -version = "2.23.4" -requires_python = ">=3.8" -summary = "Core functionality for Pydantic validation and serialization" -groups = ["default"] -dependencies = [ - "typing-extensions!=4.7.0,>=4.6.0", -] -files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, -] - -[[package]] -name = "pygments" -version = "2.18.0" -requires_python = ">=3.8" -summary = "Pygments is a syntax highlighting package written in Python." -groups = ["default"] -files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, -] - -[[package]] -name = "pytest" -version = "8.3.3" -requires_python = ">=3.8" -summary = "pytest: simple powerful testing with Python" -groups = ["dev"] -dependencies = [ - "colorama; sys_platform == \"win32\"", - "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", - "iniconfig", - "packaging", - "pluggy<2,>=1.5", - "tomli>=1; python_version < \"3.11\"", -] -files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, -] - -[[package]] -name = "pytest-cov" -version = "5.0.0" -requires_python = ">=3.8" -summary = "Pytest plugin for measuring coverage." -groups = ["dev"] -dependencies = [ - "coverage[toml]>=5.2.1", - "pytest>=4.6", -] -files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, -] - -[[package]] -name = "pytest-mock" -version = "3.14.0" -requires_python = ">=3.8" -summary = "Thin-wrapper around the mock package for easier use with pytest" -groups = ["dev"] -dependencies = [ - "pytest>=6.2.5", -] -files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -summary = "Extensions to the standard Python datetime module" -groups = ["default"] -dependencies = [ - "six>=1.5", -] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[[package]] -name = "python-multipart" -version = "0.0.12" -requires_python = ">=3.8" -summary = "A streaming multipart parser for Python" -groups = ["dev"] -files = [ - {file = "python_multipart-0.0.12-py3-none-any.whl", hash = "sha256:43dcf96cf65888a9cd3423544dd0d75ac10f7aa0c3c28a175bbcd00c9ce1aebf"}, - {file = "python_multipart-0.0.12.tar.gz", hash = "sha256:045e1f98d719c1ce085ed7f7e1ef9d8ccc8c02ba02b5566d5f7521410ced58cb"}, -] - -[[package]] -name = "rich" -version = "13.9.2" -requires_python = ">=3.8.0" -summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -groups = ["default"] -dependencies = [ - "markdown-it-py>=2.2.0", - "pygments<3.0.0,>=2.13.0", - "typing-extensions<5.0,>=4.0.0; python_version < \"3.11\"", -] -files = [ - {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, - {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, -] - -[[package]] -name = "ruamel-yaml" -version = "0.18.6" -requires_python = ">=3.7" -summary = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -groups = ["default", "dev"] -dependencies = [ - "ruamel-yaml-clib>=0.2.7; platform_python_implementation == \"CPython\" and python_version < \"3.13\"", -] -files = [ - {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, - {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, -] - -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.8" -requires_python = ">=3.6" -summary = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -groups = ["default", "dev"] -marker = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" -files = [ - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, - {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, -] - -[[package]] -name = "ruamel-yaml-string" -version = "0.1.1" -requires_python = ">=3" -summary = "add dump_to_string/dumps method that returns YAML document as string" -groups = ["dev"] -dependencies = [ - "ruamel-yaml>=0.17.17", -] -files = [ - {file = "ruamel.yaml.string-0.1.1-py3-none-any.whl", hash = "sha256:eb146bcb42b116216638034a434e9cf3ae2a5d3933aa37183a9854b5f3ff42de"}, - {file = "ruamel.yaml.string-0.1.1.tar.gz", hash = "sha256:7a7aedcc055d45c004d38b756f58474ebefb106851f4ce56ce58415709784350"}, -] - -[[package]] -name = "ruff" -version = "0.7.0" -requires_python = ">=3.7" -summary = "An extremely fast Python linter and code formatter, written in Rust." -groups = ["default"] -files = [ - {file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"}, - {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"}, - {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"}, - {file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"}, - {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"}, - {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"}, - {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"}, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -requires_python = ">=3.7" -summary = "Tool to Detect Surrounding Shell" -groups = ["default"] -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - -[[package]] -name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python 2 and 3 compatibility utilities" -groups = ["default"] -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -requires_python = ">=3.7" -summary = "Sniff out which async library your code is running under" -groups = ["default"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "syrupy" -version = "4.7.2" -requires_python = ">=3.8.1" -summary = "Pytest Snapshot Test Utility" -groups = ["dev"] -dependencies = [ - "pytest<9.0.0,>=7.0.0", -] -files = [ - {file = "syrupy-4.7.2-py3-none-any.whl", hash = "sha256:eae7ba6be5aed190237caa93be288e97ca1eec5ca58760e4818972a10c4acc64"}, - {file = "syrupy-4.7.2.tar.gz", hash = "sha256:ea45e099f242de1bb53018c238f408a5bb6c82007bc687aefcbeaa0e1c2e935a"}, -] - -[[package]] -name = "taskipy" -version = "1.13.0" -requires_python = "<4.0,>=3.6" -summary = "tasks runner for python projects" -groups = ["dev"] -dependencies = [ - "colorama<0.5.0,>=0.4.4", - "mslex<2.0.0,>=1.1.0; sys_platform == \"win32\"", - "psutil<6.0.0,>=5.7.2", - "tomli<2.0.0,>=1.2.3; python_version >= \"3.6\" and python_version < \"3.7\"", - "tomli<3.0.0,>=2.0.1; python_version ~= \"3.7\"", -] -files = [ - {file = "taskipy-1.13.0-py3-none-any.whl", hash = "sha256:56f42b7e508d9aed2c7b6365f8d3dab62dbd0c768c1ab606c819da4fc38421f7"}, - {file = "taskipy-1.13.0.tar.gz", hash = "sha256:2b52f0257958fed151f1340f7de93fcf0848f7a358ad62ba05c31c2ca04f89fe"}, -] - -[[package]] -name = "tomli" -version = "2.0.2" -requires_python = ">=3.8" -summary = "A lil' TOML parser" -groups = ["dev"] -marker = "python_version < \"4.0\"" -files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, -] - -[[package]] -name = "typer" -version = "0.12.5" -requires_python = ">=3.7" -summary = "Typer, build great CLIs. Easy to code. Based on Python type hints." -groups = ["default"] -dependencies = [ - "click>=8.0.0", - "rich>=10.11.0", - "shellingham>=1.3.0", - "typing-extensions>=3.7.4.3", -] -files = [ - {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, - {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, -] - -[[package]] -name = "types-certifi" -version = "2021.10.8.3" -summary = "Typing stubs for certifi" -groups = ["dev"] -files = [ - {file = "types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f"}, - {file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"}, -] - -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20241003" -requires_python = ">=3.8" -summary = "Typing stubs for python-dateutil" -groups = ["dev"] -files = [ - {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, - {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, -] - -[[package]] -name = "types-pyyaml" -version = "6.0.12.20240917" -requires_python = ">=3.8" -summary = "Typing stubs for PyYAML" -groups = ["dev"] -files = [ - {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, - {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["default", "dev"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 000000000..1b4fde906 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1067 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.8.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.9" +files = [ + {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, + {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "attrs" +version = "24.3.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +files = [ + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "certifi" +version = "2024.12.14" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, +] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.6.10" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, + {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, + {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, + {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, + {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, + {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, + {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, + {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, + {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, + {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, + {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, + {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, + {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, + {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, + {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.27.2" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.5" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, + {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mslex" +version = "1.3.0" +description = "shlex for windows" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mslex-1.3.0-py3-none-any.whl", hash = "sha256:c7074b347201b3466fc077c5692fbce9b5f62a63a51f537a53fbbd02eff2eea4"}, + {file = "mslex-1.3.0.tar.gz", hash = "sha256:641c887d1d3db610eee2af37a8e5abda3f70b3006cdfd2d0d29dc0d1ae28a85d"}, +] + +[[package]] +name = "mypy" +version = "1.14.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, + {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, + {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, + {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, + {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, + {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, + {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, + {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, + {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, + {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, + {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, + {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, + {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, + {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, + {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, +] + +[package.dependencies] +mypy_extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "psutil" +version = "6.1.1" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, + {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, + {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4"}, + {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468"}, + {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca"}, + {file = "psutil-6.1.1-cp27-none-win32.whl", hash = "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac"}, + {file = "psutil-6.1.1-cp27-none-win_amd64.whl", hash = "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030"}, + {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"}, + {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"}, + {file = "psutil-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603"}, + {file = "psutil-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303"}, + {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"}, + {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"}, + {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"}, +] + +[package.extras] +dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] + +[[package]] +name = "pydantic" +version = "2.10.4" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, + {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pygments" +version = "2.19.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, +] + +[package.dependencies] +coverage = {version = ">=7.5", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.20" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, + {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, +] + +[[package]] +name = "rich" +version = "13.9.4" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "ruamel-yaml" +version = "0.18.10" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1"}, + {file = "ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.12" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.9" +files = [ + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, + {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, +] + +[[package]] +name = "ruamel-yaml-string" +version = "0.1.1" +description = "add dump_to_string/dumps method that returns YAML document as string" +optional = false +python-versions = ">=3" +files = [ + {file = "ruamel.yaml.string-0.1.1-py3-none-any.whl", hash = "sha256:eb146bcb42b116216638034a434e9cf3ae2a5d3933aa37183a9854b5f3ff42de"}, + {file = "ruamel.yaml.string-0.1.1.tar.gz", hash = "sha256:7a7aedcc055d45c004d38b756f58474ebefb106851f4ce56ce58415709784350"}, +] + +[package.dependencies] +"ruamel.yaml" = ">=0.17.17" + +[[package]] +name = "ruff" +version = "0.7.4" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, + {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, + {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, + {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, + {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, + {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, + {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "syrupy" +version = "4.8.0" +description = "Pytest Snapshot Test Utility" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "syrupy-4.8.0-py3-none-any.whl", hash = "sha256:544f4ec6306f4b1c460fdab48fd60b2c7fe54a6c0a8243aeea15f9ad9c638c3f"}, + {file = "syrupy-4.8.0.tar.gz", hash = "sha256:648f0e9303aaa8387c8365d7314784c09a6bab0a407455c6a01d6a4f5c6a8ede"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9.0.0" + +[[package]] +name = "taskipy" +version = "1.14.1" +description = "tasks runner for python projects" +optional = false +python-versions = "<4.0,>=3.6" +files = [ + {file = "taskipy-1.14.1-py3-none-any.whl", hash = "sha256:6e361520f29a0fd2159848e953599f9c75b1d0b047461e4965069caeb94908f1"}, + {file = "taskipy-1.14.1.tar.gz", hash = "sha256:410fbcf89692dfd4b9f39c2b49e1750b0a7b81affd0e2d7ea8c35f9d6a4774ed"}, +] + +[package.dependencies] +colorama = ">=0.4.4,<0.5.0" +mslex = {version = ">=1.1.0,<2.0.0", markers = "sys_platform == \"win32\""} +psutil = ">=5.7.2,<7" +tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} + +[[package]] +name = "tomli" +version = "2.2.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, +] + +[[package]] +name = "typer" +version = "0.12.5" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, + {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "types-certifi" +version = "2021.10.8.3" +description = "Typing stubs for certifi" +optional = false +python-versions = "*" +files = [ + {file = "types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f"}, + {file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241206" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20241230" +description = "Typing stubs for PyYAML" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, + {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "d5daadf9bb61a900a0aaddfd615b2522dc3142940fa01d8d3c6071f40ba86998" diff --git a/pyproject.toml b/pyproject.toml index 82d2fb6e5..1bb7e629b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,50 +1,70 @@ -[project] +# This fork of openapi-python-client is published internally at Benchling. Changes in this +# project file will *not* be contributed upstream. + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "benchling-openapi-python-client" +version = "2.0.0-alpha.2" +description = "Generate modern Python clients from OpenAPI - Benchling fork" +repository = "https://github.com/benchling/openapi-python-client" +license = "MIT" +keywords=["OpenAPI", "Client", "Generator"] authors = [ - { name = "Dylan Anthony", email = "contact@dylananthony.com" }, -] -license = { text = "MIT" } -requires-python = ">=3.8.1,<4.0" -dependencies = [ - "jinja2>=3.0.0,<4.0.0", - "typer>0.6,<0.13", - "colorama>=0.4.3; sys_platform == \"win32\"", - "shellingham>=1.3.2,<2.0.0", - "pydantic>=2.1.1,<3.0.0", - "attrs>=21.3.0", - "python-dateutil>=2.8.1,<3.0.0", - "httpx>=0.20.0,<0.28.0", - "ruamel.yaml>=0.18.6,<0.19.0", - "ruff>=0.2,<0.8", - "typing-extensions>=4.8.0,<5.0.0", -] -name = "openapi-python-client" -version = "0.21.6" -description = "Generate modern Python clients from OpenAPI" -keywords = [ - "OpenAPI", - "Client", - "Generator", + "Dylan Anthony ", + "Benchling Support ", ] classifiers = [ - "Development Status :: 4 - Beta", - "License :: OSI Approved :: MIT License", + "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Code Generators", "Typing :: Typed", ] readme = "README.md" +packages = [ + {include = "openapi_python_client"}, + # Note that the module name is different from the package name, so that we can + # transparently switch between the upstream package and this one in our projects. +] +include = [ + "CHANGELOG.md", + "openapi_python_client/py.typed", +] +exclude = [ + ".gitignore", +] -[project.urls] -repository = "https://github.com/openapi-generators/openapi-python-client" - -[project.scripts] +[tool.poetry.dependencies] +python = "^3.9" +jinja2 = ">=3.0.0,<4.0.0" +typer = ">0.6,<0.13" +colorama = {version = ">=0.4.3", markers = "sys_platform == 'win32'"} +shellingham = ">=1.3.2,<2.0.0" +pydantic = ">=2.1.1,<3.0.0" +attrs = ">=22.2.0" +python-dateutil = ">=2.8.1,<3.0.0" +httpx = ">=0.20.0,<0.28.0" +"ruamel.yaml" = ">=0.18.6,<0.19.0" +ruff = ">=0.2,<0.8" +typing-extensions = ">=4.8.0,<5.0.0" + +[tool.poetry.group.dev.dependencies] +pytest = "*" +pytest-mock = "*" +mypy = "*" +pytest-cov = "*" +python-multipart = "*" +types-PyYAML = ">=6.0.3,<7.0.0" +types-certifi = "<2021.10.9,>=2020.0.0" +types-python-dateutil = "<3.0.0,>=2.0.0" +ruamel-yaml-string = ">=0.1.1" +syrupy = ">=4" +taskipy = "^1.14.0" +python-dotenv = "^1.0.1" + +[tool.poetry.scripts] openapi-python-client = "openapi_python_client.cli:app" [tool.ruff] @@ -67,7 +87,7 @@ ignore = ["E501", "PLR0913", "PLR2004"] "tests/*" = ["PLR2004"] [tool.coverage.run] -omit = ["openapi_python_client/__main__.py", "openapi_python_client/templates/*"] +omit = ["openapi_python_client/__main__.py", "openapi_python_client/templates/*", "end_to_end_tests/*", "integration_tests/*", "tests/*"] [tool.mypy] plugins = ["pydantic.mypy"] @@ -75,6 +95,10 @@ disallow_any_generics = true disallow_untyped_defs = true warn_redundant_casts = true strict_equality = true +exclude = [ + # For unknown reasons, mypy complains about this file but doesn't complain in the upstream repo + "openapi_python_client/schema/openapi_schema_pydantic/schema.py", +] [[tool.mypy.overrides]] module = [ @@ -86,60 +110,19 @@ ignore_missing_imports = true [tool.pytest.ini_options] junit_family = "xunit2" -[tool.pdm.dev-dependencies] -dev = [ - "pytest", - "pytest-mock", - "mypy", - "taskipy", - "pytest-cov", - "python-multipart", - "types-PyYAML<7.0.0,>=6.0.3", - "types-certifi<2021.10.9,>=2020.0.0", - "types-python-dateutil<3.0.0,>=2.0.0", - "ruamel-yaml-string>=0.1.1", - "syrupy>=4", -] - -[tool.pdm.build] -includes = [ - "openapi_python_client", - "CHANGELOG.md", - "openapi_python_client/py.typed", -] - -[tool.pdm.scripts] +[tool.taskipy.tasks] lint = "ruff check --fix ." format = "ruff format ." -mypy = "mypy openapi_python_client" -check = { composite = ["lint", "format", "mypy", "test"] } -regen = {composite = ["regen_e2e", "regen_integration"]} +mypy = "mypy openapi_python_client" # note, currently test code is excluded from type checking +check = "task lint && task format && task mypy && task test" +regen = "task regen_e2e && task regen_integration" e2e = "pytest openapi_python_client end_to_end_tests/test_end_to_end.py" -re = {composite = ["regen_e2e", "e2e --snapshot-update"]} +re = "task regen_e2e && task e2e --snapshot-update" regen_e2e = "python -m end_to_end_tests.regen_golden_record" - -[tool.pdm.scripts.test] -cmd = "pytest tests end_to_end_tests/test_end_to_end.py --basetemp=tests/tmp" -[tool.pdm.scripts.test.env] -"TEST_RELATIVE" = "true" - -[tool.pdm.scripts.post_test] -cmd = "rm -r tests/tmp" - -[tool.pdm.scripts.test_with_coverage] -composite = ["test --cov openapi_python_client tests --cov-report=term-missing"] - -[tool.pdm.scripts.regen_integration] -shell = """ +unit = "pytest tests" +test = "pytest tests end_to_end_tests/test_end_to_end.py end_to_end_tests/functional_tests --basetemp=tests/tmp" +post_test = "rm -r tests/tmp" +test_with_coverage = "dotenv -f tests/.env run task test --cov openapi_python_client tests --cov-report=term-missing" +regen_integration = """ openapi-python-client generate --overwrite --url https://raw.githubusercontent.com/openapi-generators/openapi-test-server/main/openapi.json --config integration-tests/config.yaml --meta none --output-path integration-tests/integration_tests \ """ - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.sdist] -include = [ - "openapi_python_client", -] -exclude = [".gitignore"] diff --git a/tests/.env b/tests/.env new file mode 100644 index 000000000..fda21b1fc --- /dev/null +++ b/tests/.env @@ -0,0 +1 @@ +TEST_RELATIVE=true # see test_config.py - this enables a test assertion about relative paths that is only applicable in CI diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index 6eeadcd78..3c810e344 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -4,7 +4,6 @@ import pytest import openapi_python_client.schema as oai -from openapi_python_client import GeneratorError from openapi_python_client.parser.errors import ParseError from openapi_python_client.parser.openapi import Endpoint, EndpointCollection from openapi_python_client.parser.properties import IntProperty, Parameters, Schemas @@ -13,50 +12,6 @@ MODULE_NAME = "openapi_python_client.parser.openapi" -class TestGeneratorData: - def test_from_dict_invalid_schema(self, mocker): - Schemas = mocker.patch(f"{MODULE_NAME}.Schemas") - config = mocker.MagicMock() - - in_dict = {} - - from openapi_python_client.parser.openapi import GeneratorData - - generator_data = GeneratorData.from_dict(in_dict, config=config) - - assert isinstance(generator_data, GeneratorError) - assert generator_data.header == "Failed to parse OpenAPI document" - keywords = ["3 validation errors for OpenAPI", "info", "paths", "openapi", "Field required"] - assert generator_data.detail and all(keyword in generator_data.detail for keyword in keywords) - - Schemas.build.assert_not_called() - Schemas.assert_not_called() - - def test_swagger_document_invalid_schema(self, mocker): - Schemas = mocker.patch(f"{MODULE_NAME}.Schemas") - config = mocker.MagicMock() - - in_dict = {"swagger": "2.0"} - - from openapi_python_client.parser.openapi import GeneratorData - - generator_data = GeneratorData.from_dict(in_dict, config=config) - - assert isinstance(generator_data, GeneratorError) - assert generator_data.header == "Failed to parse OpenAPI document" - keywords = [ - "You may be trying to use a Swagger document; this is not supported by this project.", - "info", - "paths", - "openapi", - "Field required", - ] - assert generator_data.detail and all(keyword in generator_data.detail for keyword in keywords) - - Schemas.build.assert_not_called() - Schemas.assert_not_called() - - class TestEndpoint: def make_endpoint(self): from openapi_python_client.parser.openapi import Endpoint @@ -67,7 +22,7 @@ def make_endpoint(self): description=None, name="name", requires_security=False, - tag="tag", + tags=["tag"], relative_imports={"import_3"}, ) @@ -85,7 +40,9 @@ def test__add_responses_status_code_error(self, response_status_code, mocker): response_from_data = mocker.patch(f"{MODULE_NAME}.response_from_data", return_value=(parse_error, schemas)) config = MagicMock() - response, schemas = Endpoint._add_responses(endpoint=endpoint, data=data, schemas=schemas, config=config) + response, schemas = Endpoint._add_responses( + endpoint=endpoint, data=data, schemas=schemas, responses={}, config=config + ) assert response.errors == [ ParseError( @@ -110,12 +67,28 @@ def test__add_responses_error(self, mocker): response_from_data = mocker.patch(f"{MODULE_NAME}.response_from_data", return_value=(parse_error, schemas)) config = MagicMock() - response, schemas = Endpoint._add_responses(endpoint=endpoint, data=data, schemas=schemas, config=config) + response, schemas = Endpoint._add_responses( + endpoint=endpoint, data=data, schemas=schemas, responses={}, config=config + ) response_from_data.assert_has_calls( [ - mocker.call(status_code=200, data=response_1_data, schemas=schemas, parent_name="name", config=config), - mocker.call(status_code=404, data=response_2_data, schemas=schemas, parent_name="name", config=config), + mocker.call( + status_code=200, + data=response_1_data, + schemas=schemas, + responses={}, + parent_name="name", + config=config, + ), + mocker.call( + status_code=404, + data=response_2_data, + schemas=schemas, + responses={}, + parent_name="name", + config=config, + ), ] ) assert response.errors == [ @@ -472,8 +445,9 @@ def test_from_data_bad_params(self, mocker, config): data=data, path=path, method=method, - tag="default", + tags=["default"], schemas=initial_schemas, + responses={}, parameters=parameters, config=config, request_bodies={}, @@ -507,8 +481,9 @@ def test_from_data_bad_responses(self, mocker, config): data=data, path=path, method=method, - tag="default", + tags=["default"], schemas=initial_schemas, + responses={}, parameters=initial_parameters, config=config, request_bodies={}, @@ -547,8 +522,9 @@ def test_from_data_standard(self, mocker, config): data=data, path=path, method=method, - tag="default", + tags=["default"], schemas=initial_schemas, + responses={}, parameters=initial_parameters, config=config, request_bodies={}, @@ -562,7 +538,7 @@ def test_from_data_standard(self, mocker, config): summary="", name=data.operationId, requires_security=True, - tag="default", + tags=["default"], ), data=data, schemas=initial_schemas, @@ -570,7 +546,7 @@ def test_from_data_standard(self, mocker, config): config=config, ) _add_responses.assert_called_once_with( - endpoint=param_endpoint, data=data.responses, schemas=param_schemas, config=config + endpoint=param_endpoint, data=data.responses, schemas=param_schemas, responses={}, config=config ) def test_from_data_no_operation_id(self, mocker, config): @@ -598,8 +574,9 @@ def test_from_data_no_operation_id(self, mocker, config): data=data, path=path, method=method, - tag="default", + tags=["default"], schemas=schemas, + responses={}, parameters=parameters, config=config, request_bodies={}, @@ -613,7 +590,7 @@ def test_from_data_no_operation_id(self, mocker, config): summary="", name="get_path_with_param", requires_security=True, - tag="default", + tags=["default"], ), data=data, schemas=schemas, @@ -624,6 +601,7 @@ def test_from_data_no_operation_id(self, mocker, config): endpoint=add_parameters.return_value[0], data=data.responses, schemas=add_parameters.return_value[1], + responses={}, config=config, ) @@ -652,8 +630,9 @@ def test_from_data_no_security(self, mocker, config): data=data, path=path, method=method, - tag="a", + tags=["a"], schemas=schemas, + responses={}, parameters=parameters, config=config, request_bodies={}, @@ -667,7 +646,7 @@ def test_from_data_no_security(self, mocker, config): summary="", name=data.operationId, requires_security=False, - tag="a", + tags=["a"], ), data=data, parameters=parameters, @@ -678,6 +657,7 @@ def test_from_data_no_security(self, mocker, config): endpoint=add_parameters.return_value[0], data=data.responses, schemas=add_parameters.return_value[1], + responses={}, config=config, ) @@ -693,9 +673,10 @@ def test_from_data_some_bad_bodies(self, config): ), ), schemas=Schemas(), + responses={}, config=config, parameters=Parameters(), - tag="tag", + tags=["tag"], path="/", method="get", request_bodies={}, @@ -716,9 +697,10 @@ def test_from_data_all_bodies_bad(self, config): ), ), schemas=Schemas(), + responses={}, config=config, parameters=Parameters(), - tag="tag", + tags=["tag"], path="/", method="get", request_bodies={}, @@ -787,96 +769,7 @@ def test_from_data_overrides_path_item_params_with_operation_params(self, config parameters=Parameters(), config=config, request_bodies={}, + responses={}, ) collection: EndpointCollection = collections["default"] assert isinstance(collection.endpoints[0].query_parameters[0], IntProperty) - - def test_from_data_errors(self, mocker, config): - from openapi_python_client.parser.openapi import ParseError - - path_1_put = oai.Operation.model_construct() - path_1_post = oai.Operation.model_construct(tags=["tag_2", "tag_3"]) - path_2_get = oai.Operation.model_construct() - data = { - "path_1": oai.PathItem.model_construct(post=path_1_post, put=path_1_put), - "path_2": oai.PathItem.model_construct(get=path_2_get), - } - schemas_1 = mocker.MagicMock() - schemas_2 = mocker.MagicMock() - schemas_3 = mocker.MagicMock() - parameters_1 = mocker.MagicMock() - parameters_2 = mocker.MagicMock() - parameters_3 = mocker.MagicMock() - mocker.patch.object( - Endpoint, - "from_data", - side_effect=[ - (ParseError(data="1"), schemas_1, parameters_1), - (ParseError(data="2"), schemas_2, parameters_2), - (mocker.MagicMock(errors=[ParseError(data="3")], path="path_2"), schemas_3, parameters_3), - ], - ) - schemas = mocker.MagicMock() - parameters = mocker.MagicMock() - - result, result_schemas, result_parameters = EndpointCollection.from_data( - data=data, - schemas=schemas, - config=config, - parameters=parameters, - request_bodies={}, - ) - - assert result["default"].parse_errors[0].data == "1" - assert result["default"].parse_errors[1].data == "3" - assert result["tag_2"].parse_errors[0].data == "2" - assert result_schemas == schemas_3 - - def test_from_data_tags_snake_case_sanitizer(self, mocker, config): - from openapi_python_client.parser.openapi import Endpoint, EndpointCollection - - path_1_put = oai.Operation.model_construct() - path_1_post = oai.Operation.model_construct(tags=["AMF Subscription Info (Document)", "tag_3"]) - path_2_get = oai.Operation.model_construct(tags=["3. ABC"]) - data = { - "path_1": oai.PathItem.model_construct(post=path_1_post, put=path_1_put), - "path_2": oai.PathItem.model_construct(get=path_2_get), - } - endpoint_1 = mocker.MagicMock(autospec=Endpoint, tag="default", relative_imports={"1", "2"}, path="path_1") - endpoint_2 = mocker.MagicMock( - autospec=Endpoint, tag="AMFSubscriptionInfo (Document)", relative_imports={"2"}, path="path_1" - ) - endpoint_3 = mocker.MagicMock(autospec=Endpoint, tag="default", relative_imports={"2", "3"}, path="path_2") - schemas_1 = mocker.MagicMock() - schemas_2 = mocker.MagicMock() - schemas_3 = mocker.MagicMock() - parameters_1 = mocker.MagicMock() - parameters_2 = mocker.MagicMock() - parameters_3 = mocker.MagicMock() - mocker.patch.object( - Endpoint, - "from_data", - side_effect=[ - (endpoint_1, schemas_1, parameters_1), - (endpoint_2, schemas_2, parameters_2), - (endpoint_3, schemas_3, parameters_3), - ], - ) - schemas = mocker.MagicMock() - parameters = mocker.MagicMock() - - result = EndpointCollection.from_data( - data=data, schemas=schemas, parameters=parameters, config=config, request_bodies={} - ) - - assert result == ( - { - "default": EndpointCollection("default", endpoints=[endpoint_1]), - "amf_subscription_info_document": EndpointCollection( - "amf_subscription_info_document", endpoints=[endpoint_2] - ), - "tag3_abc": EndpointCollection("tag3_abc", endpoints=[endpoint_3]), - }, - schemas_3, - parameters_3, - ) diff --git a/tests/test_parser/test_properties/test_any.py b/tests/test_parser/test_properties/test_any.py deleted file mode 100644 index d80e93e64..000000000 --- a/tests/test_parser/test_properties/test_any.py +++ /dev/null @@ -1,13 +0,0 @@ -from openapi_python_client.parser.properties import AnyProperty -from openapi_python_client.utils import PythonIdentifier - - -def test_default() -> None: - AnyProperty.build( - name="test", - required=True, - default=42, - python_name=PythonIdentifier("test", ""), - description="test", - example="test", - ) diff --git a/tests/test_parser/test_properties/test_boolean.py b/tests/test_parser/test_properties/test_boolean.py deleted file mode 100644 index 0862f1507..000000000 --- a/tests/test_parser/test_properties/test_boolean.py +++ /dev/null @@ -1,55 +0,0 @@ -import pytest - -from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import BooleanProperty -from openapi_python_client.utils import PythonIdentifier - - -def test_invalid_default_value() -> None: - err = BooleanProperty.build( - default="not a boolean", - description=None, - example=None, - required=False, - python_name=PythonIdentifier("not_a_boolean", ""), - name="not_a_boolean", - ) - - assert isinstance(err, PropertyError) - - -@pytest.mark.parametrize( - ("value", "expected"), - ( - ("true", "True"), - ("True", "True"), - ("false", "False"), - ("False", "False"), - ), -) -def test_string_default(value, expected) -> None: - prop = BooleanProperty.build( - default=value, - description=None, - example=None, - required=False, - python_name="not_a_boolean", - name="not_a_boolean", - ) - - assert isinstance(prop, BooleanProperty) - assert prop.default.python_code == expected - - -def test_bool_default() -> None: - prop = BooleanProperty.build( - default=True, - description=None, - example=None, - required=False, - python_name="not_a_boolean", - name="not_a_boolean", - ) - - assert isinstance(prop, BooleanProperty) - assert prop.default.python_code == "True" diff --git a/tests/test_parser/test_properties/test_const.py b/tests/test_parser/test_properties/test_const.py deleted file mode 100644 index ab9e29332..000000000 --- a/tests/test_parser/test_properties/test_const.py +++ /dev/null @@ -1,28 +0,0 @@ -from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import ConstProperty - - -def test_default_doesnt_match_const() -> None: - err = ConstProperty.build( - name="test", - required=True, - default="not the value", - python_name="test", - description=None, - const="the value", - ) - - assert isinstance(err, PropertyError) - - -def test_non_string_const() -> None: - prop = ConstProperty.build( - name="test", - required=True, - default=123, - python_name="test", - description=None, - const=123, - ) - - assert isinstance(prop, ConstProperty) diff --git a/tests/test_parser/test_properties/test_date.py b/tests/test_parser/test_properties/test_date.py deleted file mode 100644 index bcc3292b6..000000000 --- a/tests/test_parser/test_properties/test_date.py +++ /dev/null @@ -1,28 +0,0 @@ -from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import DateProperty - - -def test_invalid_default_value(): - err = DateProperty.build( - default="not a date", - description=None, - example=None, - required=False, - python_name="not_a_date", - name="not_a_date", - ) - - assert isinstance(err, PropertyError) - - -def test_default_with_bad_type(): - err = DateProperty.build( - default=123, - description=None, - example=None, - required=False, - python_name="not_a_date", - name="not_a_date", - ) - - assert isinstance(err, PropertyError) diff --git a/tests/test_parser/test_properties/test_datetime.py b/tests/test_parser/test_properties/test_datetime.py deleted file mode 100644 index 94ea6f09c..000000000 --- a/tests/test_parser/test_properties/test_datetime.py +++ /dev/null @@ -1,28 +0,0 @@ -from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import DateTimeProperty - - -def test_invalid_default_value(): - err = DateTimeProperty.build( - default="not a date", - description=None, - example=None, - required=False, - python_name="not_a_date", - name="not_a_date", - ) - - assert isinstance(err, PropertyError) - - -def test_default_with_bad_type(): - err = DateTimeProperty.build( - default=123, - description=None, - example=None, - required=False, - python_name="not_a_date", - name="not_a_date", - ) - - assert isinstance(err, PropertyError) diff --git a/tests/test_parser/test_properties/test_enum_property.py b/tests/test_parser/test_properties/test_enum_property.py deleted file mode 100644 index 21b183f8e..000000000 --- a/tests/test_parser/test_properties/test_enum_property.py +++ /dev/null @@ -1,81 +0,0 @@ -from typing import Type, Union - -import pytest - -import openapi_python_client.schema as oai -from openapi_python_client import Config -from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import LiteralEnumProperty, Schemas -from openapi_python_client.parser.properties.enum_property import EnumProperty - -PropertyClass = Union[Type[EnumProperty], Type[LiteralEnumProperty]] - - -@pytest.fixture(params=[EnumProperty, LiteralEnumProperty]) -def property_class(request) -> PropertyClass: - return request.param - - -def test_conflict(config: Config, property_class: PropertyClass) -> None: - schemas = Schemas() - - _, schemas = property_class.build( - data=oai.Schema(enum=["a"]), name="Existing", required=True, schemas=schemas, parent_name="", config=config - ) - err, new_schemas = property_class.build( - data=oai.Schema(enum=["a", "b"]), - name="Existing", - required=True, - schemas=schemas, - parent_name="", - config=config, - ) - - assert schemas == new_schemas - assert err.detail == "Found conflicting enums named Existing with incompatible values." - - -def test_bad_default_value(config: Config, property_class: PropertyClass) -> None: - data = oai.Schema(default="B", enum=["A"]) - schemas = Schemas() - - err, new_schemas = property_class.build( - data=data, name="Existing", required=True, schemas=schemas, parent_name="parent", config=config - ) - - assert schemas == new_schemas - assert err == PropertyError(detail="Value B is not valid for enum Existing", data=data) - - -def test_bad_default_type(config: Config, property_class: PropertyClass) -> None: - data = oai.Schema(default=123, enum=["A"]) - schemas = Schemas() - - err, new_schemas = property_class.build( - data=data, name="Existing", required=True, schemas=schemas, parent_name="parent", config=config - ) - - assert schemas == new_schemas - assert isinstance(err, PropertyError) - - -def test_mixed_types(config: Config, property_class: PropertyClass) -> None: - data = oai.Schema(enum=["A", 1]) - schemas = Schemas() - - err, _ = property_class.build( - data=data, name="Enum", required=True, schemas=schemas, parent_name="parent", config=config - ) - - assert isinstance(err, PropertyError) - - -def test_unsupported_type(config: Config, property_class: PropertyClass) -> None: - data = oai.Schema(enum=[1.4, 1.5]) - schemas = Schemas() - - err, _ = property_class.build( - data=data, name="Enum", required=True, schemas=schemas, parent_name="parent", config=config - ) - - assert isinstance(err, PropertyError) diff --git a/tests/test_parser/test_properties/test_file.py b/tests/test_parser/test_properties/test_file.py index 87298ba03..f399e8278 100644 --- a/tests/test_parser/test_properties/test_file.py +++ b/tests/test_parser/test_properties/test_file.py @@ -3,6 +3,8 @@ def test_no_default_allowed(): + # currently this is testing an unused code path: + # https://github.com/openapi-generators/openapi-python-client/issues/1162 err = FileProperty.build( default="not none", description=None, diff --git a/tests/test_parser/test_properties/test_float.py b/tests/test_parser/test_properties/test_float.py deleted file mode 100644 index 356a61424..000000000 --- a/tests/test_parser/test_properties/test_float.py +++ /dev/null @@ -1,38 +0,0 @@ -from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import FloatProperty -from openapi_python_client.parser.properties.protocol import Value - - -def test_invalid_default(): - err = FloatProperty.build( - default="not a float", - description=None, - example=None, - required=False, - python_name="not_a_float", - name="not_a_float", - ) - - assert isinstance(err, PropertyError) - - -def test_convert_from_string(): - assert FloatProperty.convert_value("1.0") == Value(python_code="1.0", raw_value="1.0") - assert FloatProperty.convert_value("1") == Value(python_code="1.0", raw_value="1") - - -def test_convert_from_float(): - assert FloatProperty.convert_value(1.0) == Value(python_code="1.0", raw_value=1.0) - - -def test_invalid_type_default(): - err = FloatProperty.build( - default=True, - description=None, - example=None, - required=False, - python_name="not_a_float", - name="not_a_float", - ) - - assert isinstance(err, PropertyError) diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index f56bf065d..1af6342ad 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -1,86 +1,18 @@ -from unittest.mock import MagicMock, call +from unittest.mock import call -import attr import pytest import openapi_python_client.schema as oai from openapi_python_client.parser.errors import ParameterError, PropertyError from openapi_python_client.parser.properties import ( - ListProperty, ReferencePath, Schemas, - StringProperty, - UnionProperty, ) -from openapi_python_client.parser.properties.protocol import ModelProperty, Value -from openapi_python_client.parser.properties.schemas import Class -from openapi_python_client.schema import DataType from openapi_python_client.utils import ClassName, PythonIdentifier MODULE_NAME = "openapi_python_client.parser.properties" -class TestStringProperty: - def test_is_base_type(self, string_property_factory): - assert string_property_factory().is_base_type is True - - @pytest.mark.parametrize( - "required, expected", - ( - (True, "str"), - (False, "Union[Unset, str]"), - ), - ) - def test_get_type_string(self, string_property_factory, required, expected): - p = string_property_factory(required=required) - - assert p.get_type_string() == expected - - -class TestDateTimeProperty: - def test_is_base_type(self, date_time_property_factory): - assert date_time_property_factory().is_base_type is True - - @pytest.mark.parametrize("required", (True, False)) - def test_get_imports(self, date_time_property_factory, required): - p = date_time_property_factory(required=required) - - expected = { - "import datetime", - "from typing import cast", - "from dateutil.parser import isoparse", - } - if not required: - expected |= { - "from typing import Union", - "from ...types import UNSET, Unset", - } - - assert p.get_imports(prefix="...") == expected - - -class TestDateProperty: - def test_is_base_type(self, date_property_factory): - assert date_property_factory().is_base_type is True - - @pytest.mark.parametrize("required", (True, False)) - def test_get_imports(self, date_property_factory, required): - p = date_property_factory(required=required) - - expected = { - "import datetime", - "from typing import cast", - "from dateutil.parser import isoparse", - } - if not required: - expected |= { - "from typing import Union", - "from ...types import UNSET, Unset", - } - - assert p.get_imports(prefix="...") == expected - - class TestFileProperty: def test_is_base_type(self, file_property_factory): assert file_property_factory().is_base_type is True @@ -102,117 +34,6 @@ def test_get_imports(self, file_property_factory, required): assert p.get_imports(prefix="...") == expected -class TestNoneProperty: - def test_is_base_type(self, none_property_factory): - assert none_property_factory().is_base_type is True - - -class TestBooleanProperty: - def test_is_base_type(self, boolean_property_factory): - assert boolean_property_factory().is_base_type is True - - -class TestAnyProperty: - def test_is_base_type(self, any_property_factory): - assert any_property_factory().is_base_type is True - - -class TestIntProperty: - def test_is_base_type(self, int_property_factory): - assert int_property_factory().is_base_type is True - - -class TestListProperty: - def test_is_base_type(self, list_property_factory): - assert list_property_factory().is_base_type is False - - @pytest.mark.parametrize("quoted", (True, False)) - def test_get_base_json_type_string_base_inner(self, list_property_factory, quoted): - p = list_property_factory() - assert p.get_base_json_type_string(quoted=quoted) == "List[str]" - - @pytest.mark.parametrize("quoted", (True, False)) - def test_get_base_json_type_string_model_inner(self, list_property_factory, model_property_factory, quoted): - m = model_property_factory() - p = list_property_factory(inner_property=m) - assert p.get_base_json_type_string(quoted=quoted) == "List[Dict[str, Any]]" - - def test_get_lazy_import_base_inner(self, list_property_factory): - p = list_property_factory() - assert p.get_lazy_imports(prefix="..") == set() - - def test_get_lazy_import_model_inner(self, list_property_factory, model_property_factory): - m = model_property_factory() - p = list_property_factory(inner_property=m) - assert p.get_lazy_imports(prefix="..") == {"from ..models.my_module import MyClass"} - - @pytest.mark.parametrize( - "required, expected", - ( - (True, "List[str]"), - (False, "Union[Unset, List[str]]"), - ), - ) - def test_get_type_string_base_inner(self, list_property_factory, required, expected): - p = list_property_factory(required=required) - - assert p.get_type_string() == expected - - @pytest.mark.parametrize( - "required, expected", - ( - (True, "List['MyClass']"), - (False, "Union[Unset, List['MyClass']]"), - ), - ) - def test_get_type_string_model_inner(self, list_property_factory, model_property_factory, required, expected): - m = model_property_factory() - p = list_property_factory(required=required, inner_property=m) - - assert p.get_type_string() == expected - - @pytest.mark.parametrize( - "quoted,expected", - [ - (False, "List[str]"), - (True, "List[str]"), - ], - ) - def test_get_base_type_string_base_inner(self, list_property_factory, quoted, expected): - p = list_property_factory() - assert p.get_base_type_string(quoted=quoted) == expected - - @pytest.mark.parametrize( - "quoted,expected", - [ - (False, "List['MyClass']"), - (True, "List['MyClass']"), - ], - ) - def test_get_base_type_string_model_inner(self, list_property_factory, model_property_factory, quoted, expected): - m = model_property_factory() - p = list_property_factory(inner_property=m) - assert p.get_base_type_string(quoted=quoted) == expected - - @pytest.mark.parametrize("required", (True, False)) - def test_get_type_imports(self, list_property_factory, date_time_property_factory, required): - inner_property = date_time_property_factory() - p = list_property_factory(inner_property=inner_property, required=required) - expected = { - "import datetime", - "from typing import cast", - "from dateutil.parser import isoparse", - "from typing import cast, List", - } - if not required: - expected |= { - "from typing import Union", - "from ...types import UNSET, Unset", - } - - assert p.get_imports(prefix="...") == expected - - class TestUnionProperty: def test_is_base_type(self, union_property_factory): assert union_property_factory().is_base_type is False @@ -314,299 +135,7 @@ def test_get_type_imports(self, union_property_factory, date_time_property_facto assert p.get_imports(prefix="...") == expected -class TestEnumProperty: - def test_is_base_type(self, enum_property_factory): - assert enum_property_factory().is_base_type is True - - @pytest.mark.parametrize( - "required, expected", - ( - (False, "Union[Unset, {}]"), - (True, "{}"), - ), - ) - def test_get_type_string(self, mocker, enum_property_factory, required, expected): - fake_class = mocker.MagicMock() - fake_class.name = "MyTestEnum" - - p = enum_property_factory(class_info=fake_class, required=required) - - assert p.get_type_string() == expected.format(fake_class.name) - assert p.get_type_string(no_optional=True) == fake_class.name - assert p.get_type_string(json=True) == expected.format("str") - - def test_get_imports(self, mocker, enum_property_factory): - fake_class = mocker.MagicMock(module_name="my_test_enum") - fake_class.name = "MyTestEnum" - prefix = "..." - - enum_property = enum_property_factory(class_info=fake_class, required=False) - - assert enum_property.get_imports(prefix=prefix) == { - f"from {prefix}models.{fake_class.module_name} import {fake_class.name}", - "from typing import Union", # Makes sure unset is handled via base class - "from ...types import UNSET, Unset", - } - - def test_values_from_list(self): - from openapi_python_client.parser.properties import EnumProperty - - data = ["abc", "123", "a23", "1bc", 4, -3, "a Thing WIth spaces", ""] - - result = EnumProperty.values_from_list(data, Class("ClassName", "module_name")) - - assert result == { - "ABC": "abc", - "VALUE_1": "123", - "A23": "a23", - "VALUE_3": "1bc", - "VALUE_4": 4, - "VALUE_NEGATIVE_3": -3, - "A_THING_WITH_SPACES": "a Thing WIth spaces", - "VALUE_7": "", - } - - def test_values_from_list_duplicate(self): - from openapi_python_client.parser.properties import EnumProperty - - data = ["abc", "123", "a23", "abc"] - - with pytest.raises(ValueError): - EnumProperty.values_from_list(data, Class("ClassName", "module_name")) - - -class TestLiteralEnumProperty: - def test_is_base_type(self, literal_enum_property_factory): - assert literal_enum_property_factory().is_base_type is True - - @pytest.mark.parametrize( - "required, expected", - ( - (False, "Union[Unset, {}]"), - (True, "{}"), - ), - ) - def test_get_type_string(self, mocker, literal_enum_property_factory, required, expected): - fake_class = mocker.MagicMock() - fake_class.name = "MyTestEnum" - - p = literal_enum_property_factory(class_info=fake_class, required=required) - - assert p.get_type_string() == expected.format(fake_class.name) - assert p.get_type_string(no_optional=True) == fake_class.name - assert p.get_type_string(json=True) == expected.format("str") - - def test_get_imports(self, mocker, literal_enum_property_factory): - fake_class = mocker.MagicMock(module_name="my_test_enum") - fake_class.name = "MyTestEnum" - prefix = "..." - - literal_enum_property = literal_enum_property_factory(class_info=fake_class, required=False) - - assert literal_enum_property.get_imports(prefix=prefix) == { - "from typing import cast", - f"from {prefix}models.{fake_class.module_name} import {fake_class.name}", - f"from {prefix}models.{fake_class.module_name} import check_my_test_enum", - "from typing import Union", # Makes sure unset is handled via base class - "from ...types import UNSET, Unset", - } - - class TestPropertyFromData: - def test_property_from_data_str_enum(self, enum_property_factory, config): - from openapi_python_client.parser.properties import Class, Schemas, property_from_data - from openapi_python_client.schema import Schema - - existing = enum_property_factory() - data = Schema(title="AnEnum", enum=["A", "B", "C"], default="B") - name = "my_enum" - required = True - - schemas = Schemas(classes_by_name={ClassName("AnEnum", prefix=""): existing}) - - prop, new_schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config - ) - - assert prop == enum_property_factory( - name=name, - required=required, - values={"A": "A", "B": "B", "C": "C"}, - class_info=Class(name=ClassName("ParentAnEnum", ""), module_name=PythonIdentifier("parent_an_enum", "")), - value_type=str, - default=Value(python_code="ParentAnEnum.B", raw_value="B"), - ) - assert schemas != new_schemas, "Provided Schemas was mutated" - assert new_schemas.classes_by_name == { - "AnEnum": existing, - "ParentAnEnum": prop, - } - - def test_property_from_data_str_enum_with_null( - self, enum_property_factory, union_property_factory, none_property_factory, config - ): - from openapi_python_client.parser.properties import Class, Schemas, property_from_data - from openapi_python_client.schema import Schema - - existing = enum_property_factory() - data = Schema(title="AnEnum", enum=["A", "B", "C", None], default="B") - name = "my_enum" - required = True - - schemas = Schemas(classes_by_name={ClassName("AnEnum", ""): existing}) - - prop, new_schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config - ) - - # None / null is removed from enum, and property is now nullable - assert isinstance(prop, UnionProperty), "Enums with None should be converted to UnionProperties" - enum_prop = enum_property_factory( - name="my_enum_type_1", - required=required, - values={"A": "A", "B": "B", "C": "C"}, - class_info=Class(name=ClassName("ParentAnEnum", ""), module_name=PythonIdentifier("parent_an_enum", "")), - value_type=str, - default=Value(python_code="ParentAnEnum.B", raw_value="B"), - ) - none_property = none_property_factory(name="my_enum_type_0", required=required) - assert prop == union_property_factory( - name=name, - default=Value(python_code="ParentAnEnum.B", raw_value="B"), - inner_properties=[none_property, enum_prop], - ) - assert schemas != new_schemas, "Provided Schemas was mutated" - assert new_schemas.classes_by_name == { - "AnEnum": existing, - "ParentAnEnum": enum_prop, - } - - def test_property_from_data_null_enum(self, enum_property_factory, none_property_factory, config): - from openapi_python_client.parser.properties import Schemas, property_from_data - from openapi_python_client.schema import Schema - - data = Schema(title="AnEnumWithOnlyNull", enum=[None], default=None) - name = "my_enum" - required = True - - schemas = Schemas() - - prop, new_schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config - ) - - assert prop == none_property_factory( - name="my_enum", required=required, default=Value(python_code="None", raw_value="None") - ) - - def test_property_from_data_int_enum(self, enum_property_factory, config): - from openapi_python_client.parser.properties import Class, Schemas, property_from_data - from openapi_python_client.schema import Schema - - name = "my_enum" - required = True - data = Schema.model_construct(title="anEnum", enum=[1, 2, 3], default=3) - - existing = enum_property_factory() - schemas = Schemas(classes_by_name={ClassName("AnEnum", ""): existing}) - - prop, new_schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config - ) - - assert prop == enum_property_factory( - name=name, - required=required, - values={"VALUE_1": 1, "VALUE_2": 2, "VALUE_3": 3}, - class_info=Class(name=ClassName("ParentAnEnum", ""), module_name=PythonIdentifier("parent_an_enum", "")), - value_type=int, - default=Value(python_code="ParentAnEnum.VALUE_3", raw_value=3), - ) - assert schemas != new_schemas, "Provided Schemas was mutated" - assert new_schemas.classes_by_name == { - "AnEnum": existing, - "ParentAnEnum": prop, - } - - def test_property_from_data_ref_enum(self, enum_property_factory, config): - from openapi_python_client.parser.properties import Class, Schemas, property_from_data - - name = "some_enum" - data = oai.Reference.model_construct(ref="#/components/schemas/MyEnum") - existing_enum = enum_property_factory( - name="an_enum", - required=False, - values={"A": "a"}, - class_info=Class(name="MyEnum", module_name="my_enum"), - ) - schemas = Schemas(classes_by_reference={"/components/schemas/MyEnum": existing_enum}) - - prop, new_schemas = property_from_data( - name=name, required=False, data=data, schemas=schemas, parent_name="", config=config - ) - - assert prop == enum_property_factory( - name="some_enum", - required=False, - values={"A": "a"}, - class_info=Class(name="MyEnum", module_name="my_enum"), - ) - assert schemas == new_schemas - - def test_property_from_data_ref_enum_with_overridden_default(self, enum_property_factory, config): - from openapi_python_client.parser.properties import Class, Schemas, property_from_data - - name = "some_enum" - required = False - data = oai.Schema.model_construct( - default="b", allOf=[oai.Reference.model_construct(ref="#/components/schemas/MyEnum")] - ) - existing_enum = enum_property_factory( - name="an_enum", - default=Value(python_code="MyEnum.A", raw_value="A"), - required=required, - values={"A": "a", "B": "b"}, - class_info=Class(name=ClassName("MyEnum", ""), module_name=PythonIdentifier("my_enum", "")), - ) - schemas = Schemas(classes_by_reference={ReferencePath("/components/schemas/MyEnum"): existing_enum}) - - prop, new_schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="", config=config - ) - new_schemas = attr.evolve(new_schemas, models_to_process=[]) # intermediate state irrelevant to this test - - assert prop == enum_property_factory( - name="some_enum", - default=Value(python_code="MyEnum.B", raw_value="b"), - required=required, - values={"A": "a", "B": "b"}, - class_info=Class(name=ClassName("MyEnum", ""), module_name=PythonIdentifier("my_enum", "")), - ) - assert schemas == new_schemas - - def test_property_from_data_ref_enum_with_invalid_default(self, enum_property_factory, config): - from openapi_python_client.parser.properties import Class, Schemas, property_from_data - - name = "some_enum" - data = oai.Schema.model_construct( - default="x", allOf=[oai.Reference.model_construct(ref="#/components/schemas/MyEnum")] - ) - existing_enum = enum_property_factory( - name="an_enum", - default=Value(python_code="MyEnum.A", raw_value="A"), - values={"A": "a", "B": "b"}, - class_info=Class(name=ClassName("MyEnum", ""), module_name=PythonIdentifier("my_enum", "")), - python_name=PythonIdentifier("an_enum", ""), - ) - schemas = Schemas(classes_by_reference={ReferencePath("/components/schemas/MyEnum"): existing_enum}) - - prop, new_schemas = property_from_data( - name=name, required=False, data=data, schemas=schemas, parent_name="", config=config - ) - - assert schemas == new_schemas - assert prop == PropertyError(data=data, detail="Value x is not valid for enum an_enum") - def test_property_from_data_ref_model(self, model_property_factory, config): from openapi_python_client.parser.properties import Class, Schemas, property_from_data @@ -690,180 +219,8 @@ def test_property_from_data_invalid_ref(self, mocker): assert prop == PropertyError(data=data, detail="bad stuff") assert schemas == new_schemas - def test_property_from_data_array(self, config): - from openapi_python_client.parser.properties import Schemas, property_from_data - - name = "a_list_prop" - required = True - data = oai.Schema( - type=DataType.ARRAY, - items=oai.Schema(type=DataType.STRING), - ) - schemas = Schemas() - - response = property_from_data( - name=name, - required=required, - data=data, - schemas=schemas, - parent_name="parent", - config=config, - )[0] - - assert isinstance(response, ListProperty) - assert isinstance(response.inner_property, StringProperty) - - def test_property_from_data_union(self, config): - from openapi_python_client.parser.properties import Schemas, property_from_data - - name = "union_prop" - required = True - data = oai.Schema( - anyOf=[oai.Schema(type=DataType.NUMBER)], - oneOf=[ - oai.Schema(type=DataType.INTEGER), - ], - ) - schemas = Schemas() - - response = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config - )[0] - - assert isinstance(response, UnionProperty) - assert len(response.inner_properties) == 2 - - def test_property_from_data_list_of_types(self, config): - from openapi_python_client.parser.properties import Schemas, property_from_data - - name = "union_prop" - required = True - data = oai.Schema( - type=[DataType.NUMBER, DataType.NULL], - ) - schemas = Schemas() - - response = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config - )[0] - - assert isinstance(response, UnionProperty) - assert len(response.inner_properties) == 2 - - def test_property_from_data_union_of_one_element(self, model_property_factory, config): - from openapi_python_client.parser.properties import Schemas, property_from_data - - name = "new_name" - required = False - class_name = "MyModel" - existing_model: ModelProperty = model_property_factory() - schemas = Schemas(classes_by_reference={f"/{class_name}": existing_model}) - - data = oai.Schema.model_construct( - allOf=[oai.Reference.model_construct(ref=f"#/{class_name}")], - ) - - prop, schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config - ) - - assert prop == attr.evolve(existing_model, name=name, required=required, python_name=PythonIdentifier(name, "")) - - def test_property_from_data_no_valid_props_in_data(self, any_property_factory): - from openapi_python_client.parser.properties import Schemas, property_from_data - - schemas = Schemas() - data = oai.Schema() - name = "blah" - - prop, new_schemas = property_from_data( - name=name, required=True, data=data, schemas=schemas, parent_name="parent", config=MagicMock() - ) - - assert prop == any_property_factory(name=name, required=True, default=None) - assert new_schemas == schemas - class TestStringBasedProperty: - @pytest.mark.parametrize("required", (True, False)) - def test_no_format(self, string_property_factory, required, config): - from openapi_python_client.parser.properties import property_from_data - - name = "some_prop" - data = oai.Schema.model_construct(type="string", default="hello world") - - p, _ = property_from_data( - name=name, required=required, data=data, parent_name=None, config=config, schemas=Schemas() - ) - - assert p == string_property_factory( - name=name, required=required, default=StringProperty.convert_value("hello world") - ) - - def test_datetime_format(self, date_time_property_factory, config): - from openapi_python_client.parser.properties import property_from_data - - name = "datetime_prop" - required = True - data = oai.Schema.model_construct(type="string", schema_format="date-time", default="2020-11-06T12:00:00") - - p, _ = property_from_data( - name=name, required=required, data=data, schemas=Schemas(), config=config, parent_name="" - ) - - assert p == date_time_property_factory( - name=name, - required=required, - default=Value(python_code=f"isoparse('{data.default}')", raw_value=data.default), - ) - - def test_datetime_bad_default(self, config): - from openapi_python_client.parser.properties import property_from_data - - name = "datetime_prop" - required = True - data = oai.Schema.model_construct(type="string", schema_format="date-time", default="a") - - result, _ = property_from_data( - name=name, required=required, data=data, schemas=Schemas(), config=config, parent_name="" - ) - - assert isinstance(result, PropertyError) - assert result.detail.startswith("Invalid datetime") - - def test_date_format(self, date_property_factory, config): - from openapi_python_client.parser.properties import property_from_data - - name = "date_prop" - required = True - - data = oai.Schema.model_construct(type="string", schema_format="date", default="2020-11-06") - - p, _ = property_from_data( - name=name, required=required, data=data, schemas=Schemas(), config=config, parent_name="" - ) - - assert p == date_property_factory( - name=name, - required=required, - default=Value(python_code=f"isoparse('{data.default}').date()", raw_value=data.default), - ) - - def test_date_format_bad_default(self, config): - from openapi_python_client.parser.properties import property_from_data - - name = "date_prop" - required = True - - data = oai.Schema.model_construct(type="string", schema_format="date", default="a") - - p, _ = property_from_data( - name=name, required=required, data=data, schemas=Schemas(), config=config, parent_name="" - ) - - assert isinstance(p, PropertyError) - assert p.detail.startswith("Invalid date") - def test__string_based_property_binary_format(self, file_property_factory, config): from openapi_python_client.parser.properties import property_from_data @@ -876,19 +233,6 @@ def test__string_based_property_binary_format(self, file_property_factory, confi ) assert p == file_property_factory(name=name, required=required) - def test__string_based_property_unsupported_format(self, string_property_factory, config): - from openapi_python_client.parser.properties import property_from_data - - name = "unknown" - required = True - data = oai.Schema.model_construct(type="string", schema_format="blah") - - p, _ = property_from_data( - name=name, required=required, data=data, schemas=Schemas(), config=config, parent_name="" - ) - - assert p == string_property_factory(name=name, required=required) - class TestCreateSchemas: def test_skips_references_and_keeps_going(self, mocker, config): diff --git a/tests/test_parser/test_properties/test_int.py b/tests/test_parser/test_properties/test_int.py deleted file mode 100644 index 7f9953761..000000000 --- a/tests/test_parser/test_properties/test_int.py +++ /dev/null @@ -1,34 +0,0 @@ -from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import IntProperty -from openapi_python_client.parser.properties.protocol import Value -from openapi_python_client.utils import PythonIdentifier - - -def test_invalid_default(): - err = IntProperty.build( - default="not a float", - description=None, - example=None, - required=False, - python_name="not_a_float", - name="not_a_float", - ) - - assert isinstance(err, PropertyError) - - -def test_convert_from_string(): - assert IntProperty.convert_value("1") == Value(python_code="1", raw_value="1") - - -def test_invalid_type_default(): - err = IntProperty.build( - default=True, - description=None, - example=None, - required=False, - python_name=PythonIdentifier("not_a_float", ""), - name="not_a_float", - ) - - assert isinstance(err, PropertyError) diff --git a/tests/test_parser/test_properties/test_list_property.py b/tests/test_parser/test_properties/test_list_property.py deleted file mode 100644 index 60fb0a35d..000000000 --- a/tests/test_parser/test_properties/test_list_property.py +++ /dev/null @@ -1,178 +0,0 @@ -import attr - -import openapi_python_client.schema as oai -from openapi_python_client.parser.errors import ParseError, PropertyError -from openapi_python_client.parser.properties import ListProperty -from openapi_python_client.parser.properties.schemas import ReferencePath -from openapi_python_client.schema import DataType -from openapi_python_client.utils import ClassName - - -def test_build_list_property_no_items(config): - from openapi_python_client.parser import properties - - name = "list_prop" - required = True - data = oai.Schema(type=DataType.ARRAY) - schemas = properties.Schemas() - - p, new_schemas = ListProperty.build( - name=name, - required=required, - data=data, - schemas=schemas, - parent_name="parent", - config=config, - process_properties=True, - roots={ReferencePath("root")}, - ) - - assert p == PropertyError(data=data, detail="type array must have items or prefixItems defined") - assert new_schemas == schemas - - -def test_build_list_property_invalid_items(config): - from openapi_python_client.parser import properties - - name = "name" - required = True - data = oai.Schema( - type=DataType.ARRAY, - items=oai.Reference.model_validate({"$ref": "doesnt exist"}), - ) - schemas = properties.Schemas(errors=[ParseError("error")]) - process_properties = False - roots: set[ReferencePath | ClassName] = {ReferencePath("root")} - - p, new_schemas = ListProperty.build( - name=name, - required=required, - data=data, - schemas=attr.evolve(schemas), - parent_name="parent", - config=config, - roots=roots, - process_properties=process_properties, - ) - - assert isinstance(p, PropertyError) - assert p.data == data.items - assert p.header.startswith(f"invalid data in items of array {name}") - assert new_schemas == schemas - - -def test_build_list_property(any_property_factory, config): - from openapi_python_client.parser import properties - - name = "prop" - data = oai.Schema( - type=DataType.ARRAY, - items=oai.Schema(), - ) - schemas = properties.Schemas(errors=[ParseError("error")]) - - p, new_schemas = ListProperty.build( - name=name, - required=True, - data=data, - schemas=schemas, - parent_name="parent", - config=config, - roots={ReferencePath("root")}, - process_properties=True, - ) - - assert isinstance(p, properties.ListProperty) - assert p.inner_property == any_property_factory(name=f"{name}_item") - assert new_schemas == schemas - - -def test_build_list_property_single_prefix_item(any_property_factory, config): - from openapi_python_client.parser import properties - - name = "prop" - data = oai.Schema( - type=DataType.ARRAY, - prefixItems=[oai.Schema()], - ) - schemas = properties.Schemas(errors=[ParseError("error")]) - - p, new_schemas = ListProperty.build( - name=name, - required=True, - data=data, - schemas=schemas, - parent_name="parent", - config=config, - roots={ReferencePath("root")}, - process_properties=True, - ) - - assert isinstance(p, properties.ListProperty) - assert p.inner_property == any_property_factory(name=f"{name}_item") - assert new_schemas == schemas - - -def test_build_list_property_items_and_prefix_items( - union_property_factory, - string_property_factory, - none_property_factory, - int_property_factory, - config, -): - from openapi_python_client.parser import properties - - name = "list_prop" - required = True - data = oai.Schema( - type=DataType.ARRAY, - items=oai.Schema(type=DataType.INTEGER), - prefixItems=[oai.Schema(type=DataType.STRING), oai.Schema(type=DataType.NULL)], - ) - schemas = properties.Schemas() - - p, new_schemas = ListProperty.build( - name=name, - required=required, - data=data, - schemas=schemas, - parent_name="parent", - config=config, - process_properties=True, - roots={ReferencePath("root")}, - ) - - assert isinstance(p, properties.ListProperty) - assert p.inner_property == union_property_factory( - name=f"{name}_item", - inner_properties=[ - string_property_factory(name=f"{name}_item_type_0"), - none_property_factory(name=f"{name}_item_type_1"), - int_property_factory(name=f"{name}_item_type_2"), - ], - ) - assert new_schemas == schemas - - -def test_build_list_property_prefix_items_only(any_property_factory, config): - from openapi_python_client.parser import properties - - name = "list_prop" - required = True - data = oai.Schema(type=DataType.ARRAY, prefixItems=[oai.Schema()]) - schemas = properties.Schemas() - - p, new_schemas = ListProperty.build( - name=name, - required=required, - data=data, - schemas=schemas, - parent_name="parent", - config=config, - process_properties=True, - roots={ReferencePath("root")}, - ) - - assert isinstance(p, properties.ListProperty) - assert p.inner_property == any_property_factory(name=f"{name}_item") - assert new_schemas == schemas diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index 8adc88e39..a51fd984b 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -19,12 +19,12 @@ class TestModelProperty: (False, True, False, False, "MyClass"), (True, False, False, False, "MyClass"), (True, True, False, False, "MyClass"), - (False, True, True, False, "Dict[str, Any]"), + (False, True, True, False, "dict[str, Any]"), (False, False, False, True, "Union[Unset, 'MyClass']"), (False, True, False, True, "'MyClass'"), (True, False, False, True, "'MyClass'"), (True, True, False, True, "'MyClass'"), - (False, True, True, True, "Dict[str, Any]"), + (False, True, True, True, "dict[str, Any]"), ], ) def test_get_type_string(self, no_optional, required, json, expected, model_property_factory, quoted): @@ -40,7 +40,6 @@ def test_get_imports(self, model_property_factory): assert prop.get_imports(prefix="..") == { "from typing import Union", "from ..types import UNSET, Unset", - "from typing import Dict", "from typing import cast", } @@ -719,8 +718,8 @@ def test_set_relative_imports(model_property_factory): from openapi_python_client.parser.properties import Class class_info = Class("ClassName", module_name="module_name") - relative_imports = {"from typing import List", f"from ..models.{class_info.module_name} import {class_info.name}"} + relative_imports = {f"from ..models.{class_info.module_name} import {class_info.name}"} model_property = model_property_factory(class_info=class_info, relative_imports=relative_imports) - assert model_property.relative_imports == {"from typing import List"} + assert model_property.relative_imports == set() diff --git a/tests/test_parser/test_properties/test_none.py b/tests/test_parser/test_properties/test_none.py index d61ca0136..b6289cdb8 100644 --- a/tests/test_parser/test_properties/test_none.py +++ b/tests/test_parser/test_properties/test_none.py @@ -5,6 +5,8 @@ def test_default(): + # currently this is testing an unused code path: + # https://github.com/openapi-generators/openapi-python-client/issues/1162 err = NoneProperty.build( default="not None", description=None, diff --git a/tests/test_parser/test_properties/test_protocol.py b/tests/test_parser/test_properties/test_protocol.py index 1d4111750..5c2f1c993 100644 --- a/tests/test_parser/test_properties/test_protocol.py +++ b/tests/test_parser/test_properties/test_protocol.py @@ -3,6 +3,7 @@ import pytest from openapi_python_client.parser.properties.protocol import Value +from openapi_python_client.parser.properties.schemas import ReferencePath def test_is_base_type(any_property_factory): @@ -85,3 +86,12 @@ def test_get_base_json_type_string(quoted, expected, any_property_factory, mocke mocker.patch.object(AnyProperty, "_json_type_string", "str") p = any_property_factory() assert p.get_base_json_type_string(quoted=quoted) is expected + + +def test_ref_path(any_property_factory, model_property_factory): + p1 = any_property_factory() + assert p1.get_ref_path() is None + + path = ReferencePath("/components/schemas/A") + p2 = model_property_factory(ref_path=path) + assert p2.get_ref_path() == path diff --git a/tests/test_parser/test_properties/test_union.py b/tests/test_parser/test_properties/test_union.py index acbbd06d6..84e00067d 100644 --- a/tests/test_parser/test_properties/test_union.py +++ b/tests/test_parser/test_properties/test_union.py @@ -1,63 +1,9 @@ import openapi_python_client.schema as oai -from openapi_python_client.parser.errors import ParseError, PropertyError +from openapi_python_client.parser.errors import ParseError from openapi_python_client.parser.properties import Schemas, UnionProperty -from openapi_python_client.parser.properties.protocol import Value from openapi_python_client.schema import DataType, ParameterLocation -def test_property_from_data_union(union_property_factory, date_time_property_factory, string_property_factory, config): - from openapi_python_client.parser.properties import Schemas, property_from_data - - name = "union_prop" - required = True - data = oai.Schema( - anyOf=[oai.Schema(type=DataType.STRING, default="a")], - oneOf=[ - oai.Schema(type=DataType.STRING, schema_format="date-time"), - ], - ) - expected = union_property_factory( - name=name, - required=required, - inner_properties=[ - string_property_factory(name=f"{name}_type_0", default=Value("'a'", "a")), - date_time_property_factory(name=f"{name}_type_1"), - ], - ) - - p, s = property_from_data( - name=name, required=required, data=data, schemas=Schemas(), parent_name="parent", config=config - ) - - assert p == expected - assert s == Schemas() - - -def test_build_union_property_invalid_property(config): - name = "bad_union" - required = True - reference = oai.Reference.model_construct(ref="#/components/schema/NotExist") - data = oai.Schema(anyOf=[reference]) - - p, s = UnionProperty.build( - name=name, required=required, data=data, schemas=Schemas(), parent_name="parent", config=config - ) - assert p == PropertyError(detail=f"Invalid property in union {name}", data=reference) - - -def test_invalid_default(config): - data = oai.Schema( - type=[DataType.NUMBER, DataType.INTEGER], - default="a", - ) - - err, _ = UnionProperty.build( - data=data, required=True, schemas=Schemas(), parent_name="parent", name="name", config=config - ) - - assert isinstance(err, PropertyError) - - def test_invalid_location(config): data = oai.Schema( type=[DataType.NUMBER, DataType.NULL], diff --git a/tests/test_parser/test_responses.py b/tests/test_parser/test_responses.py index 0ac885764..24fb94c61 100644 --- a/tests/test_parser/test_responses.py +++ b/tests/test_parser/test_responses.py @@ -1,5 +1,7 @@ from unittest.mock import MagicMock +import pytest + import openapi_python_client.schema as oai from openapi_python_client.parser.errors import ParseError, PropertyError from openapi_python_client.parser.properties import Schemas @@ -17,6 +19,7 @@ def test_response_from_data_no_content(any_property_factory): status_code=200, data=data, schemas=Schemas(), + responses={}, parent_name="parent", config=MagicMock(), ) @@ -34,31 +37,6 @@ def test_response_from_data_no_content(any_property_factory): ) -def test_response_from_data_reference(any_property_factory): - from openapi_python_client.parser.responses import Response, response_from_data - - data = oai.Reference.model_construct() - - response, schemas = response_from_data( - status_code=200, - data=data, - schemas=Schemas(), - parent_name="parent", - config=MagicMock(), - ) - - assert response == Response( - status_code=200, - prop=any_property_factory( - name="response_200", - default=None, - required=True, - ), - source=NONE_SOURCE, - data=data, - ) - - def test_response_from_data_unsupported_content_type(): from openapi_python_client.parser.responses import response_from_data @@ -69,6 +47,7 @@ def test_response_from_data_unsupported_content_type(): status_code=200, data=data, schemas=Schemas(), + responses={}, parent_name="parent", config=config, ) @@ -89,6 +68,7 @@ def test_response_from_data_no_content_schema(any_property_factory): status_code=200, data=data, schemas=Schemas(), + responses={}, parent_name="parent", config=config, ) @@ -121,6 +101,7 @@ def test_response_from_data_property_error(mocker): status_code=400, data=data, schemas=Schemas(), + responses={}, parent_name="parent", config=config, ) @@ -152,6 +133,7 @@ def test_response_from_data_property(mocker, any_property_factory): status_code=400, data=data, schemas=Schemas(), + responses={}, parent_name="parent", config=config, ) @@ -172,6 +154,99 @@ def test_response_from_data_property(mocker, any_property_factory): ) +def test_response_from_data_reference(mocker, any_property_factory): + from openapi_python_client.parser import responses + + prop = any_property_factory() + mocker.patch.object(responses, "property_from_data", return_value=(prop, Schemas())) + predefined_response_data = oai.Response.model_construct( + description="", + content={"application/json": oai.MediaType.model_construct(media_type_schema="something")}, + ) + config = MagicMock() + config.content_type_overrides = {} + + response, schemas = responses.response_from_data( + status_code=400, + data=oai.Reference.model_construct(ref="#/components/responses/ErrorResponse"), + schemas=Schemas(), + responses={"ErrorResponse": predefined_response_data}, + parent_name="parent", + config=config, + ) + + assert response == responses.Response( + status_code=400, + prop=prop, + source=JSON_SOURCE, + data=predefined_response_data, + ) + + +@pytest.mark.parametrize( + "ref_string,expected_error_string", + [ + ("#/components/responses/Nonexistent", "Could not find"), + ("https://remote-reference", "Remote references"), + ("#/components/something-that-isnt-responses/ErrorResponse", "not allowed in responses"), + ], +) +def test_response_from_data_invalid_reference(ref_string, expected_error_string, mocker, any_property_factory): + from openapi_python_client.parser import responses + + prop = any_property_factory() + mocker.patch.object(responses, "property_from_data", return_value=(prop, Schemas())) + predefined_response_data = oai.Response.model_construct( + description="", + content={"application/json": oai.MediaType.model_construct(media_type_schema="something")}, + ) + config = MagicMock() + config.content_type_overrides = {} + + response, schemas = responses.response_from_data( + status_code=400, + data=oai.Reference.model_construct(ref=ref_string), + schemas=Schemas(), + responses={"ErrorResponse": predefined_response_data}, + parent_name="parent", + config=config, + ) + + assert isinstance(response, ParseError) + assert expected_error_string in response.detail + + +def test_response_from_data_ref_to_response_that_is_a_ref(mocker, any_property_factory): + from openapi_python_client.parser import responses + + prop = any_property_factory() + mocker.patch.object(responses, "property_from_data", return_value=(prop, Schemas())) + predefined_response_base_data = oai.Response.model_construct( + description="", + content={"application/json": oai.MediaType.model_construct(media_type_schema="something")}, + ) + predefined_response_data = oai.Reference.model_construct( + ref="#/components/references/BaseResponse", + ) + config = MagicMock() + config.content_type_overrides = {} + + response, schemas = responses.response_from_data( + status_code=400, + data=oai.Reference.model_construct(ref="#/components/responses/ErrorResponse"), + schemas=Schemas(), + responses={ + "BaseResponse": predefined_response_base_data, + "ErrorResponse": predefined_response_data, + }, + parent_name="parent", + config=config, + ) + + assert isinstance(response, ParseError) + assert "Top-level $ref" in response.detail + + def test_response_from_data_content_type_overrides(any_property_factory): from openapi_python_client.parser.responses import Response, response_from_data @@ -185,6 +260,7 @@ def test_response_from_data_content_type_overrides(any_property_factory): status_code=200, data=data, schemas=Schemas(), + responses={}, parent_name="parent", config=config, ) diff --git a/tests/test_utils.py b/tests/test_utils.py index e7dccf9a8..fafa61805 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -99,7 +99,7 @@ def test_no_string_escapes(): ("int", "int_"), ("dict", "dict_"), ("not_reserved", "not_reserved"), - ("type", "type"), + ("type", "type_"), ("id", "id"), ("None", "None_"), ],