diff --git a/.github/renovate.json b/.github/renovate.json index d0cc4a064..a41a99a8f 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,10 +1,12 @@ { "extends": [ - "config:base", - ":semanticCommitTypeAll(chore)" + "config:recommended", + ":semanticCommitTypeAll(chore)", + "helpers:pinGitHubActionDigestsToSemver" ], "rangeStrategy": "widen", "lockFileMaintenance": { "enabled": true, "automerge": true }, + "minimumReleaseAge": "7 days", "regexManagers": [ { "fileMatch": [ diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index a31ca244a..948d11c65 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -11,13 +11,13 @@ jobs: test: strategy: matrix: - python: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] + python: [ "3.11", "3.12", "3.13", "3.14" ] os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@v5.4.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python }} @@ -27,7 +27,7 @@ jobs: shell: bash - name: Cache dependencies - uses: actions/cache@v4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .venv key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies-${{ hashFiles('**/pdm.lock') }} @@ -37,7 +37,7 @@ jobs: run: pip install pdm - name: Install Dependencies - run: pdm install + run: pdm sync --clean - name: Check formatting run: pdm run ruff format . --check @@ -60,7 +60,7 @@ jobs: if: matrix.os == 'ubuntu-latest' - name: Store coverage report - uses: actions/upload-artifact@v4.6.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: matrix.os == 'ubuntu-latest' with: name: coverage-${{ matrix.python }} @@ -74,11 +74,11 @@ jobs: os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@v5.4.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: "3.9" + python-version: "3.11" - name: Get Python Version id: get_python_version @@ -86,7 +86,7 @@ jobs: shell: bash - name: Cache dependencies - uses: actions/cache@v4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .venv key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-min-dependencies-${{ hashFiles('**/pdm.lock') }} @@ -96,14 +96,11 @@ jobs: run: pip install pdm - name: Install minimum dependencies - run: pdm install -L pdm.minimal.lock + run: pdm sync --clean -L pdm.minimal.lock - name: Run mypy run: pdm mypy --show-error-codes - - name: Lint - run: pdm run ruff check . - - name: Run unit tests only # snapshots are expected to fail run: pdm unit_test @@ -112,12 +109,12 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 - - uses: actions/setup-python@v5 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 with: python-version: "3.12" - name: Download coverage reports - uses: actions/download-artifact@v4.1.9 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: merge-multiple: true @@ -131,18 +128,18 @@ jobs: # Find all of the downloaded coverage reports and combine them .venv/bin/python -m coverage combine - + # Create html report .venv/bin/python -m coverage html --skip-covered --skip-empty - + # Report in Markdown and write to summary. .venv/bin/python -m coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - + # Report again and fail if under 100%. - .venv/bin/python -m coverage report --fail-under=100 + .venv/bin/python -m coverage report --fail-under=100 - name: Upload HTML report if check failed. - uses: actions/upload-artifact@v4.6.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: html-report path: htmlcov @@ -153,51 +150,35 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - httpx_version: - - "0.20.0" - - "" + lockfile: + - "pdm.lock" + - "pdm.minimal.lock" services: openapi-test-server: - image: ghcr.io/openapi-generators/openapi-test-server:0.0.1 + image: ghcr.io/openapi-generators/openapi-test-server:0.2.1 ports: - "3000:3000" steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@v5.4.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: "3.9" + python-version: "3.11" - name: Get Python Version id: get_python_version run: echo "python_version=$(python --version)" >> $GITHUB_OUTPUT - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: .venv - key: ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies-${{ hashFiles('**/pdm.lock') }} - restore-keys: | - ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-dependencies - - name: Install dependencies - run: | - pip install pdm - python -m venv .venv - pdm install - name: Cache Generated Client Dependencies - uses: actions/cache@v4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 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('integration-tests/pdm*.lock') }} restore-keys: | ${{ runner.os }}-${{ steps.get_python_version.outputs.python_version }}-integration-dependencies - - name: Set httpx version - if: matrix.httpx_version != '' - run: | - cd integration-tests - pdm add httpx==${{ matrix.httpx_version }} - name: Install Integration Dependencies run: | cd integration-tests - pdm install + pip install pdm + pdm sync --clean -L ${{ matrix.lockfile }} - name: Run Tests run: | cd integration-tests diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ae11c09f4..163bb4ab5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,12 @@ jobs: permissions: id-token: write steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Hatchling run: pip install --upgrade hatchling - name: Build run: hatchling build - name: Push to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.4 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: attestations: true diff --git a/.gitignore b/.gitignore index b04b9f514..1cd5f6801 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ 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 +tests/tmp \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f2e23582..9887dad3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,277 @@ 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.29.0 (2026-05-30) + +### Breaking Changes + +- Drop support for Python 3.10 +- Raise minimum httpx version to 0.23.1 + +### Features + +- replace python-dateutil with stdlib datetime.fromisoformat (#1429) + +### Fixes + +- Remove some generated casts that aren't necessary with mypy v2 (#1436) +- Explicitly set boundary for multipart/form-data (#1005) + +## 0.28.4 (2026-05-11) + +### Features + +- Update `uv_build` to 0.11 when using `--meta=uv` (#1434) + +#### Add support for x-enum-varnames to string enums + +##1358 by @mbbush + +You can now customize the variable names of the generated string enumerations using the x-enum-varnames openapi extension. Previously, this was only possible for integer enumerations. + +## 0.28.3 (2026-02-17) + +### Fixes + +- sort remaining lazy imports in model template (#1400) + +## 0.28.2 (2026-02-09) + +### Features + +- Update `uv_build` 0.10 when using `--meta=uv` (#1396) + +## 0.28.1 (2026-01-10) + +### Fixes + +- Apply required overrides from allOf schemas (#1384) +- Sort lazy imports to increase stability of generated code (#1378) + +## 0.28.0 (2025-12-03) + +### Breaking Changes + +- URL-encode path parameters in generated endpoints (#1349) + +### Fixes + +#### Fix bad code generation + +##1360 by @EricAtORS + +This fixes: +- missing parenthesis in to_multipart + #1338 #1318 +- missing imports in the lazy eval in to_multipart: +##931 and #1051 + +#### Fix optional bodies + +If a body is not required (the default), it will now: + +1. Have `Unset` as part of its type annotation. +2. Default to a value of `UNSET` +3. Not be included in the request if it is `UNSET` + +Thanks @orelmaliach for the report! Fixes #1354 + +## 0.27.1 (2025-11-03) + +### Fixes + +- Remove non-existent CHANGELOG.md references from UV and Poetry templates (#1344) +- Initialize optional lists as UNSET, not [] (#1346) +- Correct docstring typos in client template (#1347) +- Replace bare except blocks with specific exception types (#1348) + +#### Update `uv_build` to 0.9 + +##1352 by @johnthagen + +`uv` has been in the `0.9.x` release cycle for a while, so update templates to use the corresponding `uv_build` range. + +## 0.27.0 (2025-10-28) + +### Breaking Changes + +#### Drop support for Python 3.9 + +Both `openapi-python-client` itself and any generated clients no longer support Python 3.9. + +#### Generated models now use `from __future__ import annotations` + +This simplifies using forward references with the newer union syntax. + +### Features + +#### Upgrade generated clients to 3.10 union syntax + +All generated types now use the `A | B` syntax instead of `Union[A, B]` or `Optional[A]`. + +### Fixes + +- Drop generated `requires-python` upper bounds for uv and PDM (#1329) + +#### Change default Ruff hook to `--fix-only` + +This should enable `openapi-python-client` to keep auto-fixing lints (like removing unused imports) but _not_ fail to +generate when unfixable lints are violated. + +Since it's now unlikely for breaking changes to affect our usage (and by popular request), the upper bound of `ruff` +has been lifted. Newer versions of `openapi-python-client` should no longer be required to support newer versions of `ruff`. + +### Notes + +- Minimum Typer version is now 0.16 + +## 0.26.2 (2025-10-06) + +### Fixes + +- ambigious tilde specifier requires-python with`--meta=uv` (#1321) + +## 0.26.1 (2025-09-13) + +### Features + +- Reference schema support (#800) (#1307) +- Support Ruff 0.13 + +## 0.26.0 (2025-08-26) + +### Breaking Changes + +#### Change some union variant names + +When creating a union with `oneOf`, `anyOf`, or a list of `type`, the name of each variant used to be `type_{index}` +where the index is based on the order of the types in the union. + +This made some modules difficult to understand, what is a `my_type_type_0` after all? +It also meant that reordering union members, while not a breaking change to the API, _would_ be a breaking change +for generated clients. + +Now, if an individual variant has a `title` attribute, that `title` will be used in the name instead. +This is only an enhancement for documents which use `title` in union variants, and only a breaking change for +_inline models_ (not `#/components/schemas` which should already have used more descriptive names). + +Thanks @wallagib for PR #962! + +### Features + +#### Support patterned and default HTTP statuses + +HTTP statuses like `2XX` and `default` are now supported! + +A big thank you to: +- @PSU3D0 for PR #973 (eons ago 😅) +- @obs-gh-peterkolloch for PR #1300 +- @goodsonjr for PR #1304 + +Closes #1271 and #832 + +> [!NOTE] +> Custom template users: the `endpoint.responses` type has changed quite a bit. Check out #1303 for the changes. + +## 0.25.3 (2025-07-21) + +### Features + +- Add --meta uv for generating astral-sh/uv compatible packages. (#1286) +- Switch to `uv_build` build backend. (#1290) + +## 0.25.2 (2025-07-03) + +### Fixes + +- Import error for `types.FileType` (#1274) (#1278) + +## 0.25.1 (2025-06-19) + +### Fixes + +- Support ruff 0.12 (#1270) + +## 0.25.0 (2025-06-06) + +### Breaking Changes + +- Raise minimum httpx version to 0.23 + +#### Removed ability to set an array as a multipart body + +Previously, when defining a request's body as `multipart/form-data`, the generator would attempt to generate code +for both `object` schemas and `array` schemas. However, most arrays could not generate valid multipart bodies, as +there would be no field names (required to set the `Content-Disposition` headers). + +The code to generate any body for `multipart/form-data` where the schema is `array` has been removed, and any such +bodies will be skipped. This is not _expected_ to be a breaking change in practice, since the code generated would +probably never work. + +If you have a use-case for `multipart/form-data` with an `array` schema, please [open a new discussion](https://github.com/openapi-generators/openapi-python-client/discussions) with an example schema and the desired functional Python code. + +#### Change default multipart array serialization + +Previously, any arrays of values in a `multipart/form-data` body would be serialized as an `application/json` part. +This matches the default behavior specified by OpenAPI and supports arrays of files (`binary` format strings). +However, because this generator doesn't yet support specifying `encoding` per property, this may result in +now-incorrect code when the encoding _was_ explicitly set to `application/json` for arrays of scalar values. + +PR #938 fixes #692. Thanks @micha91 for the fix, @ratgen and @FabianSchurig for testing, and @davidlizeng for the original report... many years ago 😅. + +## 0.24.3 (2025-03-31) + +### Features + +#### Adding support for named integer enums + +##1214 by @barrybarrette + +Adding support for named integer enums via an optional extension, `x-enum-varnames`. + +This extension is added to the schema inline with the `enum` definition: +``` +"MyEnum": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 99 + ], + "type": "integer", + "format": "int32", + "x-enum-varnames": [ + "Deinstalled", + "Installed", + "Upcoming_Site", + "Lab_Site", + "Pending_Deinstall", + "Suspended", + "Install_In_Progress", + "Unknown" + ] +} +``` + +The result: +![image](https://github.com/user-attachments/assets/780880b3-2f1f-49be-823b-f9abb713a3e1) + +## 0.24.2 (2025-03-22) + +### Fixes + +#### Make lists of models and enums work correctly in custom templates + +Lists of model and enum classes should be available to custom templates via the Jinja +variables `openapi.models` and `openapi.enums`, but these were being passed in a way that made +them always appear empty. This has been fixed so a custom template can now iterate over them. + +Closes #1188. + ## 0.24.1 (2025-03-15) ### Features diff --git a/README.md b/README.md index b07e7b29b..4a886d299 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ In the config file, there's an easy way to tell `openapi-python-client` to run a ```yaml post_hooks: - - "ruff check . --fix" + - "ruff check . --fix-only" - "ruff format ." ``` @@ -192,6 +192,39 @@ content_type_overrides: application/zip: application/octet-stream ``` +## Supported Extensions + +### x-enum-varnames + +This extension has been adopted by similar projects such as [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator/pull/917). +It is intended to provide user-friendly names for integer Enum members that get generated. +It is critical that the length of the array matches that of the enum values. + +``` +"Colors": { + "type": "integer", + "format": "int32", + "enum": [ + 0, + 1, + 2 + ], + "x-enum-varnames": [ + "Red", + "Green", + "Blue" + ] +} +``` + +Results in: +``` +class Color(IntEnum): + RED = 0 + GREEN = 1 + BLUE = 2 +``` + [changelog.md]: CHANGELOG.md [poetry]: https://python-poetry.org/ [PDM]: https://pdm-project.org/latest/ 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 525f8baf2..6f4a9f2d2 100644 --- a/end_to_end_tests/__snapshots__/test_end_to_end.ambr +++ b/end_to_end_tests/__snapshots__/test_end_to_end.ambr @@ -2,11 +2,11 @@ # 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(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 + Invalid response status code pattern: abcdef, 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 @@ -16,8 +16,8 @@ # name: test_documents_with_errors[circular-body-ref] ''' Generating /test-documents-with-errors - Warning(s) encountered while generating. Client was generated, but some pieces may be missing + Warning(s) encountered while generating. Client was generated, but some pieces may be missing WARNING parsing POST / within default. Endpoint will not be generated. Circular $ref in request body @@ -30,8 +30,8 @@ # name: test_documents_with_errors[invalid-uuid-defaults] ''' Generating /test-documents-with-errors - Warning(s) encountered while generating. Client was generated, but some pieces may be missing + Warning(s) encountered while generating. Client was generated, but some pieces may be missing WARNING parsing PUT / within default. Endpoint will not be generated. cannot parse parameter of endpoint put_: Invalid UUID value: 3 @@ -49,8 +49,8 @@ # name: test_documents_with_errors[missing-body-ref] ''' Generating /test-documents-with-errors - Warning(s) encountered while generating. Client was generated, but some pieces may be missing + Warning(s) encountered while generating. Client was generated, but some pieces may be missing WARNING parsing POST / within default. Endpoint will not be generated. Could not resolve $ref #/components/requestBodies/body in request body @@ -63,8 +63,8 @@ # name: test_documents_with_errors[optional-path-param] ''' Generating /test-documents-with-errors - Warning(s) encountered while generating. Client was generated, but some pieces may be missing + Warning(s) encountered while generating. Client was generated, but some pieces may be missing WARNING parsing GET /{optional} within default. Endpoint will not be generated. Path parameter must be required diff --git a/end_to_end_tests/baseline_openapi_3.0.json b/end_to_end_tests/baseline_openapi_3.0.json index eec3e39ac..c47048218 100644 --- a/end_to_end_tests/baseline_openapi_3.0.json +++ b/end_to_end_tests/baseline_openapi_3.0.json @@ -104,6 +104,30 @@ } } }, + "/bodies/optional": { + "post": { + "tags": [ + "bodies" + ], + "description": "Test optional request body", + "operationId": "optional-body", + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/tests/": { "get": { "tags": [ @@ -402,53 +426,7 @@ "some_nullable_object", "some_required_number" ], - "properties": { - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {} - } - } - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - } - } - } - } - }, - "/tests/upload/multiple": { - "post": { - "tags": [ - "tests" - ], - "summary": "Upload multiple files", - "description": "Upload several files in the same request", - "operationId": "upload_multiple_files_tests_upload_post", - "parameters": [], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "binary" - } + "properties": {} } } }, @@ -776,7 +754,13 @@ "content": { "application/json": { "schema": { - "type": "string" + "type": "object", + "properties": { + "data": { + "type": "string", + "format": "binary" + } + } } } } @@ -1014,6 +998,124 @@ } } }, + "/responses/status-codes/default": { + "get": { + "tags": [ + "responses" + ], + "summary": "Default Status Code Only", + "operationId": "default_status_code", + "responses": { + "default": { + "description": "Default response", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/response/status-codes/patterns": { + "get": { + "tags": [ + "responses" + ], + "summary": "Status Code Patterns", + "operationId": "status_code_patterns", + "responses": { + "2XX": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "success", + "failure" + ] + } + } + } + } + } + }, + "4XX": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/response/status-codes/precedence": { + "get": { + "operationId": "status_code_precedence", + "tags": [ + "responses" + ], + "summary": "Status Codes Precedence", + "description": "Verify that specific status codes are always checked first, then ranges, then default", + "responses": { + "default": { + "description": "Default Response Should Be Last", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "4XX": { + "description": "Pattern should be after specific codes", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/auth/token_with_cookie": { "get": { "tags": [ @@ -1172,7 +1274,10 @@ }, "/tag_with_number": { "get": { - "tags": ["1", "2"], + "tags": [ + "1", + "2" + ], "responses": { "200": { "description": "Success" @@ -1707,7 +1812,9 @@ "type": "string" } }, - "required": ["type"] + "required": [ + "type" + ] }, { "type": "object", @@ -1719,7 +1826,50 @@ "type": "string" } }, - "required": ["type"] + "required": [ + "type" + ] + } + ] + } + } + } + } + } + } + }, + "/types/unions/duplicate-types": { + "post": { + "title": "duplicate union members", + "requestBody": { + "description": "The request body", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/AModel" + }, + { + "$ref": "#/components/schemas/AModel" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/AModel" + }, + { + "$ref": "#/components/schemas/AModel" } ] } @@ -1768,6 +1918,30 @@ } ] }, + "AllOfRequiredBase": { + "type": "object", + "properties": { + "bar": { + "type": "string", + "description": "The bar property" + }, + "baz": { + "type": "string", + "description": "The baz property" + } + } + }, + "AllOfRequiredDerived": { + "allOf": [ + { + "$ref": "#/components/schemas/AllOfRequiredBase" + }, + { + "type": "object", + "required": ["bar"] + } + ] + }, "AModel": { "title": "AModel", "required": [ @@ -2210,6 +2384,7 @@ "oneOf": [ { "type": "object", + "title": "Apples", "properties": { "apples": { "type": "string" @@ -2218,6 +2393,7 @@ }, { "type": "object", + "title": "Bananas", "properties": { "bananas": { "type": "string" diff --git a/end_to_end_tests/baseline_openapi_3.1.yaml b/end_to_end_tests/baseline_openapi_3.1.yaml index caddda8eb..295e6818a 100644 --- a/end_to_end_tests/baseline_openapi_3.1.yaml +++ b/end_to_end_tests/baseline_openapi_3.1.yaml @@ -100,6 +100,30 @@ info: } } }, + "/bodies/optional": { + "post": { + "tags": [ + "bodies" + ], + "description": "Test optional request body", + "operationId": "optional-body", + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/tests/": { "get": { "tags": [ @@ -418,51 +442,6 @@ info: } } }, - "/tests/upload/multiple": { - "post": { - "tags": [ - "tests" - ], - "summary": "Upload multiple files", - "description": "Upload several files in the same request", - "operationId": "upload_multiple_files_tests_upload_post", - "parameters": [ ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "binary" - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { } - } - } - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - } - } - } - } - }, "/tests/json_body": { "post": { "tags": [ @@ -759,7 +738,13 @@ info: "content": { "application/json": { "schema": { - "type": "string" + "type": "object", + "properties": { + "data": { + "type": "string", + "format": "binary" + } + } } } } @@ -997,6 +982,115 @@ info: } } }, + "/responses/status-codes/default": { + "get": { + "tags": ["responses"], + "summary": "Default Status Code Only", + "operationId": "default_status_code", + "responses": { + "default": { + "description": "Default response", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/response/status-codes/patterns": { + "get": { + "tags": ["responses"], + "summary": "Status Code Patterns", + "operationId": "status_code_patterns", + "responses": { + "2XX": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": ["success", "failure"] + } + } + } + } + } + }, + "4XX": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/response/status-codes/precedence": { + "get": { + "operationId": "status_code_precedence", + "tags": ["responses"], + "summary": "Status Codes Precedence", + "description": "Verify that specific status codes are always checked first, then ranges, then default", + "responses": { + "default": { + "description": "Default Response Should Be Last", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "4XX": { + "description": "Pattern should be after specific codes", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/auth/token_with_cookie": { "get": { "tags": [ @@ -1155,7 +1249,7 @@ info: }, "/tag_with_number": { "get": { - "tags": ["1", "2"], + "tags": [ "1", "2" ], "responses": { "200": { "description": "Success" @@ -1688,7 +1782,7 @@ info: "type": "string" } }, - "required": ["type"] + "required": [ "type" ] }, { "type": "object", @@ -1700,7 +1794,7 @@ info: "type": "string" } }, - "required": ["type"] + "required": [ "type" ] } ] } @@ -1709,6 +1803,39 @@ info: } } } + }, + "/types/unions/duplicate-types": { + "post": { + "title": "duplicate union members", + "requestBody": { + "description": "The request body", + "content": { + "application/json": { + "schema": { + "oneOf": [ + {"$ref": "#/components/schemas/AModel"}, + {"$ref": "#/components/schemas/AModel"} + ] + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + {"$ref": "#/components/schemas/AModel"}, + {"$ref": "#/components/schemas/AModel"} + ] + } + } + } + } + } + } } } "components": @@ -1744,6 +1871,27 @@ info: } ] }, + "AllOfRequiredBase": { + "type": "object", + "properties": { + "bar": { + "type": "string", + "description": "The bar property" + }, + "baz": { + "type": "string", + "description": "The baz property" + } + } + }, + "AllOfRequiredDerived": { + "allOf": [ + { "$ref": "#/components/schemas/AllOfRequiredBase" }, + { "type": "object", + "required": ["bar"] + } + ] + }, "AModel": { "title": "AModel", "required": [ @@ -2196,6 +2344,7 @@ info: "oneOf": [ { "type": "object", + "title": "Apples", "properties": { "apples": { "type": "string" @@ -2204,6 +2353,7 @@ info: }, { "type": "object", + "title": "Bananas", "properties": { "bananas": { "type": "string" diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/bodies/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/bodies/__init__.py index 89304dde0..5ff7fceb8 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/bodies/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/bodies/__init__.py @@ -2,7 +2,7 @@ import types -from . import json_like, post_bodies_multiple, refs +from . import json_like, optional_body, post_bodies_multiple, refs class BodiesEndpoints: @@ -26,3 +26,10 @@ def refs(cls) -> types.ModuleType: Test request body defined via ref """ return refs + + @classmethod + def optional_body(cls) -> types.ModuleType: + """ + Test optional request body + """ + return optional_body diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py index 0d7798e15..14b47083b 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py @@ -7,6 +7,7 @@ get_models_allof, get_models_oneof_with_required_const, post_common_parameters, + post_types_unions_duplicate_types, reserved_parameters, ) @@ -31,3 +32,7 @@ def get_models_allof(cls) -> types.ModuleType: @classmethod def get_models_oneof_with_required_const(cls) -> types.ModuleType: return get_models_oneof_with_required_const + + @classmethod + def post_types_unions_duplicate_types(cls) -> types.ModuleType: + return post_types_unions_duplicate_types 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 e09dee3e3..d1361e311 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,14 @@ import types -from . import post_responses_unions_simple_before_complex, reference_response, text_response +from . import ( + default_status_code, + post_responses_unions_simple_before_complex, + reference_response, + status_code_patterns, + status_code_precedence, + text_response, +) class ResponsesEndpoints: @@ -26,3 +33,24 @@ def reference_response(cls) -> types.ModuleType: Endpoint using predefined response """ return reference_response + + @classmethod + def default_status_code(cls) -> types.ModuleType: + """ + Default Status Code Only + """ + return default_status_code + + @classmethod + def status_code_patterns(cls) -> types.ModuleType: + """ + Status Code Patterns + """ + return status_code_patterns + + @classmethod + def status_code_precedence(cls) -> types.ModuleType: + """ + Verify that specific status codes are always checked first, then ranges, then default + """ + return status_code_precedence diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py index 1b91acc98..d7ef5cd7c 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py @@ -21,7 +21,6 @@ token_with_cookie_auth_token_with_cookie_get, unsupported_content_tests_unsupported_content_get, upload_file_tests_upload_post, - upload_multiple_files_tests_upload_post, ) @@ -82,13 +81,6 @@ def upload_file_tests_upload_post(cls) -> types.ModuleType: """ return upload_file_tests_upload_post - @classmethod - def upload_multiple_files_tests_upload_post(cls) -> types.ModuleType: - """ - Upload several files in the same request - """ - return upload_multiple_files_tests_upload_post - @classmethod def json_body_tests_json_body_post(cls) -> types.ModuleType: """ diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/models/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/models/__init__.py new file mode 100644 index 000000000..611ed15f6 --- /dev/null +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/models/__init__.py @@ -0,0 +1,16 @@ +# Testing that we can access model-related information via Jinja variables. + +# To avoid having to update this file in the golden record every time the test specs are changed, +# we won't include all the classes in this output - we'll just look for one of them. + +# Using "alls" +# AModel + +# Using "imports" +# from .a_model import AModel + +# Using "openapi.models" +# AModel (a_model) + +# Using "openapi.enums" +# AnEnum (an_enum) diff --git a/end_to_end_tests/docstrings-on-attributes-golden-record/README.md b/end_to_end_tests/docstrings-on-attributes-golden-record/README.md index 79b20f411..32f776550 100644 --- a/end_to_end_tests/docstrings-on-attributes-golden-record/README.md +++ b/end_to_end_tests/docstrings-on-attributes-golden-record/README.md @@ -47,7 +47,7 @@ By default, when you're calling an HTTPS API it will attempt to verify that SSL ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -57,7 +57,7 @@ You can also disable certificate validation altogether, but beware that **this i ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl=False ) diff --git a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/client.py b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/client.py index e05334a5f..a11b85c36 100644 --- a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/client.py +++ b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/client.py @@ -1,5 +1,5 @@ import ssl -from typing import Any, Optional, Union +from typing import Any, Self import httpx from attrs import define, evolve, field @@ -34,12 +34,12 @@ class Client: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" @@ -58,14 +58,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "Client": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "Client": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "Client": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -87,7 +87,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "Client": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -96,8 +96,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -118,7 +118,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "Client": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self @@ -157,12 +157,12 @@ class AuthenticatedClient: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) token: str """The token to use for authentication""" @@ -188,14 +188,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -218,7 +218,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "AuthenticatedClient": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -227,8 +227,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -250,7 +250,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "AuthenticatedClient": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self diff --git a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_description.py b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_description.py index 19e90ca7d..b0be6a72c 100644 --- a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_description.py +++ b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_description.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class ModelWithDescription: """This is a nice model.""" - prop_with_no_desc: Union[Unset, str] = UNSET - prop_with_desc: Union[Unset, str] = UNSET + prop_with_no_desc: str | Unset = UNSET + prop_with_desc: str | Unset = UNSET """ This is a nice property. """ - prop_with_long_desc: Union[Unset, str] = UNSET + prop_with_long_desc: str | Unset = UNSET """ It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of light, it was the season of diff --git a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_no_description.py b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_no_description.py index 2af1dcb4f..0a020f657 100644 --- a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_no_description.py +++ b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/models/model_with_no_description.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -11,8 +13,8 @@ @_attrs_define class ModelWithNoDescription: - prop_with_no_desc: Union[Unset, str] = UNSET - prop_with_desc: Union[Unset, str] = UNSET + prop_with_no_desc: str | Unset = UNSET + prop_with_desc: str | Unset = UNSET """ This is a nice property. """ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) diff --git a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/types.py b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/types.py index b9ed58b8a..b64af0952 100644 --- a/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/types.py +++ b/end_to_end_tests/docstrings-on-attributes-golden-record/my_test_api_client/types.py @@ -1,8 +1,8 @@ """Contains some shared types for properties""" -from collections.abc import MutableMapping +from collections.abc import Mapping, MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, Optional, TypeVar +from typing import IO, BinaryIO, Generic, Literal, TypeVar from attrs import define @@ -14,7 +14,15 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] +# The types that `httpx.Client(files=)` can accept, copied from that library. +FileContent = IO[bytes] | bytes | str +FileTypes = ( + # (filename, file (or bytes), content_type) + tuple[str | None, FileContent, str | None] + # (filename, file (or bytes), content_type, headers) + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) +RequestFiles = list[tuple[str, FileTypes]] @define @@ -22,10 +30,10 @@ class File: """Contains information for file uploads""" payload: BinaryIO - file_name: Optional[str] = None - mime_type: Optional[str] = None + file_name: str | None = None + mime_type: str | None = None - def to_tuple(self) -> FileJsonType: + def to_tuple(self) -> FileTypes: """Return a tuple representation that httpx will accept for multipart/form-data""" return self.file_name, self.payload, self.mime_type @@ -40,7 +48,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T | None -__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] +__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"] diff --git a/end_to_end_tests/docstrings-on-attributes-golden-record/pyproject.toml b/end_to_end_tests/docstrings-on-attributes-golden-record/pyproject.toml index feca06dbd..f2498ce1a 100644 --- a/end_to_end_tests/docstrings-on-attributes-golden-record/pyproject.toml +++ b/end_to_end_tests/docstrings-on-attributes-golden-record/pyproject.toml @@ -5,19 +5,17 @@ description = "A client library for accessing My Test API" authors = [] readme = "README.md" packages = [ - {include = "my_test_api_client"}, + { include = "my_test_api_client" }, ] -include = ["CHANGELOG.md", "my_test_api_client/py.typed"] - +include = ["my_test_api_client/py.typed"] [tool.poetry.dependencies] -python = "^3.9" -httpx = ">=0.20.0,<0.29.0" +python = "^3.11" +httpx = ">=0.23.1,<0.29.0" attrs = ">=22.2.0" -python-dateutil = "^2.8.0" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" [tool.ruff] 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 index 443d764c5..aaf33d906 100644 --- 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 @@ -1,5 +1,3 @@ -from typing import Any, ForwardRef, Union - from end_to_end_tests.functional_tests.helpers import ( assert_model_decode_encode, assert_model_property_type_hint, @@ -37,7 +35,6 @@ ".models.ModelWithArrayOfInts", ".models.ModelWithArrayOfObjects", ".models.SimpleObject", - ".types.Unset", ) class TestArraySchemas: def test_array_of_any(self, ModelWithArrayOfAny): @@ -53,9 +50,6 @@ def test_array_of_int(self, 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( @@ -64,10 +58,10 @@ def test_array_of_object(self, ModelWithArrayOfObjects, SimpleObject): 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]) + def test_type_hints(self, ModelWithArrayOfAny, ModelWithArrayOfInts, ModelWithArrayOfObjects): + assert_model_property_type_hint(ModelWithArrayOfAny, "array_prop", "list[Any] | Unset") + assert_model_property_type_hint(ModelWithArrayOfInts, "array_prop", "list[int] | Unset") + assert_model_property_type_hint(ModelWithArrayOfObjects, "array_prop", "list[SimpleObject] | Unset") @with_generated_client_fixture( @@ -108,7 +102,6 @@ def test_type_hints(self, ModelWithArrayOfAny, ModelWithArrayOfInts, ModelWithAr ".models.ModelWithPrefixItems", ".models.ModelWithMixedItems", ".models.SimpleObject", - ".types.Unset", ) class TestArraysWithPrefixItems: def test_single_prefix_item(self, ModelWithSinglePrefixItem): @@ -132,17 +125,17 @@ def test_prefix_items_and_regular_items(self, ModelWithMixedItems, SimpleObject) 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]) + def test_type_hints(self, ModelWithSinglePrefixItem, ModelWithPrefixItems, ModelWithMixedItems): + assert_model_property_type_hint(ModelWithSinglePrefixItem, "array_prop", "list[str] | Unset") assert_model_property_type_hint( ModelWithPrefixItems, "array_prop", - Union[list[Union[ForwardRef("SimpleObject"), str]], Unset], + "list[SimpleObject | str] | Unset", ) assert_model_property_type_hint( ModelWithMixedItems, "array_prop", - Union[list[Union[ForwardRef("SimpleObject"), str]], Unset], + "list[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 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 index 5f8affb25..1af1a5af4 100644 --- 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 @@ -51,7 +51,7 @@ def test_defaults_in_initializer(self, MyModel): 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), + date_time_prop=datetime.datetime(2024, 1, 2, 3, 4, 5, tzinfo=datetime.UTC), uuid_prop=uuid.UUID("07EF8B4D-AA09-4FFA-898D-C710796AFF41"), any_prop_with_string="b", any_prop_with_int=3, 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 index d2d560780..4ed00ac4a 100644 --- 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 @@ -43,7 +43,7 @@ def test_model_description(self, MyModel): def test_model_properties(self, MyModel): assert set(DocstringParser(MyModel).get_section("Attributes:")) == { "req_str (str): This is necessary.", - "opt_str (Union[Unset, str]): This isn't necessary.", + "opt_str (str | Unset): This isn't necessary.", "undescribed_prop (str):", } @@ -146,18 +146,18 @@ def test_response_single_type(self, get_simple_thing_sync): 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]", + "GoodResponse | ErrorResponse", + "ErrorResponse | GoodResponse", ) def test_request_body(self, post_simple_thing_sync): assert DocstringParser(post_simple_thing_sync).get_section("Args:") == [ - "body (Thing): The thing." + "body (Thing | Unset): 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?", + "fries (bool | Unset): 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 index 89dbef7dc..eb4e38ebc 100644 --- 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 @@ -1,4 +1,4 @@ -from typing import Literal, Union +from typing import Literal import pytest from end_to_end_tests.functional_tests.helpers import ( @@ -32,7 +32,6 @@ ".models.MyModel", ".models.MyModelInlineEnumProp", ".models.MyModelWithRequired", - ".types.Unset", ) class TestStringEnumClass: @pytest.mark.parametrize( @@ -58,10 +57,10 @@ def test_enum_prop_in_object(self, MyEnum, MyModel, MyModelInlineEnumProp): MyModel(inline_enum_prop=MyModelInlineEnumProp.A), ) - def test_type_hints(self, MyModel, MyModelWithRequired, MyEnum, Unset): - optional_type = Union[Unset, MyEnum] + def test_type_hints(self, MyModel, MyModelWithRequired): + optional_type = "MyEnum | Unset" assert_model_property_type_hint(MyModel,"enum_prop", optional_type) - assert_model_property_type_hint(MyModelWithRequired, "enum_prop", MyEnum) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", "MyEnum") def test_invalid_values(self, MyModel): with pytest.raises(ValueError): @@ -72,6 +71,35 @@ def test_invalid_values(self, MyModel): MyModel.from_dict({"enumProp": 2}) +@with_generated_client_fixture( +""" +components: + schemas: + MyStrEnum: + type: string + enum: ["a", "b", "c"] + x-enum-varnames: [ + "One", + "More than OnE", + "not_quite_four" + ] +""") +@with_generated_code_imports( + ".models.MyStrEnum", +) +class TestStrEnumVarNameExtensions: + @pytest.mark.parametrize( + "expected_name,expected_value", + [ + ("ONE", "a"), + ("MORE_THAN_ON_E", "b"), + ("NOT_QUITE_FOUR", "c"), + ], + ) + def test_enum_values(self, MyStrEnum, expected_name, expected_value): + assert getattr(MyStrEnum, expected_name) == MyStrEnum(expected_value) + + @with_generated_client_fixture( """ components: @@ -95,7 +123,6 @@ def test_invalid_values(self, MyModel): ".models.MyModel", ".models.MyModelInlineEnumProp", ".models.MyModelWithRequired", - ".types.Unset", ) class TestIntEnumClass: @pytest.mark.parametrize( @@ -117,10 +144,10 @@ def test_enum_prop_in_object(self, MyEnum, MyModel, MyModelInlineEnumProp): MyModel(inline_enum_prop=MyModelInlineEnumProp.VALUE_2), ) - def test_type_hints(self, MyModel, MyModelWithRequired, MyEnum, Unset): - optional_type = Union[Unset, MyEnum] + def test_type_hints(self, MyModel, MyModelWithRequired): + optional_type = "MyEnum | Unset" assert_model_property_type_hint(MyModel,"enum_prop", optional_type) - assert_model_property_type_hint(MyModelWithRequired, "enum_prop", MyEnum) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", "MyEnum") def test_invalid_values(self, MyModel): with pytest.raises(ValueError): @@ -129,6 +156,35 @@ def test_invalid_values(self, MyModel): MyModel.from_dict({"enumProp": "a"}) +@with_generated_client_fixture( +""" +components: + schemas: + MyEnum: + type: integer + enum: [2, 3, -4] + x-enum-varnames: [ + "Two", + "Three", + "Negative Four" + ] +""") +@with_generated_code_imports( + ".models.MyEnum", +) +class TestIntEnumVarNameExtensions: + @pytest.mark.parametrize( + "expected_name,expected_value", + [ + ("TWO", 2), + ("THREE", 3), + ("NEGATIVE_FOUR", -4), + ], + ) + def test_enum_values(self, MyEnum, expected_name, expected_value): + assert getattr(MyEnum, expected_name) == MyEnum(expected_value) + + @with_generated_client_fixture( """ components: @@ -154,7 +210,6 @@ def test_invalid_values(self, MyModel): ".models.MyEnum", ".models.MyEnumIncludingNullType1", # see comment in test_nullable_enum_prop ".models.MyModel", - ".types.Unset", ) class TestNullableEnums: def test_nullable_enum_prop(self, MyModel, MyEnum, MyEnumIncludingNullType1): @@ -170,8 +225,8 @@ def test_nullable_enum_prop(self, MyModel, MyEnum, MyEnumIncludingNullType1): 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)) - def test_type_hints(self, MyModel, MyEnum, Unset): - expected_type = Union[MyEnum, None, Unset] + def test_type_hints(self, MyModel): + expected_type = "MyEnum | None | Unset" assert_model_property_type_hint(MyModel, "nullable_enum_prop", expected_type) @@ -238,7 +293,7 @@ def test_invalid_int(self, MyModel): @with_generated_code_imports( ".models.MyModel", ".models.MyModelWithRequired", - ".types.Unset", + ".models.MyEnum", ) class TestStringLiteralEnum: def test_enum_prop(self, MyModel): @@ -246,11 +301,11 @@ def test_enum_prop(self, MyModel): 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] + def test_type_hints(self, MyModel, MyModelWithRequired, MyEnum): + optional_type = "MyEnum | Unset" assert_model_property_type_hint(MyModel, "enum_prop", optional_type) - assert_model_property_type_hint(MyModelWithRequired, "enum_prop", literal_type) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", "MyEnum") + assert MyEnum == Literal["a", "A", "b"] def test_invalid_values(self, MyModel): with pytest.raises(TypeError): @@ -282,7 +337,7 @@ def test_invalid_values(self, MyModel): @with_generated_code_imports( ".models.MyModel", ".models.MyModelWithRequired", - ".types.Unset", + ".models.MyEnum", ) class TestIntLiteralEnum: def test_enum_prop(self, MyModel): @@ -290,11 +345,11 @@ def test_enum_prop(self, MyModel): 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] + def test_type_hints(self, MyModel, MyModelWithRequired, MyEnum): + optional_type = "MyEnum | Unset" assert_model_property_type_hint(MyModel, "enum_prop", optional_type) - assert_model_property_type_hint(MyModelWithRequired, "enum_prop", literal_type) + assert_model_property_type_hint(MyModelWithRequired, "enum_prop", "MyEnum") + assert MyEnum == Literal[2, 3, -4] def test_invalid_values(self, MyModel): with pytest.raises(TypeError): diff --git a/end_to_end_tests/functional_tests/generated_code_execution/test_path_parameters.py b/end_to_end_tests/functional_tests/generated_code_execution/test_path_parameters.py new file mode 100644 index 000000000..694788d96 --- /dev/null +++ b/end_to_end_tests/functional_tests/generated_code_execution/test_path_parameters.py @@ -0,0 +1,151 @@ +from unittest.mock import MagicMock + +import httpx +import pytest + +from end_to_end_tests.functional_tests.helpers import ( + with_generated_client_fixture, + with_generated_code_import, +) + + +@with_generated_client_fixture( +""" +paths: + "/items/{item_id}/details/{detail_id}": + get: + operationId: getItemDetail + parameters: + - name: item_id + in: path + required: true + schema: + type: string + - name: detail_id + in: path + required: true + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + type: object + properties: + id: + type: string +""") +@with_generated_code_import(".api.default.get_item_detail.sync_detailed") +@with_generated_code_import(".client.Client") +class TestPathParameterEncoding: + """Test that path parameters are properly URL-encoded""" + + def test_path_params_with_normal_chars_work(self, sync_detailed, Client): + """Test that normal alphanumeric path parameters still work correctly""" + mock_httpx_client = MagicMock(spec=httpx.Client) + mock_response = MagicMock(spec=httpx.Response) + mock_response.status_code = 200 + mock_response.json.return_value = {"id": "test"} + mock_response.content = b'{"id": "test"}' + mock_response.headers = {} + mock_httpx_client.request.return_value = mock_response + + client = Client(base_url="https://api.example.com") + client.set_httpx_client(mock_httpx_client) + + sync_detailed( + item_id="item123", + detail_id="detail456", + client=client, + ) + + mock_httpx_client.request.assert_called_once() + call_kwargs = mock_httpx_client.request.call_args[1] + + # Normal characters should remain unchanged + expected_url = "/items/item123/details/detail456" + assert call_kwargs["url"] == expected_url + + def test_path_params_with_reserved_chars_are_encoded(self, sync_detailed, Client): + """Test that path parameters with reserved characters are properly URL-encoded""" + # Create a mock httpx client + mock_httpx_client = MagicMock(spec=httpx.Client) + mock_response = MagicMock(spec=httpx.Response) + mock_response.status_code = 200 + mock_response.json.return_value = {"id": "test"} + mock_response.content = b'{"id": "test"}' + mock_response.headers = {} + mock_httpx_client.request.return_value = mock_response + + # Create a client with the mock httpx client + client = Client(base_url="https://api.example.com") + client.set_httpx_client(mock_httpx_client) + + # Call the endpoint with path parameters containing reserved characters + sync_detailed( + item_id="item/with/slashes", + detail_id="detail?with=query&chars", + client=client, + ) + + # Verify the request was made with properly encoded URL + mock_httpx_client.request.assert_called_once() + call_kwargs = mock_httpx_client.request.call_args[1] + + # The URL should have encoded slashes and query characters + expected_url = "/items/item%2Fwith%2Fslashes/details/detail%3Fwith%3Dquery%26chars" + assert call_kwargs["url"] == expected_url + + def test_path_params_with_spaces_are_encoded(self, sync_detailed, Client): + """Test that path parameters with spaces are properly URL-encoded""" + mock_httpx_client = MagicMock(spec=httpx.Client) + mock_response = MagicMock(spec=httpx.Response) + mock_response.status_code = 200 + mock_response.json.return_value = {"id": "test"} + mock_response.content = b'{"id": "test"}' + mock_response.headers = {} + mock_httpx_client.request.return_value = mock_response + + client = Client(base_url="https://api.example.com") + client.set_httpx_client(mock_httpx_client) + + sync_detailed( + item_id="item with spaces", + detail_id="detail with spaces", + client=client, + ) + + mock_httpx_client.request.assert_called_once() + call_kwargs = mock_httpx_client.request.call_args[1] + + # Spaces should be encoded as %20 + expected_url = "/items/item%20with%20spaces/details/detail%20with%20spaces" + assert call_kwargs["url"] == expected_url + + def test_path_params_with_hash_are_encoded(self, sync_detailed, Client): + """Test that path parameters with hash/fragment characters are properly URL-encoded""" + mock_httpx_client = MagicMock(spec=httpx.Client) + mock_response = MagicMock(spec=httpx.Response) + mock_response.status_code = 200 + mock_response.json.return_value = {"id": "test"} + mock_response.content = b'{"id": "test"}' + mock_response.headers = {} + mock_httpx_client.request.return_value = mock_response + + client = Client(base_url="https://api.example.com") + client.set_httpx_client(mock_httpx_client) + + sync_detailed( + item_id="item#1", + detail_id="detail#id", + client=client, + ) + + mock_httpx_client.request.assert_called_once() + call_kwargs = mock_httpx_client.request.call_args[1] + + # Hash should be encoded as %23 + expected_url = "/items/item%231/details/detail%23id" + assert call_kwargs["url"] == expected_url 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 index e1cfce9a5..c1e0e9e56 100644 --- 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 @@ -1,5 +1,4 @@ import datetime -from typing import Any, ForwardRef, Union import uuid import pytest @@ -12,7 +11,7 @@ @with_generated_client_fixture( -""" + """ components: schemas: MyModel: @@ -29,7 +28,8 @@ properties: req3: {"type": "string"} required: ["req3"] -""") +""" +) @with_generated_code_imports( ".models.MyModel", ".models.DerivedModel", @@ -68,13 +68,13 @@ def test_required_missing(self, MyModel, DerivedModel): 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]) + def test_type_hints(self, MyModel): + assert_model_property_type_hint(MyModel, "req1", "str") + assert_model_property_type_hint(MyModel, "opt", "str | Unset") @with_generated_client_fixture( -""" + """ components: schemas: MyModel: @@ -89,11 +89,11 @@ def test_type_hints(self, MyModel, Unset): anyProp: {} AnyObject: type: object -""") +""" +) @with_generated_code_imports( ".models.MyModel", ".models.AnyObject", - ".types.Unset", ) class TestBasicModelProperties: def test_decode_encode(self, MyModel, AnyObject): @@ -104,7 +104,7 @@ def test_decode_encode(self, MyModel, AnyObject): "intProp": 2, "anyObjectProp": {"d": 3}, "nullProp": None, - "anyProp": "e" + "anyProp": "e", } expected_any_object = AnyObject() expected_any_object.additional_properties = {"d": 3} @@ -116,10 +116,10 @@ def test_decode_encode(self, MyModel, AnyObject): string_prop="a", number_prop=1.5, int_prop=2, - any_object_prop = expected_any_object, + any_object_prop=expected_any_object, null_prop=None, any_prop="e", - ) + ), ) @pytest.mark.parametrize( @@ -133,18 +133,18 @@ def test_decode_error_not_object(self, bad_data, MyModel): # 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]) + def test_type_hints(self, MyModel): + assert_model_property_type_hint(MyModel, "boolean_prop", "bool | Unset") + assert_model_property_type_hint(MyModel, "string_prop", "str | Unset") + assert_model_property_type_hint(MyModel, "number_prop", "float | Unset") + assert_model_property_type_hint(MyModel, "int_prop", "int | Unset") + assert_model_property_type_hint(MyModel, "any_object_prop", "AnyObject | Unset") + assert_model_property_type_hint(MyModel, "null_prop", "None | Unset") + assert_model_property_type_hint(MyModel, "any_prop", "Any | Unset") @with_generated_client_fixture( -""" + """ components: schemas: MyModel: @@ -154,10 +154,10 @@ def test_type_hints(self, MyModel, Unset): 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): @@ -166,7 +166,7 @@ def test_date(self, MyModel): 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) + date_time_value = datetime.datetime.now(datetime.UTC) json_data = {"dateTimeProp": date_time_value.isoformat()} assert_model_decode_encode(MyModel, json_data, MyModel(date_time_prop=date_time_value)) @@ -179,8 +179,64 @@ 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]) + def test_type_hints(self, MyModel): + assert_model_property_type_hint(MyModel, "date_prop", "datetime.date | Unset") + assert_model_property_type_hint(MyModel, "date_time_prop", "datetime.datetime | Unset") + assert_model_property_type_hint(MyModel, "uuid_prop", "UUID | Unset") + assert_model_property_type_hint(MyModel, "unknown_format_prop", "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: + $ref: "#/components/schemas/OtherObject" + OtherObject: + $ref: "#/components/schemas/AnotherObject" + AnotherObject: + type: object + properties: + booleanProp: {"type": "boolean"} + +""" +) +@with_generated_code_imports( + ".models.MyModel", + ".models.AnyObject", + ".types.Unset", +) +class TestReferenceSchemaProperties: + def test_decode_encode(self, MyModel, AnyObject): + json_data = { + "booleanProp": True, + "stringProp": "a", + "numberProp": 1.5, + "intProp": 2, + "anyObjectProp": {"booleanProp": False}, + "nullProp": None, + "anyProp": "e", + } + assert_model_decode_encode( + MyModel, + json_data, + MyModel( + boolean_prop=True, + string_prop="a", + number_prop=1.5, + int_prop=2, + any_object_prop=AnyObject(boolean_prop=False), + null_prop=None, + any_prop="e", + ), + ) 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 index 9a9b49e4c..27dc7430e 100644 --- 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 @@ -1,5 +1,3 @@ -from typing import ForwardRef, Union - from end_to_end_tests.functional_tests.helpers import ( assert_model_decode_encode, assert_model_property_type_hint, @@ -23,15 +21,14 @@ ) @with_generated_code_imports( ".models.MyModel", - ".types.Unset" ) class TestSimpleTypeList: 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]) + def test_type_hints(self, MyModel): + assert_model_property_type_hint(MyModel, "string_or_int_prop", "int | str | Unset") @with_generated_client_fixture( @@ -92,7 +89,6 @@ def test_type_hints(self, MyModel, Unset): ".models.ModelWithRequiredUnion", ".models.ModelWithNestedUnion", ".models.ModelWithUnionOfOne", - ".types.Unset" ) class TestOneOf: def test_disambiguate_objects_via_required_properties(self, ThingA, ThingB, ModelWithUnion): @@ -131,19 +127,19 @@ def test_disambiguate_nested_union(self, ThingA, ThingB, ModelWithNestedUnion): ModelWithNestedUnion(thing_or_value=3), ) - def test_type_hints(self, ModelWithUnion, ModelWithRequiredUnion, ModelWithUnionOfOne, ThingA, Unset): + def test_type_hints(self, ModelWithUnion, ModelWithRequiredUnion, ModelWithUnionOfOne, ThingA): assert_model_property_type_hint( ModelWithUnion, "thing", - Union[ForwardRef("ThingA"), ForwardRef("ThingB"), Unset], + "ThingA | ThingB | Unset", ) assert_model_property_type_hint( ModelWithRequiredUnion, "thing", - Union[ForwardRef("ThingA"), ForwardRef("ThingB")], + "ThingA | ThingB", ) assert_model_property_type_hint( - ModelWithUnionOfOne, "thing", Union[ForwardRef("ThingA"), Unset] + ModelWithUnionOfOne, "thing", "ThingA | Unset" ) assert_model_property_type_hint( ModelWithUnionOfOne, "required_thing", "ThingA" diff --git a/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_references.py b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_references.py new file mode 100644 index 000000000..9f609eef2 --- /dev/null +++ b/end_to_end_tests/functional_tests/generator_failure_cases/test_invalid_references.py @@ -0,0 +1,29 @@ +import pytest + +from end_to_end_tests.functional_tests.helpers import assert_bad_schema, with_generated_client_fixture + + +@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: + $ref: "#/components/schemas/OtherObject" + OtherObject: + $ref: "#/components/schemas/AnyObject" + +""" +) +class TestReferenceSchemaProperties: + def test_decode_encode(self, generated_client): + assert "Circular schema references found" in generated_client.generator_result.stderr diff --git a/end_to_end_tests/functional_tests/helpers.py b/end_to_end_tests/functional_tests/helpers.py index cb63da11b..6f6506687 100644 --- a/end_to_end_tests/functional_tests/helpers.py +++ b/end_to_end_tests/functional_tests/helpers.py @@ -1,8 +1,7 @@ from typing import Any, Dict import re -from typing import Optional -from click.testing import Result +from typer.testing import Result import pytest from end_to_end_tests.generated_client import generate_client_from_inline_spec, GeneratedClientContext @@ -15,7 +14,7 @@ def with_generated_client_fixture( 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(). """ @@ -31,9 +30,9 @@ def generated_client(self): return _decorator -def with_generated_code_import(import_path: str, alias: Optional[str] = None): +def with_generated_code_import(import_path: str, alias: str | None = 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 @@ -48,12 +47,12 @@ def _decorator(cls): 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 @@ -85,7 +84,7 @@ def inline_spec_should_fail( 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( @@ -118,7 +117,7 @@ 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 + output = generated_client.generator_result.stdout + generated_client.generator_result.stderr assert "Warning(s) encountered while generating" in output self.by_schema = {} self.output = output diff --git a/end_to_end_tests/generated_client.py b/end_to_end_tests/generated_client.py index d7cb16fc7..2f22913f6 100644 --- a/end_to_end_tests/generated_client.py +++ b/end_to_end_tests/generated_client.py @@ -5,12 +5,11 @@ from pathlib import Path import sys import tempfile -from typing import Any, Optional +from typing import Any, Self from attrs import define import pytest -from click.testing import Result -from typer.testing import CliRunner +from typer.testing import CliRunner, Result from openapi_python_client.cli import app @@ -28,9 +27,9 @@ class GeneratedClientContext: generator_result: Result base_module: str monkeypatch: pytest.MonkeyPatch - old_modules: Optional[set[str]] = None + old_modules: set[str] | None = None - def __enter__(self) -> "GeneratedClientContext": + def __enter__(self) -> Self: self.monkeypatch.syspath_prepend(self.output_path) self.old_modules = set(sys.modules.keys()) return self @@ -59,10 +58,10 @@ def import_symbol(self, module_path: str, name: str) -> Any: 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, + extra_args: list[str] | None = None, + openapi_document: str | None = None, + url: str | None = None, + config_path: Path | None = None, raise_on_error: bool = True, ) -> Result: """Generate a client from an OpenAPI document and return the result of the command.""" @@ -114,7 +113,7 @@ def generate_client_from_inline_spec( openapi_spec: str, extra_args: list[str] = [], config: str = "", - filename_suffix: Optional[str] = None, + filename_suffix: str | None = None, base_module: str = "testapi_client", add_missing_sections = True, raise_on_error: bool = True, diff --git a/end_to_end_tests/golden-record/README.md b/end_to_end_tests/golden-record/README.md index 79b20f411..32f776550 100644 --- a/end_to_end_tests/golden-record/README.md +++ b/end_to_end_tests/golden-record/README.md @@ -47,7 +47,7 @@ By default, when you're calling an HTTPS API it will attempt to verify that SSL ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -57,7 +57,7 @@ You can also disable certificate validation altogether, but beware that **this i ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl=False ) 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 626bacfa6..1a4fc2fd9 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,17 +1,17 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any import httpx from ... import errors from ...client import AuthenticatedClient, Client from ...models.json_like_body import JsonLikeBody -from ...types import Response +from ...types import UNSET, Response, Unset def _get_kwargs( *, - body: JsonLikeBody, + body: JsonLikeBody | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -20,25 +20,26 @@ def _get_kwargs( "url": "/bodies/json-like", } - _body = body.to_dict() + if not isinstance(body, Unset): + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/vnd+json" _kwargs["headers"] = headers return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,13 +50,13 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: JsonLikeBody, + client: AuthenticatedClient | Client, + body: JsonLikeBody | Unset = UNSET, ) -> Response[Any]: """A content type that works like json but isn't application/json Args: - body (JsonLikeBody): + body (JsonLikeBody | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -78,13 +79,13 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: JsonLikeBody, + client: AuthenticatedClient | Client, + body: JsonLikeBody | Unset = UNSET, ) -> Response[Any]: """A content type that works like json but isn't application/json Args: - body (JsonLikeBody): + body (JsonLikeBody | Unset): 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/bodies/optional_body.py b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/optional_body.py new file mode 100644 index 000000000..8402cf086 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/optional_body.py @@ -0,0 +1,104 @@ +from http import HTTPStatus +from typing import Any + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.optional_body_body import OptionalBodyBody +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + *, + body: OptionalBodyBody | Unset = UNSET, +) -> dict[str, Any]: + headers: dict[str, Any] = {} + + _kwargs: dict[str, Any] = { + "method": "post", + "url": "/bodies/optional", + } + + if not isinstance(body, Unset): + _kwargs["json"] = body.to_dict() + + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: + 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: 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: AuthenticatedClient | Client, + body: OptionalBodyBody | Unset = UNSET, +) -> Response[Any]: + """Test optional request body + + Args: + body (OptionalBodyBody | Unset): + + 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( + body=body, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +async def asyncio_detailed( + *, + client: AuthenticatedClient | Client, + body: OptionalBodyBody | Unset = UNSET, +) -> Response[Any]: + """Test optional request body + + Args: + body (OptionalBodyBody | Unset): + + 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( + body=body, + ) + + 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/bodies/post_bodies_multiple.py b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/post_bodies_multiple.py index 84ec8eee0..9361e3f56 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, Optional, Union +from typing import Any import httpx @@ -8,17 +8,12 @@ from ...models.post_bodies_multiple_data_body import PostBodiesMultipleDataBody from ...models.post_bodies_multiple_files_body import PostBodiesMultipleFilesBody from ...models.post_bodies_multiple_json_body import PostBodiesMultipleJsonBody -from ...types import File, Response +from ...types import UNSET, File, Response, Unset def _get_kwargs( *, - body: Union[ - PostBodiesMultipleJsonBody, - File, - PostBodiesMultipleDataBody, - PostBodiesMultipleFilesBody, - ], + body: PostBodiesMultipleJsonBody | File | PostBodiesMultipleDataBody | PostBodiesMultipleFilesBody | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -28,40 +23,39 @@ def _get_kwargs( } if isinstance(body, PostBodiesMultipleJsonBody): - _json_body = body.to_dict() + if not isinstance(body, Unset): + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _json_body headers["Content-Type"] = "application/json" if isinstance(body, File): - _content_body = body.payload - - _kwargs["content"] = _content_body + if not isinstance(body, Unset): + _kwargs["content"] = body.payload headers["Content-Type"] = "application/octet-stream" if isinstance(body, PostBodiesMultipleDataBody): - _data_body = body.to_dict() - - _kwargs["data"] = _data_body + if not isinstance(body, Unset): + _kwargs["data"] = body.to_dict() headers["Content-Type"] = "application/x-www-form-urlencoded" if isinstance(body, PostBodiesMultipleFilesBody): - _files_body = body.to_multipart() + if not isinstance(body, Unset): + _kwargs["files"] = body.to_multipart() - _kwargs["files"] = _files_body - headers["Content-Type"] = "multipart/form-data" + headers["Content-Type"] = "multipart/form-data; boundary=+++" _kwargs["headers"] = headers return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -72,21 +66,16 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: Union[ - PostBodiesMultipleJsonBody, - File, - PostBodiesMultipleDataBody, - PostBodiesMultipleFilesBody, - ], + client: AuthenticatedClient | Client, + body: PostBodiesMultipleJsonBody | File | PostBodiesMultipleDataBody | PostBodiesMultipleFilesBody | Unset = UNSET, ) -> Response[Any]: """Test multiple bodies Args: - body (PostBodiesMultipleJsonBody): - body (File): - body (PostBodiesMultipleDataBody): - body (PostBodiesMultipleFilesBody): + body (PostBodiesMultipleJsonBody | Unset): + body (File | Unset): + body (PostBodiesMultipleDataBody | Unset): + body (PostBodiesMultipleFilesBody | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -109,21 +98,16 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: Union[ - PostBodiesMultipleJsonBody, - File, - PostBodiesMultipleDataBody, - PostBodiesMultipleFilesBody, - ], + client: AuthenticatedClient | Client, + body: PostBodiesMultipleJsonBody | File | PostBodiesMultipleDataBody | PostBodiesMultipleFilesBody | Unset = UNSET, ) -> Response[Any]: """Test multiple bodies Args: - body (PostBodiesMultipleJsonBody): - body (File): - body (PostBodiesMultipleDataBody): - body (PostBodiesMultipleFilesBody): + body (PostBodiesMultipleJsonBody | Unset): + body (File | Unset): + body (PostBodiesMultipleDataBody | Unset): + body (PostBodiesMultipleFilesBody | Unset): 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/bodies/refs.py b/end_to_end_tests/golden-record/my_test_api_client/api/bodies/refs.py index a79cf178a..2e224bc8c 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,17 +1,17 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any import httpx from ... import errors from ...client import AuthenticatedClient, Client from ...models.a_model import AModel -from ...types import Response +from ...types import UNSET, Response, Unset def _get_kwargs( *, - body: AModel, + body: AModel | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -20,25 +20,26 @@ def _get_kwargs( "url": "/bodies/refs", } - _body = body.to_dict() + if not isinstance(body, Unset): + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,13 +50,13 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: AModel, + client: AuthenticatedClient | Client, + body: AModel | Unset = UNSET, ) -> Response[Any]: """Test request body defined via ref Args: - body (AModel): A Model for testing all the ways custom objects can be used + body (AModel | Unset): A Model for testing all the ways custom objects can be used Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -78,13 +79,13 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: AModel, + client: AuthenticatedClient | Client, + body: AModel | Unset = UNSET, ) -> Response[Any]: """Test request body defined via ref Args: - body (AModel): A Model for testing all the ways custom objects can be used + body (AModel | Unset): A Model for testing all the ways custom objects can be used 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/config/content_type_override.py b/end_to_end_tests/golden-record/my_test_api_client/api/config/content_type_override.py index 2bd74aac4..be06459be 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,16 +1,16 @@ from http import HTTPStatus -from typing import Any, Optional, Union, cast +from typing import Any, cast import httpx from ... import errors from ...client import AuthenticatedClient, Client -from ...types import Response +from ...types import UNSET, Response, Unset def _get_kwargs( *, - body: str, + body: str | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -19,26 +19,27 @@ def _get_kwargs( "url": "/config/content-type-override", } - _body = body + if not isinstance(body, Unset): + _kwargs["json"] = body - _kwargs["json"] = _body headers["Content-Type"] = "openapi/python/client" _kwargs["headers"] = headers return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[str]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> str | None: if response.status_code == 200: response_200 = cast(str, 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[str]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[str]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,13 +50,13 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: str, + client: AuthenticatedClient | Client, + body: str | Unset = UNSET, ) -> Response[str]: """Content Type Override Args: - body (str): + body (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -78,13 +79,13 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], - body: str, -) -> Optional[str]: + client: AuthenticatedClient | Client, + body: str | Unset = UNSET, +) -> str | None: """Content Type Override Args: - body (str): + body (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -102,13 +103,13 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: str, + client: AuthenticatedClient | Client, + body: str | Unset = UNSET, ) -> Response[str]: """Content Type Override Args: - body (str): + body (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -129,13 +130,13 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], - body: str, -) -> Optional[str]: + client: AuthenticatedClient | Client, + body: str | Unset = UNSET, +) -> str | None: """Content Type Override Args: - body (str): + body (str | Unset): 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/default/get_common_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py index 7de222f55..c82055686 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, Optional, Union +from typing import Any import httpx @@ -10,8 +10,9 @@ def _get_kwargs( *, - common: Union[Unset, str] = UNSET, + common: str | Unset = UNSET, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["common"] = common @@ -27,16 +28,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -47,12 +49,12 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - common: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + common: str | Unset = UNSET, ) -> Response[Any]: """ Args: - common (Union[Unset, str]): + common (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -75,12 +77,12 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - common: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + common: str | Unset = UNSET, ) -> Response[Any]: """ Args: - common (Union[Unset, str]): + common (str | Unset): 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/default/get_models_allof.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_models_allof.py index 9d837acd6..3558ef1be 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, Optional, Union +from typing import Any import httpx @@ -10,6 +10,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/models/allof", @@ -19,12 +20,13 @@ def _get_kwargs() -> dict[str, Any]: def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[GetModelsAllofResponse200]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> GetModelsAllofResponse200 | None: if response.status_code == 200: response_200 = GetModelsAllofResponse200.from_dict(response.json()) return response_200 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -32,7 +34,7 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response + *, client: AuthenticatedClient | Client, response: httpx.Response ) -> Response[GetModelsAllofResponse200]: return Response( status_code=HTTPStatus(response.status_code), @@ -44,7 +46,7 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[GetModelsAllofResponse200]: """ Raises: @@ -66,8 +68,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[GetModelsAllofResponse200]: + client: AuthenticatedClient | Client, +) -> GetModelsAllofResponse200 | None: """ Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -84,7 +86,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[GetModelsAllofResponse200]: """ Raises: @@ -104,8 +106,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[GetModelsAllofResponse200]: + client: AuthenticatedClient | Client, +) -> GetModelsAllofResponse200 | None: """ 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/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 85f68fb7c..73911b7cd 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, Optional, Union +from typing import Any import httpx @@ -15,6 +15,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/models/oneof-with-required-const", @@ -24,24 +25,20 @@ def _get_kwargs() -> dict[str, Any]: def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[ - Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"] -]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1 | None: if response.status_code == 200: def _parse_response_200( data: object, - ) -> Union[ - "GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1" - ]: + ) -> GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1: try: if not isinstance(data, dict): raise TypeError() response_200_type_0 = GetModelsOneofWithRequiredConstResponse200Type0.from_dict(data) return response_200_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass if not isinstance(data, dict): raise TypeError() @@ -52,6 +49,7 @@ def _parse_response_200( response_200 = _parse_response_200(response.json()) return response_200 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -59,10 +57,8 @@ def _parse_response_200( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[ - Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"] -]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -73,17 +69,15 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], -) -> Response[ - Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"] -]: + client: AuthenticatedClient | Client, +) -> Response[GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1]: """ 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[Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']] + Response[GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1] """ kwargs = _get_kwargs() @@ -97,17 +91,15 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[ - Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"] -]: + client: AuthenticatedClient | Client, +) -> GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1 | None: """ 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: - Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1'] + GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1 """ return sync_detailed( @@ -117,17 +109,15 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], -) -> Response[ - Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"] -]: + client: AuthenticatedClient | Client, +) -> Response[GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1]: """ 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[Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']] + Response[GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1] """ kwargs = _get_kwargs() @@ -139,17 +129,15 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[ - Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"] -]: + client: AuthenticatedClient | Client, +) -> GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1 | None: """ 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: - Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1'] + GetModelsOneofWithRequiredConstResponse200Type0 | GetModelsOneofWithRequiredConstResponse200Type1 """ return ( 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 5bd941c69..27509b93a 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, Optional, Union +from typing import Any import httpx @@ -10,8 +10,9 @@ def _get_kwargs( *, - common: Union[Unset, str] = UNSET, + common: str | Unset = UNSET, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["common"] = common @@ -27,16 +28,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -47,12 +49,12 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - common: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + common: str | Unset = UNSET, ) -> Response[Any]: """ Args: - common (Union[Unset, str]): + common (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -75,12 +77,12 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - common: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + common: str | Unset = UNSET, ) -> Response[Any]: """ Args: - common (Union[Unset, str]): + common (str | Unset): 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/tests/upload_multiple_files_tests_upload_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/post_types_unions_duplicate_types.py similarity index 56% rename from end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py rename to end_to_end_tests/golden-record/my_test_api_client/api/default/post_types_unions_duplicate_types.py index 3f8edc817..f6eabc4a7 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/default/post_types_unions_duplicate_types.py @@ -1,56 +1,55 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any import httpx from ... import errors from ...client import AuthenticatedClient, Client -from ...models.http_validation_error import HTTPValidationError -from ...types import File, Response +from ...models.a_model import AModel +from ...types import UNSET, Response, Unset def _get_kwargs( *, - body: list[File], + body: AModel | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} _kwargs: dict[str, Any] = { "method": "post", - "url": "/tests/upload/multiple", + "url": "/types/unions/duplicate-types", } - _body = [] - for body_item_data in body: - body_item = body_item_data.to_tuple() + if isinstance(body, AModel): + _kwargs["json"] = body.to_dict() - _body.append(body_item) - - _kwargs["files"] = _body + headers["Content-Type"] = "application/json" _kwargs["headers"] = headers return _kwargs -def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Any, HTTPValidationError]]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> AModel | None: if response.status_code == 200: - response_200 = response.json() + + def _parse_response_200(data: object) -> AModel: + if not isinstance(data, dict): + raise TypeError() + response_200_type_0 = AModel.from_dict(data) + + return response_200_type_0 + + response_200 = _parse_response_200(response.json()) + return response_200 - if response.status_code == 422: - response_422 = HTTPValidationError.from_dict(response.json()) - return response_422 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[Union[Any, HTTPValidationError]]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[AModel]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -61,22 +60,19 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: list[File], -) -> Response[Union[Any, HTTPValidationError]]: - """Upload multiple files - - Upload several files in the same request - + client: AuthenticatedClient | Client, + body: AModel | Unset = UNSET, +) -> Response[AModel]: + """ Args: - body (list[File]): + body (AModel | Unset): 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[Union[Any, HTTPValidationError]] + Response[AModel] """ kwargs = _get_kwargs( @@ -92,22 +88,19 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], - body: list[File], -) -> Optional[Union[Any, HTTPValidationError]]: - """Upload multiple files - - Upload several files in the same request - + client: AuthenticatedClient | Client, + body: AModel | Unset = UNSET, +) -> AModel | None: + """ Args: - body (list[File]): + body (AModel | Unset): 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: - Union[Any, HTTPValidationError] + AModel """ return sync_detailed( @@ -118,22 +111,19 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: list[File], -) -> Response[Union[Any, HTTPValidationError]]: - """Upload multiple files - - Upload several files in the same request - + client: AuthenticatedClient | Client, + body: AModel | Unset = UNSET, +) -> Response[AModel]: + """ Args: - body (list[File]): + body (AModel | Unset): 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[Union[Any, HTTPValidationError]] + Response[AModel] """ kwargs = _get_kwargs( @@ -147,22 +137,19 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], - body: list[File], -) -> Optional[Union[Any, HTTPValidationError]]: - """Upload multiple files - - Upload several files in the same request - + client: AuthenticatedClient | Client, + body: AModel | Unset = UNSET, +) -> AModel | None: + """ Args: - body (list[File]): + body (AModel | Unset): 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: - Union[Any, HTTPValidationError] + AModel """ return ( 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 fe7adf04c..687473903 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, Optional, Union +from typing import Any import httpx @@ -13,6 +13,7 @@ def _get_kwargs( client_query: str, url_query: str, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["client"] = client_query @@ -30,16 +31,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -50,7 +52,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, client_query: str, url_query: str, ) -> Response[Any]: @@ -81,7 +83,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, client_query: str, url_query: str, ) -> Response[Any]: 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 ffc9b535e..9990c8b5e 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,9 +1,8 @@ import datetime from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any import httpx -from dateutil.parser import isoparse from ... import errors from ...client import AuthenticatedClient, Client @@ -17,18 +16,19 @@ def _get_kwargs( *, string_prop: str = "the default string", string_with_num: str = "1", - date_prop: datetime.date = isoparse("1010-10-10").date(), + date_prop: datetime.date = datetime.date.fromisoformat("1010-10-10"), float_prop: float = 3.14, float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, list_prop: list[AnEnum], - union_prop: Union[float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, + union_prop: float | str = "not a float", + union_prop_with_ref: AnEnum | float | Unset = 0.6, enum_prop: AnEnum, - model_prop: "ModelWithUnionProperty", - required_model_prop: "ModelWithUnionProperty", + model_prop: ModelWithUnionProperty, + required_model_prop: ModelWithUnionProperty, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["string_prop"] = string_prop @@ -53,11 +53,11 @@ def _get_kwargs( params["list_prop"] = json_list_prop - json_union_prop: Union[float, str] + json_union_prop: float | str json_union_prop = union_prop params["union_prop"] = json_union_prop - json_union_prop_with_ref: Union[Unset, float, str] + json_union_prop_with_ref: float | str | Unset if isinstance(union_prop_with_ref, Unset): json_union_prop_with_ref = UNSET elif isinstance(union_prop_with_ref, AnEnum): @@ -87,15 +87,17 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Any | HTTPValidationError | None: if response.status_code == 200: response_200 = response.json() return response_200 + if response.status_code == 422: response_422 = HTTPValidationError.from_dict(response.json()) return response_422 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -103,8 +105,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[Any | HTTPValidationError]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -115,34 +117,34 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, string_prop: str = "the default string", string_with_num: str = "1", - date_prop: datetime.date = isoparse("1010-10-10").date(), + date_prop: datetime.date = datetime.date.fromisoformat("1010-10-10"), float_prop: float = 3.14, float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, list_prop: list[AnEnum], - union_prop: Union[float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, + union_prop: float | str = "not a float", + union_prop_with_ref: AnEnum | float | Unset = 0.6, enum_prop: AnEnum, - model_prop: "ModelWithUnionProperty", - required_model_prop: "ModelWithUnionProperty", -) -> Response[Union[Any, HTTPValidationError]]: + model_prop: ModelWithUnionProperty, + required_model_prop: ModelWithUnionProperty, +) -> Response[Any | HTTPValidationError]: """Defaults Args: string_prop (str): Default: 'the default string'. string_with_num (str): Default: '1'. - date_prop (datetime.date): Default: isoparse('1010-10-10').date(). + date_prop (datetime.date): Default: datetime.date.fromisoformat('1010-10-10'). float_prop (float): Default: 3.14. float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. list_prop (list[AnEnum]): - union_prop (Union[float, str]): Default: 'not a float'. - union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. + union_prop (float | str): Default: 'not a float'. + union_prop_with_ref (AnEnum | float | Unset): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used model_prop (ModelWithUnionProperty): required_model_prop (ModelWithUnionProperty): @@ -152,7 +154,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -180,34 +182,34 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, string_prop: str = "the default string", string_with_num: str = "1", - date_prop: datetime.date = isoparse("1010-10-10").date(), + date_prop: datetime.date = datetime.date.fromisoformat("1010-10-10"), float_prop: float = 3.14, float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, list_prop: list[AnEnum], - union_prop: Union[float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, + union_prop: float | str = "not a float", + union_prop_with_ref: AnEnum | float | Unset = 0.6, enum_prop: AnEnum, - model_prop: "ModelWithUnionProperty", - required_model_prop: "ModelWithUnionProperty", -) -> Optional[Union[Any, HTTPValidationError]]: + model_prop: ModelWithUnionProperty, + required_model_prop: ModelWithUnionProperty, +) -> Any | HTTPValidationError | None: """Defaults Args: string_prop (str): Default: 'the default string'. string_with_num (str): Default: '1'. - date_prop (datetime.date): Default: isoparse('1010-10-10').date(). + date_prop (datetime.date): Default: datetime.date.fromisoformat('1010-10-10'). float_prop (float): Default: 3.14. float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. list_prop (list[AnEnum]): - union_prop (Union[float, str]): Default: 'not a float'. - union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. + union_prop (float | str): Default: 'not a float'. + union_prop_with_ref (AnEnum | float | Unset): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used model_prop (ModelWithUnionProperty): required_model_prop (ModelWithUnionProperty): @@ -217,7 +219,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return sync_detailed( @@ -240,34 +242,34 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, string_prop: str = "the default string", string_with_num: str = "1", - date_prop: datetime.date = isoparse("1010-10-10").date(), + date_prop: datetime.date = datetime.date.fromisoformat("1010-10-10"), float_prop: float = 3.14, float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, list_prop: list[AnEnum], - union_prop: Union[float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, + union_prop: float | str = "not a float", + union_prop_with_ref: AnEnum | float | Unset = 0.6, enum_prop: AnEnum, - model_prop: "ModelWithUnionProperty", - required_model_prop: "ModelWithUnionProperty", -) -> Response[Union[Any, HTTPValidationError]]: + model_prop: ModelWithUnionProperty, + required_model_prop: ModelWithUnionProperty, +) -> Response[Any | HTTPValidationError]: """Defaults Args: string_prop (str): Default: 'the default string'. string_with_num (str): Default: '1'. - date_prop (datetime.date): Default: isoparse('1010-10-10').date(). + date_prop (datetime.date): Default: datetime.date.fromisoformat('1010-10-10'). float_prop (float): Default: 3.14. float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. list_prop (list[AnEnum]): - union_prop (Union[float, str]): Default: 'not a float'. - union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. + union_prop (float | str): Default: 'not a float'. + union_prop_with_ref (AnEnum | float | Unset): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used model_prop (ModelWithUnionProperty): required_model_prop (ModelWithUnionProperty): @@ -277,7 +279,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -303,34 +305,34 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, string_prop: str = "the default string", string_with_num: str = "1", - date_prop: datetime.date = isoparse("1010-10-10").date(), + date_prop: datetime.date = datetime.date.fromisoformat("1010-10-10"), float_prop: float = 3.14, float_with_int: float = 3.0, int_prop: int = 7, boolean_prop: bool = False, list_prop: list[AnEnum], - union_prop: Union[float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, + union_prop: float | str = "not a float", + union_prop_with_ref: AnEnum | float | Unset = 0.6, enum_prop: AnEnum, - model_prop: "ModelWithUnionProperty", - required_model_prop: "ModelWithUnionProperty", -) -> Optional[Union[Any, HTTPValidationError]]: + model_prop: ModelWithUnionProperty, + required_model_prop: ModelWithUnionProperty, +) -> Any | HTTPValidationError | None: """Defaults Args: string_prop (str): Default: 'the default string'. string_with_num (str): Default: '1'. - date_prop (datetime.date): Default: isoparse('1010-10-10').date(). + date_prop (datetime.date): Default: datetime.date.fromisoformat('1010-10-10'). float_prop (float): Default: 3.14. float_with_int (float): Default: 3.0. int_prop (int): Default: 7. boolean_prop (bool): Default: False. list_prop (list[AnEnum]): - union_prop (Union[float, str]): Default: 'not a float'. - union_prop_with_ref (Union[AnEnum, Unset, float]): Default: 0.6. + union_prop (float | str): Default: 'not a float'. + union_prop_with_ref (AnEnum | float | Unset): Default: 0.6. enum_prop (AnEnum): For testing Enums in all the ways they can be used model_prop (ModelWithUnionProperty): required_model_prop (ModelWithUnionProperty): @@ -340,7 +342,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return ( 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 52385855c..53b574ebd 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, Optional, Union +from typing import Any import httpx @@ -12,6 +12,7 @@ def _get_kwargs( *, bool_enum: bool, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["bool_enum"] = bool_enum @@ -27,16 +28,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -47,7 +49,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, bool_enum: bool, ) -> Response[Any]: """Bool Enum @@ -76,7 +78,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, bool_enum: bool, ) -> Response[Any]: """Bool Enum 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 26c3729fe..264c5af9e 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, Optional, Union +from typing import Any import httpx @@ -13,6 +13,7 @@ def _get_kwargs( *, int_enum: AnIntEnum, ) -> dict[str, Any]: + params: dict[str, Any] = {} json_int_enum = int_enum.value @@ -29,16 +30,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,7 +51,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, int_enum: AnIntEnum, ) -> Response[Any]: """Int Enum @@ -78,7 +80,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, int_enum: AnIntEnum, ) -> Response[Any]: """Int Enum 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 ad9428a72..20c2db527 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, Optional, Union +from typing import Any import httpx @@ -12,12 +12,12 @@ def _get_kwargs( *, - boolean_header: Union[Unset, bool] = UNSET, - string_header: Union[Unset, str] = UNSET, - number_header: Union[Unset, float] = UNSET, - integer_header: Union[Unset, int] = UNSET, - int_enum_header: Union[Unset, GetLocationHeaderTypesIntEnumHeader] = UNSET, - string_enum_header: Union[Unset, GetLocationHeaderTypesStringEnumHeader] = UNSET, + boolean_header: bool | Unset = UNSET, + string_header: str | Unset = UNSET, + number_header: float | Unset = UNSET, + integer_header: int | Unset = UNSET, + int_enum_header: GetLocationHeaderTypesIntEnumHeader | Unset = UNSET, + string_enum_header: GetLocationHeaderTypesStringEnumHeader | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} if not isinstance(boolean_header, Unset): @@ -47,16 +47,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -67,22 +68,22 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - boolean_header: Union[Unset, bool] = UNSET, - string_header: Union[Unset, str] = UNSET, - number_header: Union[Unset, float] = UNSET, - integer_header: Union[Unset, int] = UNSET, - int_enum_header: Union[Unset, GetLocationHeaderTypesIntEnumHeader] = UNSET, - string_enum_header: Union[Unset, GetLocationHeaderTypesStringEnumHeader] = UNSET, + client: AuthenticatedClient | Client, + boolean_header: bool | Unset = UNSET, + string_header: str | Unset = UNSET, + number_header: float | Unset = UNSET, + integer_header: int | Unset = UNSET, + int_enum_header: GetLocationHeaderTypesIntEnumHeader | Unset = UNSET, + string_enum_header: GetLocationHeaderTypesStringEnumHeader | Unset = UNSET, ) -> Response[Any]: """ Args: - boolean_header (Union[Unset, bool]): - string_header (Union[Unset, str]): - number_header (Union[Unset, float]): - integer_header (Union[Unset, int]): - int_enum_header (Union[Unset, GetLocationHeaderTypesIntEnumHeader]): - string_enum_header (Union[Unset, GetLocationHeaderTypesStringEnumHeader]): + boolean_header (bool | Unset): + string_header (str | Unset): + number_header (float | Unset): + integer_header (int | Unset): + int_enum_header (GetLocationHeaderTypesIntEnumHeader | Unset): + string_enum_header (GetLocationHeaderTypesStringEnumHeader | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -110,22 +111,22 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - boolean_header: Union[Unset, bool] = UNSET, - string_header: Union[Unset, str] = UNSET, - number_header: Union[Unset, float] = UNSET, - integer_header: Union[Unset, int] = UNSET, - int_enum_header: Union[Unset, GetLocationHeaderTypesIntEnumHeader] = UNSET, - string_enum_header: Union[Unset, GetLocationHeaderTypesStringEnumHeader] = UNSET, + client: AuthenticatedClient | Client, + boolean_header: bool | Unset = UNSET, + string_header: str | Unset = UNSET, + number_header: float | Unset = UNSET, + integer_header: int | Unset = UNSET, + int_enum_header: GetLocationHeaderTypesIntEnumHeader | Unset = UNSET, + string_enum_header: GetLocationHeaderTypesStringEnumHeader | Unset = UNSET, ) -> Response[Any]: """ Args: - boolean_header (Union[Unset, bool]): - string_header (Union[Unset, str]): - number_header (Union[Unset, float]): - integer_header (Union[Unset, int]): - int_enum_header (Union[Unset, GetLocationHeaderTypesIntEnumHeader]): - string_enum_header (Union[Unset, GetLocationHeaderTypesStringEnumHeader]): + boolean_header (bool | Unset): + string_header (str | Unset): + number_header (float | Unset): + integer_header (int | Unset): + int_enum_header (GetLocationHeaderTypesIntEnumHeader | Unset): + string_enum_header (GetLocationHeaderTypesStringEnumHeader | Unset): 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/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 e28e37a36..756e4a266 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, Optional, Union +from typing import Any import httpx @@ -12,23 +12,24 @@ def _get_kwargs( *, not_null_required: datetime.datetime, - null_required: Union[None, datetime.datetime], - null_not_required: Union[None, Unset, datetime.datetime] = UNSET, - not_null_not_required: Union[Unset, datetime.datetime] = UNSET, + null_required: datetime.datetime | None, + null_not_required: datetime.datetime | None | Unset = UNSET, + not_null_not_required: datetime.datetime | Unset = UNSET, ) -> dict[str, Any]: + params: dict[str, Any] = {} json_not_null_required = not_null_required.isoformat() params["not_null_required"] = json_not_null_required - json_null_required: Union[None, str] + json_null_required: None | str if isinstance(null_required, datetime.datetime): json_null_required = null_required.isoformat() else: json_null_required = null_required params["null_required"] = json_null_required - json_null_not_required: Union[None, Unset, str] + json_null_not_required: None | str | Unset if isinstance(null_not_required, Unset): json_null_not_required = UNSET elif isinstance(null_not_required, datetime.datetime): @@ -37,7 +38,7 @@ def _get_kwargs( json_null_not_required = null_not_required params["null_not_required"] = json_null_not_required - json_not_null_not_required: Union[Unset, str] = UNSET + json_not_null_not_required: str | Unset = UNSET if not isinstance(not_null_not_required, Unset): json_not_null_not_required = not_null_not_required.isoformat() params["not_null_not_required"] = json_not_null_not_required @@ -53,16 +54,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -73,18 +75,18 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, not_null_required: datetime.datetime, - null_required: Union[None, datetime.datetime], - null_not_required: Union[None, Unset, datetime.datetime] = UNSET, - not_null_not_required: Union[Unset, datetime.datetime] = UNSET, + null_required: datetime.datetime | None, + null_not_required: datetime.datetime | None | Unset = UNSET, + not_null_not_required: datetime.datetime | Unset = UNSET, ) -> Response[Any]: """ Args: not_null_required (datetime.datetime): - null_required (Union[None, datetime.datetime]): - null_not_required (Union[None, Unset, datetime.datetime]): - not_null_not_required (Union[Unset, datetime.datetime]): + null_required (datetime.datetime | None): + null_not_required (datetime.datetime | None | Unset): + not_null_not_required (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -110,18 +112,18 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, not_null_required: datetime.datetime, - null_required: Union[None, datetime.datetime], - null_not_required: Union[None, Unset, datetime.datetime] = UNSET, - not_null_not_required: Union[Unset, datetime.datetime] = UNSET, + null_required: datetime.datetime | None, + null_not_required: datetime.datetime | None | Unset = UNSET, + not_null_not_required: datetime.datetime | Unset = UNSET, ) -> Response[Any]: """ Args: not_null_required (datetime.datetime): - null_required (Union[None, datetime.datetime]): - null_not_required (Union[None, Unset, datetime.datetime]): - not_null_not_required (Union[Unset, datetime.datetime]): + null_required (datetime.datetime | None): + null_not_required (datetime.datetime | None | Unset): + not_null_not_required (datetime.datetime | Unset): 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/naming/hyphen_in_path.py b/end_to_end_tests/golden-record/my_test_api_client/api/naming/hyphen_in_path.py index a0caba2d6..d30d3c1ae 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,6 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any +from urllib.parse import quote import httpx @@ -11,24 +12,28 @@ def _get_kwargs( hyphen_in_path: str, ) -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", - "url": f"/naming/{hyphen_in_path}", + "url": "/naming/{hyphen_in_path}".format( + hyphen_in_path=quote(str(hyphen_in_path), safe=""), + ), } return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -40,7 +45,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( hyphen_in_path: str, *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Args: @@ -68,7 +73,7 @@ def sync_detailed( async def asyncio_detailed( hyphen_in_path: str, *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Args: 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 7df2d318f..5945ad0de 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, Optional, Union +from typing import Any import httpx @@ -14,6 +14,7 @@ def _get_kwargs( mixed_case: str, mixedCase: str, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["mixed_case"] = mixed_case @@ -31,13 +32,12 @@ def _get_kwargs( return _kwargs -def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[MixedCaseResponse200]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> MixedCaseResponse200 | None: if response.status_code == 200: response_200 = MixedCaseResponse200.from_dict(response.json()) return response_200 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -45,7 +45,7 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response + *, client: AuthenticatedClient | Client, response: httpx.Response ) -> Response[MixedCaseResponse200]: return Response( status_code=HTTPStatus(response.status_code), @@ -57,7 +57,7 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, mixed_case: str, mixedCase: str, ) -> Response[MixedCaseResponse200]: @@ -88,10 +88,10 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, mixed_case: str, mixedCase: str, -) -> Optional[MixedCaseResponse200]: +) -> MixedCaseResponse200 | None: """ Args: mixed_case (str): @@ -114,7 +114,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, mixed_case: str, mixedCase: str, ) -> Response[MixedCaseResponse200]: @@ -143,10 +143,10 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, mixed_case: str, mixedCase: str, -) -> Optional[MixedCaseResponse200]: +) -> MixedCaseResponse200 | None: """ Args: mixed_case (str): 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 3bb8b698b..9e848f7af 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, Optional, Union +from typing import Any import httpx @@ -9,12 +9,12 @@ from ...models.post_naming_property_conflict_with_import_response_200 import ( PostNamingPropertyConflictWithImportResponse200, ) -from ...types import Response +from ...types import UNSET, Response, Unset def _get_kwargs( *, - body: PostNamingPropertyConflictWithImportBody, + body: PostNamingPropertyConflictWithImportBody | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -23,9 +23,9 @@ def _get_kwargs( "url": "/naming/property-conflict-with-import", } - _body = body.to_dict() + if not isinstance(body, Unset): + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers @@ -33,12 +33,13 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[PostNamingPropertyConflictWithImportResponse200]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> PostNamingPropertyConflictWithImportResponse200 | None: if response.status_code == 200: response_200 = PostNamingPropertyConflictWithImportResponse200.from_dict(response.json()) return response_200 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -46,7 +47,7 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response + *, client: AuthenticatedClient | Client, response: httpx.Response ) -> Response[PostNamingPropertyConflictWithImportResponse200]: return Response( status_code=HTTPStatus(response.status_code), @@ -58,12 +59,12 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: PostNamingPropertyConflictWithImportBody, + client: AuthenticatedClient | Client, + body: PostNamingPropertyConflictWithImportBody | Unset = UNSET, ) -> Response[PostNamingPropertyConflictWithImportResponse200]: """ Args: - body (PostNamingPropertyConflictWithImportBody): + body (PostNamingPropertyConflictWithImportBody | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -86,12 +87,12 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], - body: PostNamingPropertyConflictWithImportBody, -) -> Optional[PostNamingPropertyConflictWithImportResponse200]: + client: AuthenticatedClient | Client, + body: PostNamingPropertyConflictWithImportBody | Unset = UNSET, +) -> PostNamingPropertyConflictWithImportResponse200 | None: """ Args: - body (PostNamingPropertyConflictWithImportBody): + body (PostNamingPropertyConflictWithImportBody | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -109,12 +110,12 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: PostNamingPropertyConflictWithImportBody, + client: AuthenticatedClient | Client, + body: PostNamingPropertyConflictWithImportBody | Unset = UNSET, ) -> Response[PostNamingPropertyConflictWithImportResponse200]: """ Args: - body (PostNamingPropertyConflictWithImportBody): + body (PostNamingPropertyConflictWithImportBody | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -135,12 +136,12 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], - body: PostNamingPropertyConflictWithImportBody, -) -> Optional[PostNamingPropertyConflictWithImportResponse200]: + client: AuthenticatedClient | Client, + body: PostNamingPropertyConflictWithImportBody | Unset = UNSET, +) -> PostNamingPropertyConflictWithImportResponse200 | None: """ Args: - body (PostNamingPropertyConflictWithImportBody): + body (PostNamingPropertyConflictWithImportBody | Unset): 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/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 e7a8e2712..801929db9 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,6 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any +from urllib.parse import quote import httpx @@ -11,10 +12,10 @@ def _get_kwargs( path_param: str, *, - string_param: Union[Unset, str] = UNSET, - integer_param: Union[Unset, int] = 0, - header_param: Union[None, Unset, str] = UNSET, - cookie_param: Union[Unset, str] = UNSET, + string_param: str | Unset = UNSET, + integer_param: int | Unset = 0, + header_param: None | str | Unset = UNSET, + cookie_param: str | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} if not isinstance(header_param, Unset): @@ -34,7 +35,9 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": f"/parameter-references/{path_param}", + "url": "/parameter-references/{path_param}".format( + path_param=quote(str(path_param), safe=""), + ), "params": params, "cookies": cookies, } @@ -43,16 +46,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -64,20 +68,20 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( path_param: str, *, - client: Union[AuthenticatedClient, Client], - string_param: Union[Unset, str] = UNSET, - integer_param: Union[Unset, int] = 0, - header_param: Union[None, Unset, str] = UNSET, - cookie_param: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + string_param: str | Unset = UNSET, + integer_param: int | Unset = 0, + header_param: None | str | Unset = UNSET, + cookie_param: str | Unset = UNSET, ) -> Response[Any]: """Test different types of parameter references Args: path_param (str): - string_param (Union[Unset, str]): - integer_param (Union[Unset, int]): Default: 0. - header_param (Union[None, Unset, str]): - cookie_param (Union[Unset, str]): + string_param (str | Unset): + integer_param (int | Unset): Default: 0. + header_param (None | str | Unset): + cookie_param (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -105,20 +109,20 @@ def sync_detailed( async def asyncio_detailed( path_param: str, *, - client: Union[AuthenticatedClient, Client], - string_param: Union[Unset, str] = UNSET, - integer_param: Union[Unset, int] = 0, - header_param: Union[None, Unset, str] = UNSET, - cookie_param: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + string_param: str | Unset = UNSET, + integer_param: int | Unset = 0, + header_param: None | str | Unset = UNSET, + cookie_param: str | Unset = UNSET, ) -> Response[Any]: """Test different types of parameter references Args: path_param (str): - string_param (Union[Unset, str]): - integer_param (Union[Unset, int]): Default: 0. - header_param (Union[None, Unset, str]): - cookie_param (Union[Unset, str]): + string_param (str | Unset): + integer_param (int | Unset): Default: 0. + header_param (None | str | Unset): + cookie_param (str | Unset): 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/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 704996107..23c5b72da 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,6 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any +from urllib.parse import quote import httpx @@ -11,8 +12,9 @@ def _get_kwargs( param_path: str, *, - param_query: Union[Unset, str] = UNSET, + param_query: str | Unset = UNSET, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["param"] = param_query @@ -21,23 +23,26 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "delete", - "url": f"/common_parameters_overriding/{param_path}", + "url": "/common_parameters_overriding/{param_path}".format( + param_path=quote(str(param_path), safe=""), + ), "params": params, } return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,13 +54,13 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( param_path: str, *, - client: Union[AuthenticatedClient, Client], - param_query: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + param_query: str | Unset = UNSET, ) -> Response[Any]: """ Args: param_path (str): - param_query (Union[Unset, str]): + param_query (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -80,13 +85,13 @@ def sync_detailed( async def asyncio_detailed( param_path: str, *, - client: Union[AuthenticatedClient, Client], - param_query: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + param_query: str | Unset = UNSET, ) -> Response[Any]: """ Args: param_path (str): - param_query (Union[Unset, str]): + param_query (str | Unset): 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/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 b6efbba9b..3233fa605 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,6 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any +from urllib.parse import quote import httpx @@ -13,6 +14,7 @@ def _get_kwargs( *, param_query: str = "overridden_in_GET", ) -> dict[str, Any]: + params: dict[str, Any] = {} params["param"] = param_query @@ -21,23 +23,26 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": f"/common_parameters_overriding/{param_path}", + "url": "/common_parameters_overriding/{param_path}".format( + param_path=quote(str(param_path), safe=""), + ), "params": params, } return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,7 +54,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( param_path: str, *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, param_query: str = "overridden_in_GET", ) -> Response[Any]: """Test that if you have an overriding property from `PathItem` in `Operation`, it produces valid code @@ -82,7 +87,7 @@ def sync_detailed( async def asyncio_detailed( param_path: str, *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, param_query: str = "overridden_in_GET", ) -> Response[Any]: """Test that if you have an overriding property from `PathItem` in `Operation`, it produces valid code 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 6a7ed7fd5..dd11f68ca 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,6 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any +from urllib.parse import quote import httpx @@ -11,9 +12,9 @@ def _get_kwargs( param_path: str, *, - param_query: Union[Unset, str] = UNSET, - param_header: Union[Unset, str] = UNSET, - param_cookie: Union[Unset, str] = UNSET, + param_query: str | Unset = UNSET, + param_header: str | Unset = UNSET, + param_cookie: str | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} if not isinstance(param_header, Unset): @@ -31,7 +32,9 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": f"/same-name-multiple-locations/{param_path}", + "url": "/same-name-multiple-locations/{param_path}".format( + param_path=quote(str(param_path), safe=""), + ), "params": params, "cookies": cookies, } @@ -40,16 +43,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -61,17 +65,17 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( param_path: str, *, - client: Union[AuthenticatedClient, Client], - param_query: Union[Unset, str] = UNSET, - param_header: Union[Unset, str] = UNSET, - param_cookie: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + param_query: str | Unset = UNSET, + param_header: str | Unset = UNSET, + param_cookie: str | Unset = UNSET, ) -> Response[Any]: """ Args: param_path (str): - param_query (Union[Unset, str]): - param_header (Union[Unset, str]): - param_cookie (Union[Unset, str]): + param_query (str | Unset): + param_header (str | Unset): + param_cookie (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -98,17 +102,17 @@ def sync_detailed( async def asyncio_detailed( param_path: str, *, - client: Union[AuthenticatedClient, Client], - param_query: Union[Unset, str] = UNSET, - param_header: Union[Unset, str] = UNSET, - param_cookie: Union[Unset, str] = UNSET, + client: AuthenticatedClient | Client, + param_query: str | Unset = UNSET, + param_header: str | Unset = UNSET, + param_cookie: str | Unset = UNSET, ) -> Response[Any]: """ Args: param_path (str): - param_query (Union[Unset, str]): - param_header (Union[Unset, str]): - param_cookie (Union[Unset, str]): + param_query (str | Unset): + param_header (str | Unset): + param_cookie (str | Unset): 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/parameters/multiple_path_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py index 44345aa26..d683b856c 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,6 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any +from urllib.parse import quote import httpx @@ -14,24 +15,31 @@ def _get_kwargs( param1: str, param3: int, ) -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", - "url": f"/multiple-path-parameters/{param4}/something/{param2}/{param1}/{param3}", + "url": "/multiple-path-parameters/{param4}/something/{param2}/{param1}/{param3}".format( + param4=quote(str(param4), safe=""), + param2=quote(str(param2), safe=""), + param1=quote(str(param1), safe=""), + param3=quote(str(param3), safe=""), + ), } return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -46,7 +54,7 @@ def sync_detailed( param1: str, param3: int, *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Args: @@ -83,7 +91,7 @@ async def asyncio_detailed( param1: str, param3: int, *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Args: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/responses/default_status_code.py b/end_to_end_tests/golden-record/my_test_api_client/api/responses/default_status_code.py new file mode 100644 index 000000000..aa32c5393 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/responses/default_status_code.py @@ -0,0 +1,115 @@ +from http import HTTPStatus +from typing import Any + +import httpx + +from ...client import AuthenticatedClient, Client +from ...types import Response + + +def _get_kwargs() -> dict[str, Any]: + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/responses/status-codes/default", + } + + return _kwargs + + +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> str: + response_default = response.text + return response_default + + +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[str]: + 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: AuthenticatedClient | Client, +) -> Response[str]: + """Default Status Code Only + + 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[str] + """ + + kwargs = _get_kwargs() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: AuthenticatedClient | Client, +) -> str | None: + """Default Status Code Only + + 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: + str + """ + + return sync_detailed( + client=client, + ).parsed + + +async def asyncio_detailed( + *, + client: AuthenticatedClient | Client, +) -> Response[str]: + """Default Status Code Only + + 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[str] + """ + + kwargs = _get_kwargs() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: AuthenticatedClient | Client, +) -> str | None: + """Default Status Code Only + + 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: + str + """ + + return ( + await asyncio_detailed( + client=client, + ) + ).parsed 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 cf0599306..022f30062 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, Optional, Union +from typing import Any import httpx @@ -12,6 +12,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "post", "url": "/responses/unions/simple_before_complex", @@ -21,12 +22,13 @@ def _get_kwargs() -> dict[str, Any]: def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> PostResponsesUnionsSimpleBeforeComplexResponse200 | None: if response.status_code == 200: response_200 = PostResponsesUnionsSimpleBeforeComplexResponse200.from_dict(response.json()) return response_200 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -34,7 +36,7 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response + *, client: AuthenticatedClient | Client, response: httpx.Response ) -> Response[PostResponsesUnionsSimpleBeforeComplexResponse200]: return Response( status_code=HTTPStatus(response.status_code), @@ -46,7 +48,7 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[PostResponsesUnionsSimpleBeforeComplexResponse200]: """Regression test for #603 @@ -69,8 +71,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]: + client: AuthenticatedClient | Client, +) -> PostResponsesUnionsSimpleBeforeComplexResponse200 | None: """Regression test for #603 Raises: @@ -88,7 +90,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[PostResponsesUnionsSimpleBeforeComplexResponse200]: """Regression test for #603 @@ -109,8 +111,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]: + client: AuthenticatedClient | Client, +) -> PostResponsesUnionsSimpleBeforeComplexResponse200 | None: """Regression test for #603 Raises: 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 index ac71e9e50..77429a73b 100644 --- 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 @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any import httpx @@ -10,6 +10,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/responses/reference", @@ -18,18 +19,19 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[AModel]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> AModel | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[AModel]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -40,7 +42,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[AModel]: """Endpoint using predefined response @@ -63,8 +65,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[AModel]: + client: AuthenticatedClient | Client, +) -> AModel | None: """Endpoint using predefined response Raises: @@ -82,7 +84,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[AModel]: """Endpoint using predefined response @@ -103,8 +105,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[AModel]: + client: AuthenticatedClient | Client, +) -> AModel | None: """Endpoint using predefined response Raises: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/responses/status_code_patterns.py b/end_to_end_tests/golden-record/my_test_api_client/api/responses/status_code_patterns.py new file mode 100644 index 000000000..796d3764f --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/responses/status_code_patterns.py @@ -0,0 +1,134 @@ +from http import HTTPStatus +from typing import Any + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.status_code_patterns_response_2xx import StatusCodePatternsResponse2XX +from ...models.status_code_patterns_response_4xx import StatusCodePatternsResponse4XX +from ...types import Response + + +def _get_kwargs() -> dict[str, Any]: + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/response/status-codes/patterns", + } + + return _kwargs + + +def _parse_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX | None: + if 200 <= response.status_code <= 299: + response_2xx = StatusCodePatternsResponse2XX.from_dict(response.json()) + + return response_2xx + + if 400 <= response.status_code <= 499: + response_4xx = StatusCodePatternsResponse4XX.from_dict(response.json()) + + return response_4xx + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX]: + 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: AuthenticatedClient | Client, +) -> Response[StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX]: + """Status Code Patterns + + 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[StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX] + """ + + kwargs = _get_kwargs() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: AuthenticatedClient | Client, +) -> StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX | None: + """Status Code Patterns + + 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: + StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX + """ + + return sync_detailed( + client=client, + ).parsed + + +async def asyncio_detailed( + *, + client: AuthenticatedClient | Client, +) -> Response[StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX]: + """Status Code Patterns + + 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[StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX] + """ + + kwargs = _get_kwargs() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: AuthenticatedClient | Client, +) -> StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX | None: + """Status Code Patterns + + 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: + StatusCodePatternsResponse2XX | StatusCodePatternsResponse4XX + """ + + return ( + await asyncio_detailed( + client=client, + ) + ).parsed diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/responses/status_code_precedence.py b/end_to_end_tests/golden-record/my_test_api_client/api/responses/status_code_precedence.py new file mode 100644 index 000000000..11d832c87 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/responses/status_code_precedence.py @@ -0,0 +1,135 @@ +from http import HTTPStatus +from typing import Any + +import httpx + +from ...client import AuthenticatedClient, Client +from ...types import Response + + +def _get_kwargs() -> dict[str, Any]: + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/response/status-codes/precedence", + } + + return _kwargs + + +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> str: + if response.status_code == 200: + response_200 = response.text + return response_200 + + if response.status_code == 404: + response_404 = response.text + return response_404 + + if 400 <= response.status_code <= 499: + response_4xx = response.text + return response_4xx + + response_default = response.text + return response_default + + +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[str]: + 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: AuthenticatedClient | Client, +) -> Response[str]: + """Status Codes Precedence + + Verify that specific status codes are always checked first, then ranges, then default + + 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[str] + """ + + kwargs = _get_kwargs() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: AuthenticatedClient | Client, +) -> str | None: + """Status Codes Precedence + + Verify that specific status codes are always checked first, then ranges, then default + + 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: + str + """ + + return sync_detailed( + client=client, + ).parsed + + +async def asyncio_detailed( + *, + client: AuthenticatedClient | Client, +) -> Response[str]: + """Status Codes Precedence + + Verify that specific status codes are always checked first, then ranges, then default + + 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[str] + """ + + kwargs = _get_kwargs() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: AuthenticatedClient | Client, +) -> str | None: + """Status Codes Precedence + + Verify that specific status codes are always checked first, then ranges, then default + + 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: + str + """ + + 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 057ceb2de..0f56f8c77 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, Optional, Union +from typing import Any import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "post", "url": "/responses/text", @@ -17,17 +18,18 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[str]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> str | None: if response.status_code == 200: response_200 = response.text 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[str]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[str]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -38,7 +40,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[str]: """Text Response @@ -61,8 +63,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[str]: + client: AuthenticatedClient | Client, +) -> str | None: """Text Response Raises: @@ -80,7 +82,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[str]: """Text Response @@ -101,8 +103,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[str]: + client: AuthenticatedClient | Client, +) -> str | None: """Text Response Raises: 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 62631355f..3ee534c8f 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, Optional, Union +from typing import Any import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tag_with_number", @@ -17,16 +18,17 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -37,7 +39,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Raises: @@ -59,7 +61,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Raises: 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 index 62631355f..3ee534c8f 100644 --- 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 @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Optional, Union +from typing import Any import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tag_with_number", @@ -17,16 +18,17 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -37,7 +39,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Raises: @@ -59,7 +61,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """ Raises: 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 0852815d2..e806fe60c 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, Optional, Union +from typing import Any import httpx @@ -21,9 +21,8 @@ def _get_kwargs( "url": "/tests/callback", } - _body = body.to_dict() + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers @@ -31,15 +30,17 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Any | HTTPValidationError | None: if response.status_code == 200: response_200 = response.json() return response_200 + if response.status_code == 422: response_422 = HTTPValidationError.from_dict(response.json()) return response_422 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -47,8 +48,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[Any | HTTPValidationError]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -59,9 +60,9 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Response[Union[Any, HTTPValidationError]]: +) -> Response[Any | HTTPValidationError]: """Path with callback Try sending a request related to a callback @@ -74,7 +75,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -90,9 +91,9 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Any | HTTPValidationError | None: """Path with callback Try sending a request related to a callback @@ -105,7 +106,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return sync_detailed( @@ -116,9 +117,9 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Response[Union[Any, HTTPValidationError]]: +) -> Response[Any | HTTPValidationError]: """Path with callback Try sending a request related to a callback @@ -131,7 +132,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -145,9 +146,9 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Any | HTTPValidationError | None: """Path with callback Try sending a request related to a callback @@ -160,7 +161,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return ( 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 e7cd44f70..d9182317d 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, Optional, Union +from typing import Any import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/description-with-backslash", @@ -17,16 +18,17 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -37,7 +39,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: r""" Test description with \ @@ -62,7 +64,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: r""" Test description with \ 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 147eed3a7..0a20d2f8f 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, Optional, Union, cast +from typing import Any, cast import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/booleans", @@ -17,18 +18,19 @@ 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: AuthenticatedClient | Client, response: httpx.Response) -> list[bool] | None: if response.status_code == 200: response_200 = cast(list[bool], 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[list[bool]]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[list[bool]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -39,7 +41,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[bool]]: """Get Basic List Of Booleans @@ -64,8 +66,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[bool]]: + client: AuthenticatedClient | Client, +) -> list[bool] | None: """Get Basic List Of Booleans Get a list of booleans @@ -85,7 +87,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[bool]]: """Get Basic List Of Booleans @@ -108,8 +110,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[bool]]: + client: AuthenticatedClient | Client, +) -> list[bool] | None: """Get Basic List Of Booleans Get a list of booleans 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 02b3abb1f..0beb7b1dd 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, Optional, Union, cast +from typing import Any, cast import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/floats", @@ -17,18 +18,19 @@ 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: AuthenticatedClient | Client, response: httpx.Response) -> list[float] | None: if response.status_code == 200: response_200 = cast(list[float], 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[list[float]]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[list[float]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -39,7 +41,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[float]]: """Get Basic List Of Floats @@ -64,8 +66,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[float]]: + client: AuthenticatedClient | Client, +) -> list[float] | None: """Get Basic List Of Floats Get a list of floats @@ -85,7 +87,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[float]]: """Get Basic List Of Floats @@ -108,8 +110,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[float]]: + client: AuthenticatedClient | Client, +) -> list[float] | None: """Get Basic List Of Floats Get a list of floats 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 e71537363..c0ca1e8c7 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, Optional, Union, cast +from typing import Any, cast import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/integers", @@ -17,18 +18,19 @@ 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: AuthenticatedClient | Client, response: httpx.Response) -> list[int] | None: if response.status_code == 200: response_200 = cast(list[int], 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[list[int]]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[list[int]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -39,7 +41,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[int]]: """Get Basic List Of Integers @@ -64,8 +66,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[int]]: + client: AuthenticatedClient | Client, +) -> list[int] | None: """Get Basic List Of Integers Get a list of integers @@ -85,7 +87,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[int]]: """Get Basic List Of Integers @@ -108,8 +110,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[int]]: + client: AuthenticatedClient | Client, +) -> list[int] | None: """Get Basic List Of Integers Get a list of integers 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 70f153829..4cb0a1bef 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, Optional, Union, cast +from typing import Any, cast import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/basic_lists/strings", @@ -17,18 +18,19 @@ 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: AuthenticatedClient | Client, response: httpx.Response) -> list[str] | None: if response.status_code == 200: response_200 = cast(list[str], 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[list[str]]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[list[str]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -39,7 +41,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[str]]: """Get Basic List Of Strings @@ -64,8 +66,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[str]]: + client: AuthenticatedClient | Client, +) -> list[str] | None: """Get Basic List Of Strings Get a list of strings @@ -85,7 +87,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[list[str]]: """Get Basic List Of Strings @@ -108,8 +110,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[list[str]]: + client: AuthenticatedClient | Client, +) -> list[str] | None: """Get Basic List Of Strings Get a list of strings 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 a708cf71d..5fcbd86e1 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, Optional, Union +from typing import Any import httpx @@ -16,10 +16,11 @@ def _get_kwargs( *, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[AnEnumWithNull | None], an_enum_value_with_only_null: list[None], - some_date: Union[datetime.date, datetime.datetime], + some_date: datetime.date | datetime.datetime, ) -> dict[str, Any]: + params: dict[str, Any] = {} json_an_enum_value = [] @@ -31,7 +32,7 @@ def _get_kwargs( json_an_enum_value_with_null = [] for an_enum_value_with_null_item_data in an_enum_value_with_null: - an_enum_value_with_null_item: Union[None, str] + an_enum_value_with_null_item: None | str if isinstance(an_enum_value_with_null_item_data, AnEnumWithNull): an_enum_value_with_null_item = an_enum_value_with_null_item_data.value else: @@ -64,8 +65,8 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[HTTPValidationError, list["AModel"]]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> HTTPValidationError | list[AModel] | None: if response.status_code == 200: response_200 = [] _response_200 = response.json() @@ -75,14 +76,17 @@ def _parse_response( response_200.append(response_200_item) return response_200 + if response.status_code == 422: response_422 = HTTPValidationError.from_dict(response.json()) return response_422 + if response.status_code == 423: response_423 = HTTPValidationError.from_dict(response.json()) return response_423 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -90,8 +94,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[HTTPValidationError, list["AModel"]]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[HTTPValidationError | list[AModel]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -102,28 +106,28 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[AnEnumWithNull | None], an_enum_value_with_only_null: list[None], - some_date: Union[datetime.date, datetime.datetime], -) -> Response[Union[HTTPValidationError, list["AModel"]]]: + some_date: datetime.date | datetime.datetime, +) -> Response[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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - some_date (Union[datetime.date, datetime.datetime]): + some_date (datetime.date | datetime.datetime): 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[Union[HTTPValidationError, list['AModel']]] + Response[HTTPValidationError | list[AModel]] """ kwargs = _get_kwargs( @@ -142,28 +146,28 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[AnEnumWithNull | None], an_enum_value_with_only_null: list[None], - some_date: Union[datetime.date, datetime.datetime], -) -> Optional[Union[HTTPValidationError, list["AModel"]]]: + some_date: datetime.date | datetime.datetime, +) -> HTTPValidationError | list[AModel] | None: """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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - some_date (Union[datetime.date, datetime.datetime]): + some_date (datetime.date | datetime.datetime): 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: - Union[HTTPValidationError, list['AModel']] + HTTPValidationError | list[AModel] """ return sync_detailed( @@ -177,28 +181,28 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[AnEnumWithNull | None], an_enum_value_with_only_null: list[None], - some_date: Union[datetime.date, datetime.datetime], -) -> Response[Union[HTTPValidationError, list["AModel"]]]: + some_date: datetime.date | datetime.datetime, +) -> Response[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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - some_date (Union[datetime.date, datetime.datetime]): + some_date (datetime.date | datetime.datetime): 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[Union[HTTPValidationError, list['AModel']]] + Response[HTTPValidationError | list[AModel]] """ kwargs = _get_kwargs( @@ -215,28 +219,28 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[AnEnumWithNull | None], an_enum_value_with_only_null: list[None], - some_date: Union[datetime.date, datetime.datetime], -) -> Optional[Union[HTTPValidationError, list["AModel"]]]: + some_date: datetime.date | datetime.datetime, +) -> HTTPValidationError | list[AModel] | None: """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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - some_date (Union[datetime.date, datetime.datetime]): + some_date (datetime.date | datetime.datetime): 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: - Union[HTTPValidationError, list['AModel']] + 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 f33a23dc7..5be950c77 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, Optional, Union +from typing import Any import httpx @@ -21,9 +21,8 @@ def _get_kwargs( "url": "/tests/json_body", } - _body = body.to_dict() + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers @@ -31,15 +30,17 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Any | HTTPValidationError | None: if response.status_code == 200: response_200 = response.json() return response_200 + if response.status_code == 422: response_422 = HTTPValidationError.from_dict(response.json()) return response_422 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -47,8 +48,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[Any | HTTPValidationError]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -59,9 +60,9 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Response[Union[Any, HTTPValidationError]]: +) -> Response[Any | HTTPValidationError]: """Json Body Try sending a JSON body @@ -74,7 +75,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -90,9 +91,9 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Any | HTTPValidationError | None: """Json Body Try sending a JSON body @@ -105,7 +106,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return sync_detailed( @@ -116,9 +117,9 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Response[Union[Any, HTTPValidationError]]: +) -> Response[Any | HTTPValidationError]: """Json Body Try sending a JSON body @@ -131,7 +132,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -145,9 +146,9 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Any | HTTPValidationError | None: """Json Body Try sending a JSON body @@ -160,7 +161,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return ( 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 586947f49..34c4b54c0 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, Optional, Union +from typing import Any import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/no_response", @@ -17,16 +18,17 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -37,7 +39,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """No Response @@ -60,7 +62,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """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 efb0f4ae5..c08c3caca 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, Optional, Union +from typing import Any import httpx @@ -10,6 +10,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/octet_stream", @@ -18,18 +19,19 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[File]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> File | None: if response.status_code == 200: response_200 = File(payload=BytesIO(response.content)) 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[File]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[File]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -40,7 +42,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[File]: """Octet Stream @@ -63,8 +65,8 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[File]: + client: AuthenticatedClient | Client, +) -> File | None: """Octet Stream Raises: @@ -82,7 +84,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[File]: """Octet Stream @@ -103,8 +105,8 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], -) -> Optional[File]: + client: AuthenticatedClient | Client, +) -> File | None: """Octet Stream Raises: 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 ea0cbd65a..1dd936601 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,17 +1,18 @@ from http import HTTPStatus -from typing import Any, Optional, Union, cast +from typing import Any import httpx from ... import errors from ...client import AuthenticatedClient, Client from ...models.http_validation_error import HTTPValidationError -from ...types import File, Response +from ...models.octet_stream_tests_octet_stream_post_response_200 import OctetStreamTestsOctetStreamPostResponse200 +from ...types import UNSET, File, Response, Unset def _get_kwargs( *, - body: File, + body: File | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -20,9 +21,8 @@ def _get_kwargs( "url": "/tests/octet_stream", } - _body = body.payload - - _kwargs["content"] = _body + if not isinstance(body, Unset): + _kwargs["content"] = body.payload headers["Content-Type"] = "application/octet-stream" _kwargs["headers"] = headers @@ -30,15 +30,18 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[HTTPValidationError, str]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200 | None: if response.status_code == 200: - response_200 = cast(str, response.json()) + response_200 = OctetStreamTestsOctetStreamPostResponse200.from_dict(response.json()) + return response_200 + if response.status_code == 422: response_422 = HTTPValidationError.from_dict(response.json()) return response_422 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -46,8 +49,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[HTTPValidationError, str]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -58,20 +61,20 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: File, -) -> Response[Union[HTTPValidationError, str]]: + client: AuthenticatedClient | Client, + body: File | Unset = UNSET, +) -> Response[HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200]: """Binary (octet stream) request body Args: - body (File): A file to upload + body (File | Unset): A file to upload 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[Union[HTTPValidationError, str]] + Response[HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200] """ kwargs = _get_kwargs( @@ -87,20 +90,20 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], - body: File, -) -> Optional[Union[HTTPValidationError, str]]: + client: AuthenticatedClient | Client, + body: File | Unset = UNSET, +) -> HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200 | None: """Binary (octet stream) request body Args: - body (File): A file to upload + body (File | Unset): A file to upload 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: - Union[HTTPValidationError, str] + HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200 """ return sync_detailed( @@ -111,20 +114,20 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: File, -) -> Response[Union[HTTPValidationError, str]]: + client: AuthenticatedClient | Client, + body: File | Unset = UNSET, +) -> Response[HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200]: """Binary (octet stream) request body Args: - body (File): A file to upload + body (File | Unset): A file to upload 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[Union[HTTPValidationError, str]] + Response[HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200] """ kwargs = _get_kwargs( @@ -138,20 +141,20 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], - body: File, -) -> Optional[Union[HTTPValidationError, str]]: + client: AuthenticatedClient | Client, + body: File | Unset = UNSET, +) -> HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200 | None: """Binary (octet stream) request body Args: - body (File): A file to upload + body (File | Unset): A file to upload 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: - Union[HTTPValidationError, str] + HTTPValidationError | OctetStreamTestsOctetStreamPostResponse200 """ return ( 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 ec65d0363..5e1ba06ed 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, Optional, Union +from typing import Any import httpx @@ -20,25 +20,24 @@ def _get_kwargs( "url": "/tests/post_form_data", } - _body = body.to_dict() - - _kwargs["data"] = _body + _kwargs["data"] = body.to_dict() headers["Content-Type"] = "application/x-www-form-urlencoded" _kwargs["headers"] = headers return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,7 +48,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AFormData, ) -> Response[Any]: """Post form data @@ -80,7 +79,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: AFormData, ) -> Response[Any]: """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 bc5ad7cc4..168fd9c82 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, Optional, Union +from typing import Any import httpx @@ -20,25 +20,24 @@ def _get_kwargs( "url": "/tests/post_form_data_inline", } - _body = body.to_dict() - - _kwargs["data"] = _body + _kwargs["data"] = body.to_dict() headers["Content-Type"] = "application/x-www-form-urlencoded" _kwargs["headers"] = headers return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,7 +48,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostFormDataInlineBody, ) -> Response[Any]: """Post form data (inline schema) @@ -80,7 +79,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostFormDataInlineBody, ) -> Response[Any]: """Post form data (inline schema) 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 ba40de26f..498d0572e 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, Optional, Union, cast +from typing import Any, cast import httpx @@ -20,9 +20,8 @@ def _get_kwargs( "url": "/tests/json_body/string", } - _body = body + _kwargs["json"] = body - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers @@ -30,15 +29,17 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[HTTPValidationError, str]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> HTTPValidationError | str | None: if response.status_code == 200: response_200 = cast(str, response.json()) return response_200 + if response.status_code == 422: response_422 = HTTPValidationError.from_dict(response.json()) return response_422 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -46,8 +47,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[HTTPValidationError, str]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[HTTPValidationError | str]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -58,9 +59,9 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: str, -) -> Response[Union[HTTPValidationError, str]]: +) -> Response[HTTPValidationError | str]: """Json Body Which is String Args: @@ -71,7 +72,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[HTTPValidationError, str]] + Response[HTTPValidationError | str] """ kwargs = _get_kwargs( @@ -87,9 +88,9 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: str, -) -> Optional[Union[HTTPValidationError, str]]: +) -> HTTPValidationError | str | None: """Json Body Which is String Args: @@ -100,7 +101,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[HTTPValidationError, str] + HTTPValidationError | str """ return sync_detailed( @@ -111,9 +112,9 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: str, -) -> Response[Union[HTTPValidationError, str]]: +) -> Response[HTTPValidationError | str]: """Json Body Which is String Args: @@ -124,7 +125,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[HTTPValidationError, str]] + Response[HTTPValidationError | str] """ kwargs = _get_kwargs( @@ -138,9 +139,9 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: str, -) -> Optional[Union[HTTPValidationError, str]]: +) -> HTTPValidationError | str | None: """Json Body Which is String Args: @@ -151,7 +152,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[HTTPValidationError, str] + HTTPValidationError | str """ return ( 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 287ea4a1a..ea2e45c94 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, Optional, Union +from typing import Any import httpx @@ -21,9 +21,8 @@ def _get_kwargs( "url": "/tests/inline_objects", } - _body = body.to_dict() + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers @@ -31,12 +30,13 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[TestInlineObjectsResponse200]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> TestInlineObjectsResponse200 | None: if response.status_code == 200: response_200 = TestInlineObjectsResponse200.from_dict(response.json()) return response_200 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -44,7 +44,7 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response + *, client: AuthenticatedClient | Client, response: httpx.Response ) -> Response[TestInlineObjectsResponse200]: return Response( status_code=HTTPStatus(response.status_code), @@ -56,7 +56,7 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: TestInlineObjectsBody, ) -> Response[TestInlineObjectsResponse200]: """Test Inline Objects @@ -85,9 +85,9 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: TestInlineObjectsBody, -) -> Optional[TestInlineObjectsResponse200]: +) -> TestInlineObjectsResponse200 | None: """Test Inline Objects Args: @@ -109,7 +109,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: TestInlineObjectsBody, ) -> Response[TestInlineObjectsResponse200]: """Test Inline Objects @@ -136,9 +136,9 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: TestInlineObjectsBody, -) -> Optional[TestInlineObjectsResponse200]: +) -> TestInlineObjectsResponse200 | None: """Test Inline Objects Args: 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 22ac00650..9ad310a2a 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, Optional, Union +from typing import Any import httpx @@ -12,6 +12,7 @@ def _get_kwargs( *, my_token: str, ) -> dict[str, Any]: + cookies = {} cookies["MyToken"] = my_token @@ -24,18 +25,20 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: if response.status_code == 200: return None + if response.status_code == 401: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -46,7 +49,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, my_token: str, ) -> Response[Any]: """TOKEN_WITH_COOKIE @@ -77,7 +80,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, my_token: str, ) -> Response[Any]: """TOKEN_WITH_COOKIE 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 61e8434e6..409f6147a 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, Optional, Union +from typing import Any import httpx @@ -9,6 +9,7 @@ def _get_kwargs() -> dict[str, Any]: + _kwargs: dict[str, Any] = { "method": "get", "url": "/tests/unsupported_content", @@ -17,16 +18,17 @@ def _get_kwargs() -> dict[str, Any]: return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -37,7 +39,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """Unsupported Content @@ -60,7 +62,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, ) -> Response[Any]: """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 9f1864ec3..6339d61f3 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, Optional, Union +from typing import Any import httpx @@ -21,24 +21,26 @@ def _get_kwargs( "url": "/tests/upload", } - _body = body.to_multipart() + _kwargs["files"] = body.to_multipart() - _kwargs["files"] = _body + headers["Content-Type"] = "multipart/form-data; boundary=+++" _kwargs["headers"] = headers return _kwargs def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Any | HTTPValidationError | None: if response.status_code == 200: response_200 = response.json() return response_200 + if response.status_code == 422: response_422 = HTTPValidationError.from_dict(response.json()) return response_422 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -46,8 +48,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Any, HTTPValidationError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[Any | HTTPValidationError]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -58,9 +60,9 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: BodyUploadFileTestsUploadPost, -) -> Response[Union[Any, HTTPValidationError]]: +) -> Response[Any | HTTPValidationError]: """Upload File Upload a file @@ -73,7 +75,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -89,9 +91,9 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: BodyUploadFileTestsUploadPost, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Any | HTTPValidationError | None: """Upload File Upload a file @@ -104,7 +106,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return sync_detailed( @@ -115,9 +117,9 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: BodyUploadFileTestsUploadPost, -) -> Response[Union[Any, HTTPValidationError]]: +) -> Response[Any | HTTPValidationError]: """Upload File Upload a file @@ -130,7 +132,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Any, HTTPValidationError]] + Response[Any | HTTPValidationError] """ kwargs = _get_kwargs( @@ -144,9 +146,9 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: BodyUploadFileTestsUploadPost, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Any | HTTPValidationError | None: """Upload File Upload a file @@ -159,7 +161,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Any, HTTPValidationError] + Any | HTTPValidationError """ return ( 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 b46550153..d7713f03f 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, Optional, Union +from typing import Any import httpx @@ -12,6 +12,7 @@ def _get_kwargs( *, import_: str, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["import"] = import_ @@ -27,16 +28,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -47,7 +49,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, import_: str, ) -> Response[Any]: """ @@ -75,7 +77,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, import_: str, ) -> Response[Any]: """ 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 e80446f10..edb246d7e 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, Optional, Union +from typing import Any, Self import httpx from attrs import define, evolve, field @@ -38,12 +38,12 @@ class Client: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" @@ -62,14 +62,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "Client": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "Client": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "Client": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -91,7 +91,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "Client": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -100,8 +100,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -122,7 +122,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "Client": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self @@ -168,12 +168,12 @@ class AuthenticatedClient: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) token: str prefix: str = "Bearer" @@ -196,14 +196,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -226,7 +226,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "AuthenticatedClient": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -235,8 +235,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -258,7 +258,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "AuthenticatedClient": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self 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..c62e4cfa6 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 @@ -7,6 +7,8 @@ from .a_model_with_properties_reference_that_are_not_object import AModelWithPropertiesReferenceThatAreNotObject from .all_of_has_properties_but_no_type import AllOfHasPropertiesButNoType from .all_of_has_properties_but_no_type_type_enum import AllOfHasPropertiesButNoTypeTypeEnum +from .all_of_required_base import AllOfRequiredBase +from .all_of_required_derived import AllOfRequiredDerived from .all_of_sub_model import AllOfSubModel from .all_of_sub_model_type_enum import AllOfSubModelTypeEnum from .an_all_of_enum import AnAllOfEnum @@ -72,9 +74,11 @@ from .model_with_recursive_ref_in_additional_properties import ModelWithRecursiveRefInAdditionalProperties from .model_with_union_property import ModelWithUnionProperty from .model_with_union_property_inlined import ModelWithUnionPropertyInlined -from .model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0 -from .model_with_union_property_inlined_fruit_type_1 import ModelWithUnionPropertyInlinedFruitType1 +from .model_with_union_property_inlined_apples import ModelWithUnionPropertyInlinedApples +from .model_with_union_property_inlined_bananas import ModelWithUnionPropertyInlinedBananas from .none import None_ +from .octet_stream_tests_octet_stream_post_response_200 import OctetStreamTestsOctetStreamPostResponse200 +from .optional_body_body import OptionalBodyBody from .post_bodies_multiple_data_body import PostBodiesMultipleDataBody from .post_bodies_multiple_files_body import PostBodiesMultipleFilesBody from .post_bodies_multiple_json_body import PostBodiesMultipleJsonBody @@ -85,6 +89,9 @@ from .post_responses_unions_simple_before_complex_response_200a_type_1 import ( PostResponsesUnionsSimpleBeforeComplexResponse200AType1, ) +from .status_code_patterns_response_2xx import StatusCodePatternsResponse2XX +from .status_code_patterns_response_2xx_status import StatusCodePatternsResponse2XXStatus +from .status_code_patterns_response_4xx import StatusCodePatternsResponse4XX from .test_inline_objects_body import TestInlineObjectsBody from .test_inline_objects_response_200 import TestInlineObjectsResponse200 from .validation_error import ValidationError @@ -95,6 +102,8 @@ "AFormData", "AllOfHasPropertiesButNoType", "AllOfHasPropertiesButNoTypeTypeEnum", + "AllOfRequiredBase", + "AllOfRequiredDerived", "AllOfSubModel", "AllOfSubModelTypeEnum", "AModel", @@ -154,9 +163,11 @@ "ModelWithRecursiveRefInAdditionalProperties", "ModelWithUnionProperty", "ModelWithUnionPropertyInlined", - "ModelWithUnionPropertyInlinedFruitType0", - "ModelWithUnionPropertyInlinedFruitType1", + "ModelWithUnionPropertyInlinedApples", + "ModelWithUnionPropertyInlinedBananas", "None_", + "OctetStreamTestsOctetStreamPostResponse200", + "OptionalBodyBody", "PostBodiesMultipleDataBody", "PostBodiesMultipleFilesBody", "PostBodiesMultipleJsonBody", @@ -165,6 +176,9 @@ "PostNamingPropertyConflictWithImportResponse200", "PostResponsesUnionsSimpleBeforeComplexResponse200", "PostResponsesUnionsSimpleBeforeComplexResponse200AType1", + "StatusCodePatternsResponse2XX", + "StatusCodePatternsResponse2XXStatus", + "StatusCodePatternsResponse4XX", "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 index bc18e6472..635661b7b 100644 --- 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 @@ -1,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class ADiscriminatedUnionType1: """ Attributes: - model_type (Union[Unset, str]): + model_type (str | Unset): """ - model_type: Union[Unset, str] = UNSET + model_type: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: 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 index 79ef64c81..0797af899 100644 --- 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 @@ -1,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class ADiscriminatedUnionType2: """ Attributes: - model_type (Union[Unset, str]): + model_type (str | Unset): """ - model_type: Union[Unset, str] = UNSET + model_type: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: 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 63a652054..257054eb2 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,11 +16,11 @@ class AFormData: """ Attributes: an_required_field (str): - an_optional_field (Union[Unset, str]): + an_optional_field (str | Unset): """ an_required_field: str - an_optional_field: Union[Unset, str] = UNSET + an_optional_field: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 5a9ba85ae..4ca83858f 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,10 +1,11 @@ +from __future__ import annotations + import datetime from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from uuid import UUID from attrs import define as _attrs_define -from dateutil.parser import isoparse from ..models.an_all_of_enum import AnAllOfEnum from ..models.an_enum import AnEnum @@ -26,62 +27,62 @@ class AModel: Attributes: an_enum_value (AnEnum): For testing Enums in all the ways they can be used an_allof_enum_with_overridden_default (AnAllOfEnum): Default: AnAllOfEnum.OVERRIDDEN_DEFAULT. - a_camel_date_time (Union[datetime.date, datetime.datetime]): + a_camel_date_time (datetime.date | datetime.datetime): a_date (datetime.date): - a_nullable_date (Union[None, datetime.date]): + a_nullable_date (datetime.date | None): a_uuid (UUID): - a_nullable_uuid (Union[None, UUID]): Default: UUID('07EF8B4D-AA09-4FFA-898D-C710796AFF41'). - required_nullable (Union[None, str]): + a_nullable_uuid (None | UUID): Default: UUID('07EF8B4D-AA09-4FFA-898D-C710796AFF41'). + required_nullable (None | str): required_not_nullable (str): - one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', Any]): - nullable_one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', None]): + one_of_models (Any | FreeFormModel | ModelWithUnionProperty): + nullable_one_of_models (FreeFormModel | ModelWithUnionProperty | None): model (ModelWithUnionProperty): - 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]]]): - a_not_required_date (Union[Unset, datetime.date]): - a_not_required_uuid (Union[Unset, UUID]): - attr_1_leading_digit (Union[Unset, str]): - attr_leading_underscore (Union[Unset, str]): - not_required_nullable (Union[None, Unset, str]): - not_required_not_nullable (Union[Unset, str]): - not_required_one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', Unset]): - not_required_nullable_one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', None, Unset, str]): - not_required_model (Union[Unset, ModelWithUnionProperty]): - not_required_nullable_model (Union['ModelWithUnionProperty', None, Unset]): + nullable_model (ModelWithUnionProperty | None): + any_value (Any | Unset): Default: 'default'. + an_optional_allof_enum (AnAllOfEnum | Unset): + nested_list_of_enums (list[list[DifferentEnum]] | Unset): + a_not_required_date (datetime.date | Unset): + a_not_required_uuid (UUID | Unset): + attr_1_leading_digit (str | Unset): + attr_leading_underscore (str | Unset): + not_required_nullable (None | str | Unset): + not_required_not_nullable (str | Unset): + not_required_one_of_models (FreeFormModel | ModelWithUnionProperty | Unset): + not_required_nullable_one_of_models (FreeFormModel | ModelWithUnionProperty | None | str | Unset): + not_required_model (ModelWithUnionProperty | Unset): + not_required_nullable_model (ModelWithUnionProperty | None | Unset): """ an_enum_value: AnEnum - a_camel_date_time: Union[datetime.date, datetime.datetime] + a_camel_date_time: datetime.date | datetime.datetime a_date: datetime.date - a_nullable_date: Union[None, datetime.date] + a_nullable_date: datetime.date | None a_uuid: UUID - required_nullable: Union[None, str] + required_nullable: None | str required_not_nullable: str - one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", Any] - nullable_one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", None] - model: "ModelWithUnionProperty" - nullable_model: Union["ModelWithUnionProperty", None] + one_of_models: Any | FreeFormModel | ModelWithUnionProperty + nullable_one_of_models: FreeFormModel | ModelWithUnionProperty | None + model: ModelWithUnionProperty + nullable_model: ModelWithUnionProperty | None an_allof_enum_with_overridden_default: AnAllOfEnum = AnAllOfEnum.OVERRIDDEN_DEFAULT - 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 - 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 - attr_leading_underscore: Union[Unset, str] = UNSET - not_required_nullable: Union[None, Unset, str] = UNSET - not_required_not_nullable: Union[Unset, str] = UNSET - not_required_one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", Unset] = UNSET - not_required_nullable_one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", None, Unset, str] = UNSET - not_required_model: Union[Unset, "ModelWithUnionProperty"] = UNSET - not_required_nullable_model: Union["ModelWithUnionProperty", None, Unset] = UNSET + a_nullable_uuid: None | UUID = UUID("07EF8B4D-AA09-4FFA-898D-C710796AFF41") + any_value: Any | Unset = "default" + an_optional_allof_enum: AnAllOfEnum | Unset = UNSET + nested_list_of_enums: list[list[DifferentEnum]] | Unset = UNSET + a_not_required_date: datetime.date | Unset = UNSET + a_not_required_uuid: UUID | Unset = UNSET + attr_1_leading_digit: str | Unset = UNSET + attr_leading_underscore: str | Unset = UNSET + not_required_nullable: None | str | Unset = UNSET + not_required_not_nullable: str | Unset = UNSET + not_required_one_of_models: FreeFormModel | ModelWithUnionProperty | Unset = UNSET + not_required_nullable_one_of_models: FreeFormModel | ModelWithUnionProperty | None | str | Unset = UNSET + not_required_model: ModelWithUnionProperty | Unset = UNSET + not_required_nullable_model: ModelWithUnionProperty | None | Unset = UNSET def to_dict(self) -> dict[str, Any]: - from ..models.free_form_model import FreeFormModel - from ..models.model_with_union_property import ModelWithUnionProperty + from ..models.free_form_model import FreeFormModel # noqa: PLC0415 + from ..models.model_with_union_property import ModelWithUnionProperty # noqa: PLC0415 an_enum_value = self.an_enum_value.value @@ -95,7 +96,7 @@ def to_dict(self) -> dict[str, Any]: a_date = self.a_date.isoformat() - a_nullable_date: Union[None, str] + a_nullable_date: None | str if isinstance(self.a_nullable_date, datetime.date): a_nullable_date = self.a_nullable_date.isoformat() else: @@ -103,18 +104,18 @@ def to_dict(self) -> dict[str, Any]: a_uuid = str(self.a_uuid) - a_nullable_uuid: Union[None, str] + a_nullable_uuid: None | str if isinstance(self.a_nullable_uuid, UUID): a_nullable_uuid = str(self.a_nullable_uuid) else: a_nullable_uuid = self.a_nullable_uuid - required_nullable: Union[None, str] + required_nullable: None | str required_nullable = self.required_nullable required_not_nullable = self.required_not_nullable - one_of_models: Union[Any, dict[str, Any]] + one_of_models: 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): @@ -122,7 +123,7 @@ def to_dict(self) -> dict[str, Any]: else: one_of_models = self.one_of_models - nullable_one_of_models: Union[None, dict[str, Any]] + nullable_one_of_models: dict[str, Any] | None 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): @@ -132,7 +133,7 @@ def to_dict(self) -> dict[str, Any]: model = self.model.to_dict() - nullable_model: Union[None, dict[str, Any]] + nullable_model: dict[str, Any] | None if isinstance(self.nullable_model, ModelWithUnionProperty): nullable_model = self.nullable_model.to_dict() else: @@ -140,11 +141,11 @@ def to_dict(self) -> dict[str, Any]: any_value = self.any_value - an_optional_allof_enum: Union[Unset, str] = UNSET + an_optional_allof_enum: str | Unset = UNSET 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: list[list[str]] | Unset = 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: @@ -155,11 +156,11 @@ def to_dict(self) -> dict[str, Any]: nested_list_of_enums.append(nested_list_of_enums_item) - a_not_required_date: Union[Unset, str] = UNSET + a_not_required_date: str | Unset = UNSET if not isinstance(self.a_not_required_date, Unset): a_not_required_date = self.a_not_required_date.isoformat() - a_not_required_uuid: Union[Unset, str] = UNSET + a_not_required_uuid: str | Unset = UNSET if not isinstance(self.a_not_required_uuid, Unset): a_not_required_uuid = str(self.a_not_required_uuid) @@ -167,7 +168,7 @@ def to_dict(self) -> dict[str, Any]: attr_leading_underscore = self.attr_leading_underscore - not_required_nullable: Union[None, Unset, str] + not_required_nullable: None | str | Unset if isinstance(self.not_required_nullable, Unset): not_required_nullable = UNSET else: @@ -175,7 +176,7 @@ def to_dict(self) -> dict[str, Any]: not_required_not_nullable = self.not_required_not_nullable - not_required_one_of_models: Union[Unset, dict[str, Any]] + not_required_one_of_models: dict[str, Any] | Unset if isinstance(self.not_required_one_of_models, Unset): not_required_one_of_models = UNSET elif isinstance(self.not_required_one_of_models, FreeFormModel): @@ -183,7 +184,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[None, Unset, dict[str, Any], str] + not_required_nullable_one_of_models: dict[str, Any] | None | str | Unset 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): @@ -193,11 +194,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: dict[str, Any] | Unset = UNSET if not isinstance(self.not_required_model, Unset): not_required_model = self.not_required_model.to_dict() - not_required_nullable_model: Union[None, Unset, dict[str, Any]] + not_required_nullable_model: dict[str, Any] | None | Unset if isinstance(self.not_required_nullable_model, Unset): not_required_nullable_model = UNSET elif isinstance(self.not_required_nullable_model, ModelWithUnionProperty): @@ -206,6 +207,7 @@ def to_dict(self) -> dict[str, Any]: not_required_nullable_model = self.not_required_nullable_model field_dict: dict[str, Any] = {} + field_dict.update( { "an_enum_value": an_enum_value, @@ -254,51 +256,51 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.free_form_model import FreeFormModel - from ..models.model_with_union_property import ModelWithUnionProperty + from ..models.free_form_model import FreeFormModel # noqa: PLC0415 + from ..models.model_with_union_property import ModelWithUnionProperty # noqa: PLC0415 d = dict(src_dict) an_enum_value = AnEnum(d.pop("an_enum_value")) an_allof_enum_with_overridden_default = AnAllOfEnum(d.pop("an_allof_enum_with_overridden_default")) - def _parse_a_camel_date_time(data: object) -> Union[datetime.date, datetime.datetime]: + def _parse_a_camel_date_time(data: object) -> datetime.date | datetime.datetime: try: if not isinstance(data, str): raise TypeError() - a_camel_date_time_type_0 = isoparse(data) + a_camel_date_time_type_0 = datetime.datetime.fromisoformat(data) return a_camel_date_time_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass if not isinstance(data, str): raise TypeError() - a_camel_date_time_type_1 = isoparse(data).date() + a_camel_date_time_type_1 = datetime.date.fromisoformat(data) return a_camel_date_time_type_1 a_camel_date_time = _parse_a_camel_date_time(d.pop("aCamelDateTime")) - a_date = isoparse(d.pop("a_date")).date() + a_date = datetime.date.fromisoformat(d.pop("a_date")) - def _parse_a_nullable_date(data: object) -> Union[None, datetime.date]: + def _parse_a_nullable_date(data: object) -> datetime.date | None: if data is None: return data try: if not isinstance(data, str): raise TypeError() - a_nullable_date_type_0 = isoparse(data).date() + a_nullable_date_type_0 = datetime.date.fromisoformat(data) return a_nullable_date_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union[None, datetime.date], data) + return cast(datetime.date | None, data) a_nullable_date = _parse_a_nullable_date(d.pop("a_nullable_date")) a_uuid = UUID(d.pop("a_uuid")) - def _parse_a_nullable_uuid(data: object) -> Union[None, UUID]: + def _parse_a_nullable_uuid(data: object) -> None | UUID: if data is None: return data try: @@ -307,29 +309,29 @@ def _parse_a_nullable_uuid(data: object) -> Union[None, UUID]: a_nullable_uuid_type_0 = UUID(data) return a_nullable_uuid_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union[None, UUID], data) + return cast(None | UUID, data) a_nullable_uuid = _parse_a_nullable_uuid(d.pop("a_nullable_uuid")) - def _parse_required_nullable(data: object) -> Union[None, str]: + def _parse_required_nullable(data: object) -> None | str: if data is None: return data - return cast(Union[None, str], data) + return cast(None | str, data) required_nullable = _parse_required_nullable(d.pop("required_nullable")) required_not_nullable = d.pop("required_not_nullable") - def _parse_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnionProperty", Any]: + def _parse_one_of_models(data: object) -> Any | FreeFormModel | ModelWithUnionProperty: try: if not isinstance(data, dict): raise TypeError() one_of_models_type_0 = FreeFormModel.from_dict(data) return one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, dict): @@ -337,13 +339,13 @@ def _parse_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnion one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return one_of_models_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["FreeFormModel", "ModelWithUnionProperty", Any], data) + return cast(Any | FreeFormModel | ModelWithUnionProperty, data) one_of_models = _parse_one_of_models(d.pop("one_of_models")) - def _parse_nullable_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnionProperty", None]: + def _parse_nullable_one_of_models(data: object) -> FreeFormModel | ModelWithUnionProperty | None: if data is None: return data try: @@ -352,7 +354,7 @@ def _parse_nullable_one_of_models(data: object) -> Union["FreeFormModel", "Model nullable_one_of_models_type_0 = FreeFormModel.from_dict(data) return nullable_one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, dict): @@ -360,15 +362,15 @@ def _parse_nullable_one_of_models(data: object) -> Union["FreeFormModel", "Model nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return nullable_one_of_models_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["FreeFormModel", "ModelWithUnionProperty", None], data) + return cast(FreeFormModel | ModelWithUnionProperty | None, data) nullable_one_of_models = _parse_nullable_one_of_models(d.pop("nullable_one_of_models")) model = ModelWithUnionProperty.from_dict(d.pop("model")) - def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None]: + def _parse_nullable_model(data: object) -> ModelWithUnionProperty | None: if data is None: return data try: @@ -377,42 +379,44 @@ def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None] nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) return nullable_model_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["ModelWithUnionProperty", None], data) + return cast(ModelWithUnionProperty | None, data) nullable_model = _parse_nullable_model(d.pop("nullable_model")) any_value = d.pop("any_value", UNSET) _an_optional_allof_enum = d.pop("an_optional_allof_enum", UNSET) - an_optional_allof_enum: Union[Unset, AnAllOfEnum] + an_optional_allof_enum: AnAllOfEnum | Unset if isinstance(_an_optional_allof_enum, Unset): an_optional_allof_enum = UNSET else: an_optional_allof_enum = AnAllOfEnum(_an_optional_allof_enum) - nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) - for nested_list_of_enums_item_data in _nested_list_of_enums or []: - nested_list_of_enums_item = [] - _nested_list_of_enums_item = nested_list_of_enums_item_data - for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: - nested_list_of_enums_item_item = DifferentEnum(nested_list_of_enums_item_item_data) + nested_list_of_enums: list[list[DifferentEnum]] | Unset = UNSET + if _nested_list_of_enums is not UNSET: + nested_list_of_enums = [] + for nested_list_of_enums_item_data in _nested_list_of_enums: + nested_list_of_enums_item = [] + _nested_list_of_enums_item = nested_list_of_enums_item_data + for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: + nested_list_of_enums_item_item = DifferentEnum(nested_list_of_enums_item_item_data) - nested_list_of_enums_item.append(nested_list_of_enums_item_item) + nested_list_of_enums_item.append(nested_list_of_enums_item_item) - nested_list_of_enums.append(nested_list_of_enums_item) + nested_list_of_enums.append(nested_list_of_enums_item) _a_not_required_date = d.pop("a_not_required_date", UNSET) - a_not_required_date: Union[Unset, datetime.date] + a_not_required_date: datetime.date | Unset if isinstance(_a_not_required_date, Unset): a_not_required_date = UNSET else: - a_not_required_date = isoparse(_a_not_required_date).date() + a_not_required_date = datetime.date.fromisoformat(_a_not_required_date) _a_not_required_uuid = d.pop("a_not_required_uuid", UNSET) - a_not_required_uuid: Union[Unset, UUID] + a_not_required_uuid: UUID | Unset if isinstance(_a_not_required_uuid, Unset): a_not_required_uuid = UNSET else: @@ -422,18 +426,18 @@ def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None] attr_leading_underscore = d.pop("_leading_underscore", UNSET) - def _parse_not_required_nullable(data: object) -> Union[None, Unset, str]: + def _parse_not_required_nullable(data: object) -> None | str | Unset: if data is None: return data if isinstance(data, Unset): return data - return cast(Union[None, Unset, str], data) + return cast(None | str | Unset, data) not_required_nullable = _parse_not_required_nullable(d.pop("not_required_nullable", UNSET)) not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnionProperty", Unset]: + def _parse_not_required_one_of_models(data: object) -> FreeFormModel | ModelWithUnionProperty | Unset: if isinstance(data, Unset): return data try: @@ -442,7 +446,7 @@ def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "M not_required_one_of_models_type_0 = FreeFormModel.from_dict(data) return not_required_one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass if not isinstance(data, dict): raise TypeError() @@ -454,7 +458,7 @@ def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "M def _parse_not_required_nullable_one_of_models( data: object, - ) -> Union["FreeFormModel", "ModelWithUnionProperty", None, Unset, str]: + ) -> FreeFormModel | ModelWithUnionProperty | None | str | Unset: if data is None: return data if isinstance(data, Unset): @@ -465,7 +469,7 @@ def _parse_not_required_nullable_one_of_models( not_required_nullable_one_of_models_type_0 = FreeFormModel.from_dict(data) return not_required_nullable_one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, dict): @@ -473,22 +477,22 @@ def _parse_not_required_nullable_one_of_models( not_required_nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return not_required_nullable_one_of_models_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["FreeFormModel", "ModelWithUnionProperty", None, Unset, str], data) + return cast(FreeFormModel | ModelWithUnionProperty | None | str | Unset, data) not_required_nullable_one_of_models = _parse_not_required_nullable_one_of_models( d.pop("not_required_nullable_one_of_models", UNSET) ) _not_required_model = d.pop("not_required_model", UNSET) - not_required_model: Union[Unset, ModelWithUnionProperty] + not_required_model: ModelWithUnionProperty | Unset if isinstance(_not_required_model, Unset): not_required_model = UNSET else: not_required_model = ModelWithUnionProperty.from_dict(_not_required_model) - def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionProperty", None, Unset]: + def _parse_not_required_nullable_model(data: object) -> ModelWithUnionProperty | None | Unset: if data is None: return data if isinstance(data, Unset): @@ -499,9 +503,9 @@ def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionPro not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) return not_required_nullable_model_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["ModelWithUnionProperty", None, Unset], data) + return cast(ModelWithUnionProperty | None | Unset, data) not_required_nullable_model = _parse_not_required_nullable_model(d.pop("not_required_nullable_model", UNSET)) 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 2d165b50e..4c31bf01b 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,3 +1,5 @@ +from __future__ import annotations + import datetime from collections.abc import Mapping from io import BytesIO @@ -5,7 +7,6 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field -from dateutil.parser import isoparse from ..models.an_enum import AnEnum from ..types import File @@ -228,16 +229,16 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: date_properties_ref = [] _date_properties_ref = d.pop("date_properties_ref") for componentsschemas_an_other_array_of_date_item_data in _date_properties_ref: - componentsschemas_an_other_array_of_date_item = isoparse( + componentsschemas_an_other_array_of_date_item = datetime.date.fromisoformat( componentsschemas_an_other_array_of_date_item_data - ).date() + ) date_properties_ref.append(componentsschemas_an_other_array_of_date_item) datetime_properties_ref = [] _datetime_properties_ref = d.pop("datetime_properties_ref") for componentsschemas_an_other_array_of_date_time_item_data in _datetime_properties_ref: - componentsschemas_an_other_array_of_date_time_item = isoparse( + componentsschemas_an_other_array_of_date_time_item = datetime.datetime.fromisoformat( componentsschemas_an_other_array_of_date_time_item_data ) @@ -274,14 +275,18 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: date_properties = [] _date_properties = d.pop("date_properties") for componentsschemas_an_array_of_date_item_data in _date_properties: - componentsschemas_an_array_of_date_item = isoparse(componentsschemas_an_array_of_date_item_data).date() + componentsschemas_an_array_of_date_item = datetime.date.fromisoformat( + componentsschemas_an_array_of_date_item_data + ) date_properties.append(componentsschemas_an_array_of_date_item) datetime_properties = [] _datetime_properties = d.pop("datetime_properties") for componentsschemas_an_array_of_date_time_item_data in _datetime_properties: - componentsschemas_an_array_of_date_time_item = isoparse(componentsschemas_an_array_of_date_time_item_data) + componentsschemas_an_array_of_date_time_item = datetime.datetime.fromisoformat( + componentsschemas_an_array_of_date_time_item_data + ) datetime_properties.append(componentsschemas_an_array_of_date_time_item) @@ -308,9 +313,9 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: str_property_ref = d.pop("str_property_ref") - date_property_ref = isoparse(d.pop("date_property_ref")).date() + date_property_ref = datetime.date.fromisoformat(d.pop("date_property_ref")) - datetime_property_ref = isoparse(d.pop("datetime_property_ref")) + datetime_property_ref = datetime.datetime.fromisoformat(d.pop("datetime_property_ref")) int32_property_ref = d.pop("int32_property_ref") 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 7ff816bd4..3295dcf26 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,14 +16,14 @@ class AllOfHasPropertiesButNoType: """ Attributes: - a_sub_property (Union[Unset, str]): - type_ (Union[Unset, str]): - type_enum (Union[Unset, AllOfHasPropertiesButNoTypeTypeEnum]): + a_sub_property (str | Unset): + type_ (str | Unset): + type_enum (AllOfHasPropertiesButNoTypeTypeEnum | Unset): """ - a_sub_property: Union[Unset, str] = UNSET - type_: Union[Unset, str] = UNSET - type_enum: Union[Unset, AllOfHasPropertiesButNoTypeTypeEnum] = UNSET + a_sub_property: str | Unset = UNSET + type_: str | Unset = UNSET + type_enum: AllOfHasPropertiesButNoTypeTypeEnum | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -29,7 +31,7 @@ def to_dict(self) -> dict[str, Any]: type_ = self.type_ - type_enum: Union[Unset, int] = UNSET + type_enum: int | Unset = UNSET if not isinstance(self.type_enum, Unset): type_enum = self.type_enum.value @@ -53,7 +55,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: type_ = d.pop("type", UNSET) _type_enum = d.pop("type_enum", UNSET) - type_enum: Union[Unset, AllOfHasPropertiesButNoTypeTypeEnum] + type_enum: AllOfHasPropertiesButNoTypeTypeEnum | Unset if isinstance(_type_enum, Unset): type_enum = UNSET else: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/all_of_required_base.py b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_required_base.py new file mode 100644 index 000000000..b06f45c30 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_required_base.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="AllOfRequiredBase") + + +@_attrs_define +class AllOfRequiredBase: + """ + Attributes: + bar (str | Unset): The bar property + baz (str | Unset): The baz property + """ + + bar: str | Unset = UNSET + baz: str | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + bar = self.bar + + baz = self.baz + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if bar is not UNSET: + field_dict["bar"] = bar + if baz is not UNSET: + field_dict["baz"] = baz + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + bar = d.pop("bar", UNSET) + + baz = d.pop("baz", UNSET) + + all_of_required_base = cls( + bar=bar, + baz=baz, + ) + + all_of_required_base.additional_properties = d + return all_of_required_base + + @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/all_of_required_derived.py b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_required_derived.py new file mode 100644 index 000000000..505334d6b --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_required_derived.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="AllOfRequiredDerived") + + +@_attrs_define +class AllOfRequiredDerived: + """ + Attributes: + bar (str): The bar property + baz (str | Unset): The baz property + """ + + bar: str + baz: str | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + bar = self.bar + + baz = self.baz + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "bar": bar, + } + ) + if baz is not UNSET: + field_dict["baz"] = baz + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + bar = d.pop("bar") + + baz = d.pop("baz", UNSET) + + all_of_required_derived = cls( + bar=bar, + baz=baz, + ) + + all_of_required_derived.additional_properties = d + return all_of_required_derived + + @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/all_of_sub_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/all_of_sub_model.py index d7b1deb90..65b6f97c6 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,14 +16,14 @@ class AllOfSubModel: """ Attributes: - a_sub_property (Union[Unset, str]): - type_ (Union[Unset, str]): - type_enum (Union[Unset, AllOfSubModelTypeEnum]): + a_sub_property (str | Unset): + type_ (str | Unset): + type_enum (AllOfSubModelTypeEnum | Unset): """ - a_sub_property: Union[Unset, str] = UNSET - type_: Union[Unset, str] = UNSET - type_enum: Union[Unset, AllOfSubModelTypeEnum] = UNSET + a_sub_property: str | Unset = UNSET + type_: str | Unset = UNSET + type_enum: AllOfSubModelTypeEnum | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -29,7 +31,7 @@ def to_dict(self) -> dict[str, Any]: type_ = self.type_ - type_enum: Union[Unset, int] = UNSET + type_enum: int | Unset = UNSET if not isinstance(self.type_enum, Unset): type_enum = self.type_enum.value @@ -53,7 +55,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: type_ = d.pop("type", UNSET) _type_enum = d.pop("type_enum", UNSET) - type_enum: Union[Unset, AllOfSubModelTypeEnum] + type_enum: AllOfSubModelTypeEnum | Unset if isinstance(_type_enum, Unset): type_enum = UNSET else: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_all_of_enum.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_all_of_enum.py index 3aef48f8f..1439e1781 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_all_of_enum.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_all_of_enum.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class AnAllOfEnum(str, Enum): +class AnAllOfEnum(StrEnum): A_DEFAULT = "a_default" BAR = "bar" FOO = "foo" 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 54c4da080..77453bf2c 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,14 +19,14 @@ class AnArrayWithACircularRefInItemsObjectAItem: """ Attributes: - circular (Union[Unset, list['AnArrayWithACircularRefInItemsObjectBItem']]): + circular (list[AnArrayWithACircularRefInItemsObjectBItem] | Unset): """ - circular: Union[Unset, list["AnArrayWithACircularRefInItemsObjectBItem"]] = UNSET + circular: list[AnArrayWithACircularRefInItemsObjectBItem] | Unset = 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 + circular: list[dict[str, Any]] | Unset = 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: @@ -44,20 +46,22 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_b_item import ( - AnArrayWithACircularRefInItemsObjectBItem, + AnArrayWithACircularRefInItemsObjectBItem, # noqa: PLC0415 ) d = dict(src_dict) - circular = [] _circular = d.pop("circular", UNSET) - for componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item_data in _circular or []: - componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item = ( - AnArrayWithACircularRefInItemsObjectBItem.from_dict( - componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item_data + circular: list[AnArrayWithACircularRefInItemsObjectBItem] | Unset = UNSET + if _circular is not UNSET: + circular = [] + for componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item_data in _circular: + componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item = ( + AnArrayWithACircularRefInItemsObjectBItem.from_dict( + componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item_data + ) ) - ) - circular.append(componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item) + circular.append(componentsschemas_an_array_with_a_circular_ref_in_items_object_b_item) an_array_with_a_circular_ref_in_items_object_a_item = cls( circular=circular, 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 70aa27507..a8a735b44 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import TYPE_CHECKING, Any, TypeVar @@ -17,11 +19,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] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = [] @@ -38,7 +41,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_additional_properties_b_item import ( - AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem, + AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem, # noqa: PLC0415 ) d = dict(src_dict) @@ -72,12 +75,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: 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"] - ) -> None: + def __setitem__(self, key: str, value: list[AnArrayWithACircularRefInItemsObjectAdditionalPropertiesBItem]) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 119557650..f7d6bcb21 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import TYPE_CHECKING, Any, TypeVar @@ -17,11 +19,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] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = [] @@ -38,7 +41,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_additional_properties_a_item import ( - AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem, + AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem, # noqa: PLC0415 ) d = dict(src_dict) @@ -72,12 +75,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: 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"] - ) -> None: + def __setitem__(self, key: str, value: list[AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem]) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 e9b891737..e4039d6be 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,14 +19,14 @@ class AnArrayWithACircularRefInItemsObjectBItem: """ Attributes: - circular (Union[Unset, list['AnArrayWithACircularRefInItemsObjectAItem']]): + circular (list[AnArrayWithACircularRefInItemsObjectAItem] | Unset): """ - circular: Union[Unset, list["AnArrayWithACircularRefInItemsObjectAItem"]] = UNSET + circular: list[AnArrayWithACircularRefInItemsObjectAItem] | Unset = 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 + circular: list[dict[str, Any]] | Unset = 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: @@ -44,20 +46,22 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.an_array_with_a_circular_ref_in_items_object_a_item import ( - AnArrayWithACircularRefInItemsObjectAItem, + AnArrayWithACircularRefInItemsObjectAItem, # noqa: PLC0415 ) d = dict(src_dict) - circular = [] _circular = d.pop("circular", UNSET) - for componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item_data in _circular or []: - componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item = ( - AnArrayWithACircularRefInItemsObjectAItem.from_dict( - componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item_data + circular: list[AnArrayWithACircularRefInItemsObjectAItem] | Unset = UNSET + if _circular is not UNSET: + circular = [] + for componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item_data in _circular: + componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item = ( + AnArrayWithACircularRefInItemsObjectAItem.from_dict( + componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item_data + ) ) - ) - circular.append(componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item) + circular.append(componentsschemas_an_array_with_a_circular_ref_in_items_object_a_item) an_array_with_a_circular_ref_in_items_object_b_item = cls( circular=circular, 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 262617c7a..d91654029 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -11,11 +13,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] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = [] @@ -60,12 +63,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: 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"] - ) -> None: + def __setitem__(self, key: str, value: list[AnArrayWithARecursiveRefInItemsObjectAdditionalPropertiesItem]) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 792994018..581a9f622 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,14 +15,14 @@ class AnArrayWithARecursiveRefInItemsObjectItem: """ Attributes: - recursive (Union[Unset, list['AnArrayWithARecursiveRefInItemsObjectItem']]): + recursive (list[AnArrayWithARecursiveRefInItemsObjectItem] | Unset): """ - recursive: Union[Unset, list["AnArrayWithARecursiveRefInItemsObjectItem"]] = UNSET + recursive: list[AnArrayWithARecursiveRefInItemsObjectItem] | Unset = 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 + recursive: list[dict[str, Any]] | Unset = UNSET if not isinstance(self.recursive, Unset): recursive = [] for componentsschemas_an_array_with_a_recursive_ref_in_items_object_item_data in self.recursive: @@ -40,16 +42,18 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) - recursive = [] _recursive = d.pop("recursive", UNSET) - for componentsschemas_an_array_with_a_recursive_ref_in_items_object_item_data in _recursive or []: - componentsschemas_an_array_with_a_recursive_ref_in_items_object_item = ( - AnArrayWithARecursiveRefInItemsObjectItem.from_dict( - componentsschemas_an_array_with_a_recursive_ref_in_items_object_item_data + recursive: list[AnArrayWithARecursiveRefInItemsObjectItem] | Unset = UNSET + if _recursive is not UNSET: + recursive = [] + for componentsschemas_an_array_with_a_recursive_ref_in_items_object_item_data in _recursive: + componentsschemas_an_array_with_a_recursive_ref_in_items_object_item = ( + AnArrayWithARecursiveRefInItemsObjectItem.from_dict( + componentsschemas_an_array_with_a_recursive_ref_in_items_object_item_data + ) ) - ) - recursive.append(componentsschemas_an_array_with_a_recursive_ref_in_items_object_item) + recursive.append(componentsschemas_an_array_with_a_recursive_ref_in_items_object_item) an_array_with_a_recursive_ref_in_items_object_item = cls( recursive=recursive, diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_enum.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_enum.py index c266d0763..fe9c407d8 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_enum.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_enum.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class AnEnum(str, Enum): +class AnEnum(StrEnum): FIRST_VALUE = "FIRST_VALUE" SECOND_VALUE = "SECOND_VALUE" diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/an_enum_with_null.py b/end_to_end_tests/golden-record/my_test_api_client/models/an_enum_with_null.py index b1d6611e0..002ee3667 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/an_enum_with_null.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/an_enum_with_null.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class AnEnumWithNull(str, Enum): +class AnEnumWithNull(StrEnum): FIRST_VALUE = "FIRST_VALUE" SECOND_VALUE = "SECOND_VALUE" 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 ab92e1498..7c58d1a9c 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -15,24 +17,24 @@ class AnotherAllOfSubModel: """ Attributes: - another_sub_property (Union[Unset, str]): - type_ (Union[Unset, AnotherAllOfSubModelType]): - type_enum (Union[Unset, AnotherAllOfSubModelTypeEnum]): + another_sub_property (str | Unset): + type_ (AnotherAllOfSubModelType | Unset): + type_enum (AnotherAllOfSubModelTypeEnum | Unset): """ - another_sub_property: Union[Unset, str] = UNSET - type_: Union[Unset, AnotherAllOfSubModelType] = UNSET - type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] = UNSET + another_sub_property: str | Unset = UNSET + type_: AnotherAllOfSubModelType | Unset = UNSET + type_enum: AnotherAllOfSubModelTypeEnum | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: another_sub_property = self.another_sub_property - type_: Union[Unset, str] = UNSET + type_: str | Unset = UNSET if not isinstance(self.type_, Unset): type_ = self.type_.value - type_enum: Union[Unset, int] = UNSET + type_enum: int | Unset = UNSET if not isinstance(self.type_enum, Unset): type_enum = self.type_enum.value @@ -54,14 +56,14 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: another_sub_property = d.pop("another_sub_property", UNSET) _type_ = d.pop("type", UNSET) - type_: Union[Unset, AnotherAllOfSubModelType] + type_: AnotherAllOfSubModelType | Unset if isinstance(_type_, Unset): type_ = UNSET else: type_ = AnotherAllOfSubModelType(_type_) _type_enum = d.pop("type_enum", UNSET) - type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] + type_enum: AnotherAllOfSubModelTypeEnum | Unset if isinstance(_type_enum, Unset): type_enum = UNSET else: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model_type.py b/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model_type.py index b2e82aa7c..61d3a0c57 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model_type.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/another_all_of_sub_model_type.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class AnotherAllOfSubModelType(str, Enum): +class AnotherAllOfSubModelType(StrEnum): SUBMODEL = "submodel" def __str__(self) -> str: 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 f5db27451..0b8caee2c 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,15 +1,17 @@ +from __future__ import annotations + import datetime import json from collections.abc import Mapping from io import BytesIO -from typing import TYPE_CHECKING, Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field -from dateutil.parser import isoparse +from .. import types from ..models.different_enum import DifferentEnum -from ..types import UNSET, File, FileJsonType, Unset +from ..types import UNSET, File, FileTypes, Unset if TYPE_CHECKING: from ..models.a_form_data import AFormData @@ -35,40 +37,40 @@ class BodyUploadFileTestsUploadPost: some_file (File): some_required_number (float): some_object (BodyUploadFileTestsUploadPostSomeObject): - some_nullable_object (Union['BodyUploadFileTestsUploadPostSomeNullableObject', None]): - some_optional_file (Union[Unset, File]): - some_string (Union[Unset, str]): Default: 'some_default_string'. - a_datetime (Union[Unset, datetime.datetime]): - 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[None, Unset, list['AFormData']]): - some_optional_object (Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject]): - some_enum (Union[Unset, DifferentEnum]): An enumeration. + some_nullable_object (BodyUploadFileTestsUploadPostSomeNullableObject | None): + some_optional_file (File | Unset): + some_string (str | Unset): Default: 'some_default_string'. + a_datetime (datetime.datetime | Unset): + a_date (datetime.date | Unset): + some_number (float | Unset): + some_nullable_number (float | None | Unset): + some_int_array (list[int | None] | Unset): + some_array (list[AFormData] | None | Unset): + some_optional_object (BodyUploadFileTestsUploadPostSomeOptionalObject | Unset): + some_enum (DifferentEnum | Unset): An enumeration. """ some_file: File some_required_number: float - some_object: "BodyUploadFileTestsUploadPostSomeObject" - some_nullable_object: Union["BodyUploadFileTestsUploadPostSomeNullableObject", None] - some_optional_file: Union[Unset, File] = UNSET - some_string: Union[Unset, str] = "some_default_string" - a_datetime: Union[Unset, datetime.datetime] = UNSET - 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[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( + some_object: BodyUploadFileTestsUploadPostSomeObject + some_nullable_object: BodyUploadFileTestsUploadPostSomeNullableObject | None + some_optional_file: File | Unset = UNSET + some_string: str | Unset = "some_default_string" + a_datetime: datetime.datetime | Unset = UNSET + a_date: datetime.date | Unset = UNSET + some_number: float | Unset = UNSET + some_nullable_number: float | None | Unset = UNSET + some_int_array: list[int | None] | Unset = UNSET + some_array: list[AFormData] | None | Unset = UNSET + some_optional_object: BodyUploadFileTestsUploadPostSomeOptionalObject | Unset = UNSET + some_enum: DifferentEnum | Unset = UNSET + additional_properties: dict[str, BodyUploadFileTestsUploadPostAdditionalProperty] = _attrs_field( init=False, factory=dict ) def to_dict(self) -> dict[str, Any]: from ..models.body_upload_file_tests_upload_post_some_nullable_object import ( - BodyUploadFileTestsUploadPostSomeNullableObject, + BodyUploadFileTestsUploadPostSomeNullableObject, # noqa: PLC0415 ) some_file = self.some_file.to_tuple() @@ -77,43 +79,43 @@ def to_dict(self) -> dict[str, Any]: some_object = self.some_object.to_dict() - some_nullable_object: Union[None, dict[str, Any]] + some_nullable_object: dict[str, Any] | None if isinstance(self.some_nullable_object, BodyUploadFileTestsUploadPostSomeNullableObject): some_nullable_object = self.some_nullable_object.to_dict() else: some_nullable_object = self.some_nullable_object - some_optional_file: Union[Unset, FileJsonType] = UNSET + some_optional_file: FileTypes | Unset = UNSET if not isinstance(self.some_optional_file, Unset): some_optional_file = self.some_optional_file.to_tuple() some_string = self.some_string - a_datetime: Union[Unset, str] = UNSET + a_datetime: str | Unset = UNSET if not isinstance(self.a_datetime, Unset): a_datetime = self.a_datetime.isoformat() - a_date: Union[Unset, str] = UNSET + a_date: str | Unset = UNSET if not isinstance(self.a_date, Unset): a_date = self.a_date.isoformat() some_number = self.some_number - some_nullable_number: Union[None, Unset, float] + some_nullable_number: float | None | Unset if isinstance(self.some_nullable_number, Unset): some_nullable_number = UNSET else: some_nullable_number = self.some_nullable_number - some_int_array: Union[Unset, list[Union[None, int]]] = UNSET + some_int_array: list[int | None] | Unset = UNSET if not isinstance(self.some_int_array, Unset): some_int_array = [] for some_int_array_item_data in self.some_int_array: - some_int_array_item: Union[None, int] + some_int_array_item: int | None some_int_array_item = some_int_array_item_data some_int_array.append(some_int_array_item) - some_array: Union[None, Unset, list[dict[str, Any]]] + some_array: list[dict[str, Any]] | None | Unset if isinstance(self.some_array, Unset): some_array = UNSET elif isinstance(self.some_array, list): @@ -125,17 +127,18 @@ 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: dict[str, Any] | Unset = UNSET if not isinstance(self.some_optional_object, Unset): some_optional_object = self.some_optional_object.to_dict() - some_enum: Union[Unset, str] = UNSET + some_enum: str | Unset = UNSET if not isinstance(self.some_enum, Unset): some_enum = self.some_enum.value field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() + field_dict.update( { "some_file": some_file, @@ -167,129 +170,99 @@ def to_dict(self) -> dict[str, Any]: return field_dict - def to_multipart(self) -> dict[str, Any]: - some_file = self.some_file.to_tuple() + def to_multipart(self) -> types.RequestFiles: + from ..models.body_upload_file_tests_upload_post_some_nullable_object import ( + BodyUploadFileTestsUploadPostSomeNullableObject, # noqa: PLC0415 + ) + + files: types.RequestFiles = [] - some_required_number = (None, str(self.some_required_number).encode(), "text/plain") + files.append(("some_file", self.some_file.to_tuple())) - some_object = (None, json.dumps(self.some_object.to_dict()).encode(), "application/json") + files.append(("some_required_number", (None, str(self.some_required_number).encode(), "text/plain"))) - some_nullable_object: tuple[None, bytes, str] + files.append(("some_object", (None, json.dumps(self.some_object.to_dict()).encode(), "application/json"))) if isinstance(self.some_nullable_object, BodyUploadFileTestsUploadPostSomeNullableObject): - some_nullable_object = (None, json.dumps(self.some_nullable_object.to_dict()).encode(), "application/json") + files.append( + ( + "some_nullable_object", + (None, json.dumps(self.some_nullable_object.to_dict()).encode(), "application/json"), + ) + ) else: - some_nullable_object = (None, str(self.some_nullable_object).encode(), "text/plain") + files.append(("some_nullable_object", (None, str(self.some_nullable_object).encode(), "text/plain"))) - some_optional_file: Union[Unset, FileJsonType] = UNSET if not isinstance(self.some_optional_file, Unset): - some_optional_file = self.some_optional_file.to_tuple() + files.append(("some_optional_file", self.some_optional_file.to_tuple())) - some_string = ( - self.some_string - if isinstance(self.some_string, Unset) - else (None, str(self.some_string).encode(), "text/plain") - ) + if not isinstance(self.some_string, Unset): + files.append(("some_string", (None, str(self.some_string).encode(), "text/plain"))) - a_datetime: Union[Unset, bytes] = UNSET if not isinstance(self.a_datetime, Unset): - a_datetime = self.a_datetime.isoformat().encode() + files.append(("a_datetime", (None, self.a_datetime.isoformat().encode(), "text/plain"))) - a_date: Union[Unset, bytes] = UNSET if not isinstance(self.a_date, Unset): - a_date = self.a_date.isoformat().encode() + files.append(("a_date", (None, self.a_date.isoformat().encode(), "text/plain"))) - some_number = ( - self.some_number - if isinstance(self.some_number, Unset) - else (None, str(self.some_number).encode(), "text/plain") - ) + if not isinstance(self.some_number, Unset): + files.append(("some_number", (None, str(self.some_number).encode(), "text/plain"))) - some_nullable_number: Union[Unset, tuple[None, bytes, str]] + if not isinstance(self.some_nullable_number, Unset): + if isinstance(self.some_nullable_number, float): + files.append(("some_nullable_number", (None, str(self.some_nullable_number).encode(), "text/plain"))) + else: + files.append(("some_nullable_number", (None, str(self.some_nullable_number).encode(), "text/plain"))) - if isinstance(self.some_nullable_number, Unset): - some_nullable_number = UNSET - elif isinstance(self.some_nullable_number, float): - some_nullable_number = (None, str(self.some_nullable_number).encode(), "text/plain") - else: - some_nullable_number = (None, str(self.some_nullable_number).encode(), "text/plain") - - 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: - some_int_array_item: Union[None, int] - some_int_array_item = some_int_array_item_data - _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[Unset, tuple[None, bytes, str]] - - if isinstance(self.some_array, Unset): - some_array = UNSET - elif isinstance(self.some_array, list): - _temp_some_array = [] - for some_array_type_0_item_data in self.some_array: - some_array_type_0_item = some_array_type_0_item_data.to_dict() - _temp_some_array.append(some_array_type_0_item) - some_array = (None, json.dumps(_temp_some_array).encode(), "application/json") - else: - some_array = (None, str(self.some_array).encode(), "text/plain") + for some_int_array_item_element in self.some_int_array: + if isinstance(some_int_array_item_element, int): + files.append(("some_int_array", (None, str(some_int_array_item_element).encode(), "text/plain"))) + else: + files.append(("some_int_array", (None, str(some_int_array_item_element).encode(), "text/plain"))) + + if not isinstance(self.some_array, Unset): + if isinstance(self.some_array, list): + for some_array_type_0_item_element in self.some_array: + files.append( + ( + "some_array", + (None, json.dumps(some_array_type_0_item_element.to_dict()).encode(), "application/json"), + ) + ) + else: + files.append(("some_array", (None, str(self.some_array).encode(), "text/plain"))) - 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") + files.append( + ( + "some_optional_object", + (None, json.dumps(self.some_optional_object.to_dict()).encode(), "application/json"), + ) + ) - 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") + files.append(("some_enum", (None, str(self.some_enum.value).encode(), "text/plain"))) - 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( - { - "some_file": some_file, - "some_required_number": some_required_number, - "some_object": some_object, - "some_nullable_object": some_nullable_object, - } - ) - if some_optional_file is not UNSET: - field_dict["some_optional_file"] = some_optional_file - if some_string is not UNSET: - field_dict["some_string"] = some_string - if a_datetime is not UNSET: - field_dict["a_datetime"] = a_datetime - if a_date is not UNSET: - field_dict["a_date"] = a_date - if some_number is not UNSET: - field_dict["some_number"] = some_number - if some_nullable_number is not UNSET: - field_dict["some_nullable_number"] = some_nullable_number - if some_int_array is not UNSET: - field_dict["some_int_array"] = some_int_array - if some_array is not UNSET: - field_dict["some_array"] = some_array - if some_optional_object is not UNSET: - field_dict["some_optional_object"] = some_optional_object - if some_enum is not UNSET: - field_dict["some_enum"] = some_enum + files.append((prop_name, (None, json.dumps(prop.to_dict()).encode(), "application/json"))) - return field_dict + return files @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.a_form_data import AFormData + from ..models.a_form_data import AFormData # noqa: PLC0415 from ..models.body_upload_file_tests_upload_post_additional_property import ( - BodyUploadFileTestsUploadPostAdditionalProperty, + BodyUploadFileTestsUploadPostAdditionalProperty, # noqa: PLC0415 ) from ..models.body_upload_file_tests_upload_post_some_nullable_object import ( - BodyUploadFileTestsUploadPostSomeNullableObject, + BodyUploadFileTestsUploadPostSomeNullableObject, # noqa: PLC0415 + ) + from ..models.body_upload_file_tests_upload_post_some_object import ( + BodyUploadFileTestsUploadPostSomeObject, # noqa: PLC0415 ) - from ..models.body_upload_file_tests_upload_post_some_object import BodyUploadFileTestsUploadPostSomeObject from ..models.body_upload_file_tests_upload_post_some_optional_object import ( - BodyUploadFileTestsUploadPostSomeOptionalObject, + BodyUploadFileTestsUploadPostSomeOptionalObject, # noqa: PLC0415 ) d = dict(src_dict) @@ -299,7 +272,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: some_object = BodyUploadFileTestsUploadPostSomeObject.from_dict(d.pop("some_object")) - def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploadPostSomeNullableObject", None]: + def _parse_some_nullable_object(data: object) -> BodyUploadFileTestsUploadPostSomeNullableObject | None: if data is None: return data try: @@ -308,14 +281,14 @@ def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploa some_nullable_object_type_0 = BodyUploadFileTestsUploadPostSomeNullableObject.from_dict(data) return some_nullable_object_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["BodyUploadFileTestsUploadPostSomeNullableObject", None], data) + return cast(BodyUploadFileTestsUploadPostSomeNullableObject | None, data) some_nullable_object = _parse_some_nullable_object(d.pop("some_nullable_object")) _some_optional_file = d.pop("some_optional_file", UNSET) - some_optional_file: Union[Unset, File] + some_optional_file: File | Unset if isinstance(_some_optional_file, Unset): some_optional_file = UNSET else: @@ -324,44 +297,46 @@ def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploa some_string = d.pop("some_string", UNSET) _a_datetime = d.pop("a_datetime", UNSET) - a_datetime: Union[Unset, datetime.datetime] + a_datetime: datetime.datetime | Unset if isinstance(_a_datetime, Unset): a_datetime = UNSET else: - a_datetime = isoparse(_a_datetime) + a_datetime = datetime.datetime.fromisoformat(_a_datetime) _a_date = d.pop("a_date", UNSET) - a_date: Union[Unset, datetime.date] + a_date: datetime.date | Unset if isinstance(_a_date, Unset): a_date = UNSET else: - a_date = isoparse(_a_date).date() + a_date = datetime.date.fromisoformat(_a_date) some_number = d.pop("some_number", UNSET) - def _parse_some_nullable_number(data: object) -> Union[None, Unset, float]: + def _parse_some_nullable_number(data: object) -> float | None | Unset: if data is None: return data if isinstance(data, Unset): return data - return cast(Union[None, Unset, float], data) + return cast(float | None | Unset, data) some_nullable_number = _parse_some_nullable_number(d.pop("some_nullable_number", UNSET)) - some_int_array = [] _some_int_array = d.pop("some_int_array", UNSET) - for some_int_array_item_data in _some_int_array or []: + some_int_array: list[int | None] | Unset = UNSET + if _some_int_array is not UNSET: + some_int_array = [] + for some_int_array_item_data in _some_int_array: - def _parse_some_int_array_item(data: object) -> Union[None, int]: - if data is None: - return data - return cast(Union[None, int], data) + def _parse_some_int_array_item(data: object) -> int | None: + if data is None: + return data + return cast(int | None, data) - some_int_array_item = _parse_some_int_array_item(some_int_array_item_data) + some_int_array_item = _parse_some_int_array_item(some_int_array_item_data) - some_int_array.append(some_int_array_item) + some_int_array.append(some_int_array_item) - def _parse_some_array(data: object) -> Union[None, Unset, list["AFormData"]]: + def _parse_some_array(data: object) -> list[AFormData] | None | Unset: if data is None: return data if isinstance(data, Unset): @@ -377,21 +352,21 @@ def _parse_some_array(data: object) -> Union[None, Unset, list["AFormData"]]: some_array_type_0.append(some_array_type_0_item) return some_array_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union[None, Unset, list["AFormData"]], data) + return cast(list[AFormData] | None | Unset, data) some_array = _parse_some_array(d.pop("some_array", UNSET)) _some_optional_object = d.pop("some_optional_object", UNSET) - some_optional_object: Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject] + some_optional_object: BodyUploadFileTestsUploadPostSomeOptionalObject | Unset if isinstance(_some_optional_object, Unset): some_optional_object = UNSET else: some_optional_object = BodyUploadFileTestsUploadPostSomeOptionalObject.from_dict(_some_optional_object) _some_enum = d.pop("some_enum", UNSET) - some_enum: Union[Unset, DifferentEnum] + some_enum: DifferentEnum | Unset if isinstance(_some_enum, Unset): some_enum = UNSET else: @@ -427,10 +402,10 @@ def _parse_some_array(data: object) -> Union[None, Unset, list["AFormData"]]: def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> "BodyUploadFileTestsUploadPostAdditionalProperty": + def __getitem__(self, key: str) -> BodyUploadFileTestsUploadPostAdditionalProperty: return self.additional_properties[key] - def __setitem__(self, key: str, value: "BodyUploadFileTestsUploadPostAdditionalProperty") -> None: + def __setitem__(self, key: str, value: BodyUploadFileTestsUploadPostAdditionalProperty) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 0ec86a372..160cb2542 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class BodyUploadFileTestsUploadPostAdditionalProperty: """ Attributes: - foo (Union[Unset, str]): + foo (str | Unset): """ - foo: Union[Unset, str] = UNSET + foo: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 b04e030aa..47796442d 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class BodyUploadFileTestsUploadPostSomeNullableObject: """ Attributes: - bar (Union[Unset, str]): + bar (str | Unset): """ - bar: Union[Unset, str] = UNSET + bar: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 ce776a4e0..0cc39b78b 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar 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 8e6eb4e83..f5658196c 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/different_enum.py b/end_to_end_tests/golden-record/my_test_api_client/models/different_enum.py index 6c167f25c..3aa4aa4df 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/different_enum.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/different_enum.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class DifferentEnum(str, Enum): +class DifferentEnum(StrEnum): DIFFERENT = "DIFFERENT" OTHER = "OTHER" 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 a3d2773a4..cf4966bf0 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,11 +1,12 @@ +from __future__ import annotations + import datetime from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from uuid import UUID from attrs import define as _attrs_define from attrs import field as _attrs_field -from dateutil.parser import isoparse from ..models.an_all_of_enum import AnAllOfEnum from ..models.an_enum import AnEnum @@ -26,65 +27,65 @@ class Extended: Attributes: an_enum_value (AnEnum): For testing Enums in all the ways they can be used an_allof_enum_with_overridden_default (AnAllOfEnum): Default: AnAllOfEnum.OVERRIDDEN_DEFAULT. - a_camel_date_time (Union[datetime.date, datetime.datetime]): + a_camel_date_time (datetime.date | datetime.datetime): a_date (datetime.date): - a_nullable_date (Union[None, datetime.date]): + a_nullable_date (datetime.date | None): a_uuid (UUID): - a_nullable_uuid (Union[None, UUID]): Default: UUID('07EF8B4D-AA09-4FFA-898D-C710796AFF41'). - required_nullable (Union[None, str]): + a_nullable_uuid (None | UUID): Default: UUID('07EF8B4D-AA09-4FFA-898D-C710796AFF41'). + required_nullable (None | str): required_not_nullable (str): - one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', Any]): - nullable_one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', None]): + one_of_models (Any | FreeFormModel | ModelWithUnionProperty): + nullable_one_of_models (FreeFormModel | ModelWithUnionProperty | None): model (ModelWithUnionProperty): - 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]]]): - a_not_required_date (Union[Unset, datetime.date]): - a_not_required_uuid (Union[Unset, UUID]): - attr_1_leading_digit (Union[Unset, str]): - attr_leading_underscore (Union[Unset, str]): - not_required_nullable (Union[None, Unset, str]): - not_required_not_nullable (Union[Unset, str]): - not_required_one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', Unset]): - not_required_nullable_one_of_models (Union['FreeFormModel', 'ModelWithUnionProperty', None, Unset, str]): - not_required_model (Union[Unset, ModelWithUnionProperty]): - not_required_nullable_model (Union['ModelWithUnionProperty', None, Unset]): - from_extended (Union[Unset, str]): + nullable_model (ModelWithUnionProperty | None): + any_value (Any | Unset): Default: 'default'. + an_optional_allof_enum (AnAllOfEnum | Unset): + nested_list_of_enums (list[list[DifferentEnum]] | Unset): + a_not_required_date (datetime.date | Unset): + a_not_required_uuid (UUID | Unset): + attr_1_leading_digit (str | Unset): + attr_leading_underscore (str | Unset): + not_required_nullable (None | str | Unset): + not_required_not_nullable (str | Unset): + not_required_one_of_models (FreeFormModel | ModelWithUnionProperty | Unset): + not_required_nullable_one_of_models (FreeFormModel | ModelWithUnionProperty | None | str | Unset): + not_required_model (ModelWithUnionProperty | Unset): + not_required_nullable_model (ModelWithUnionProperty | None | Unset): + from_extended (str | Unset): """ an_enum_value: AnEnum - a_camel_date_time: Union[datetime.date, datetime.datetime] + a_camel_date_time: datetime.date | datetime.datetime a_date: datetime.date - a_nullable_date: Union[None, datetime.date] + a_nullable_date: datetime.date | None a_uuid: UUID - required_nullable: Union[None, str] + required_nullable: None | str required_not_nullable: str - one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", Any] - nullable_one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", None] - model: "ModelWithUnionProperty" - nullable_model: Union["ModelWithUnionProperty", None] + one_of_models: Any | FreeFormModel | ModelWithUnionProperty + nullable_one_of_models: FreeFormModel | ModelWithUnionProperty | None + model: ModelWithUnionProperty + nullable_model: ModelWithUnionProperty | None an_allof_enum_with_overridden_default: AnAllOfEnum = AnAllOfEnum.OVERRIDDEN_DEFAULT - 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 - 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 - attr_leading_underscore: Union[Unset, str] = UNSET - not_required_nullable: Union[None, Unset, str] = UNSET - not_required_not_nullable: Union[Unset, str] = UNSET - not_required_one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", Unset] = UNSET - not_required_nullable_one_of_models: Union["FreeFormModel", "ModelWithUnionProperty", None, Unset, str] = UNSET - not_required_model: Union[Unset, "ModelWithUnionProperty"] = UNSET - not_required_nullable_model: Union["ModelWithUnionProperty", None, Unset] = UNSET - from_extended: Union[Unset, str] = UNSET + a_nullable_uuid: None | UUID = UUID("07EF8B4D-AA09-4FFA-898D-C710796AFF41") + any_value: Any | Unset = "default" + an_optional_allof_enum: AnAllOfEnum | Unset = UNSET + nested_list_of_enums: list[list[DifferentEnum]] | Unset = UNSET + a_not_required_date: datetime.date | Unset = UNSET + a_not_required_uuid: UUID | Unset = UNSET + attr_1_leading_digit: str | Unset = UNSET + attr_leading_underscore: str | Unset = UNSET + not_required_nullable: None | str | Unset = UNSET + not_required_not_nullable: str | Unset = UNSET + not_required_one_of_models: FreeFormModel | ModelWithUnionProperty | Unset = UNSET + not_required_nullable_one_of_models: FreeFormModel | ModelWithUnionProperty | None | str | Unset = UNSET + not_required_model: ModelWithUnionProperty | Unset = UNSET + not_required_nullable_model: ModelWithUnionProperty | None | Unset = UNSET + from_extended: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: - from ..models.free_form_model import FreeFormModel - from ..models.model_with_union_property import ModelWithUnionProperty + from ..models.free_form_model import FreeFormModel # noqa: PLC0415 + from ..models.model_with_union_property import ModelWithUnionProperty # noqa: PLC0415 an_enum_value = self.an_enum_value.value @@ -98,7 +99,7 @@ def to_dict(self) -> dict[str, Any]: a_date = self.a_date.isoformat() - a_nullable_date: Union[None, str] + a_nullable_date: None | str if isinstance(self.a_nullable_date, datetime.date): a_nullable_date = self.a_nullable_date.isoformat() else: @@ -106,18 +107,18 @@ def to_dict(self) -> dict[str, Any]: a_uuid = str(self.a_uuid) - a_nullable_uuid: Union[None, str] + a_nullable_uuid: None | str if isinstance(self.a_nullable_uuid, UUID): a_nullable_uuid = str(self.a_nullable_uuid) else: a_nullable_uuid = self.a_nullable_uuid - required_nullable: Union[None, str] + required_nullable: None | str required_nullable = self.required_nullable required_not_nullable = self.required_not_nullable - one_of_models: Union[Any, dict[str, Any]] + one_of_models: 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): @@ -125,7 +126,7 @@ def to_dict(self) -> dict[str, Any]: else: one_of_models = self.one_of_models - nullable_one_of_models: Union[None, dict[str, Any]] + nullable_one_of_models: dict[str, Any] | None 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): @@ -135,7 +136,7 @@ def to_dict(self) -> dict[str, Any]: model = self.model.to_dict() - nullable_model: Union[None, dict[str, Any]] + nullable_model: dict[str, Any] | None if isinstance(self.nullable_model, ModelWithUnionProperty): nullable_model = self.nullable_model.to_dict() else: @@ -143,11 +144,11 @@ def to_dict(self) -> dict[str, Any]: any_value = self.any_value - an_optional_allof_enum: Union[Unset, str] = UNSET + an_optional_allof_enum: str | Unset = UNSET 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: list[list[str]] | Unset = 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: @@ -158,11 +159,11 @@ def to_dict(self) -> dict[str, Any]: nested_list_of_enums.append(nested_list_of_enums_item) - a_not_required_date: Union[Unset, str] = UNSET + a_not_required_date: str | Unset = UNSET if not isinstance(self.a_not_required_date, Unset): a_not_required_date = self.a_not_required_date.isoformat() - a_not_required_uuid: Union[Unset, str] = UNSET + a_not_required_uuid: str | Unset = UNSET if not isinstance(self.a_not_required_uuid, Unset): a_not_required_uuid = str(self.a_not_required_uuid) @@ -170,7 +171,7 @@ def to_dict(self) -> dict[str, Any]: attr_leading_underscore = self.attr_leading_underscore - not_required_nullable: Union[None, Unset, str] + not_required_nullable: None | str | Unset if isinstance(self.not_required_nullable, Unset): not_required_nullable = UNSET else: @@ -178,7 +179,7 @@ def to_dict(self) -> dict[str, Any]: not_required_not_nullable = self.not_required_not_nullable - not_required_one_of_models: Union[Unset, dict[str, Any]] + not_required_one_of_models: dict[str, Any] | Unset if isinstance(self.not_required_one_of_models, Unset): not_required_one_of_models = UNSET elif isinstance(self.not_required_one_of_models, FreeFormModel): @@ -186,7 +187,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[None, Unset, dict[str, Any], str] + not_required_nullable_one_of_models: dict[str, Any] | None | str | Unset 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): @@ -196,11 +197,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: dict[str, Any] | Unset = UNSET if not isinstance(self.not_required_model, Unset): not_required_model = self.not_required_model.to_dict() - not_required_nullable_model: Union[None, Unset, dict[str, Any]] + not_required_nullable_model: dict[str, Any] | None | Unset if isinstance(self.not_required_nullable_model, Unset): not_required_nullable_model = UNSET elif isinstance(self.not_required_nullable_model, ModelWithUnionProperty): @@ -262,51 +263,51 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.free_form_model import FreeFormModel - from ..models.model_with_union_property import ModelWithUnionProperty + from ..models.free_form_model import FreeFormModel # noqa: PLC0415 + from ..models.model_with_union_property import ModelWithUnionProperty # noqa: PLC0415 d = dict(src_dict) an_enum_value = AnEnum(d.pop("an_enum_value")) an_allof_enum_with_overridden_default = AnAllOfEnum(d.pop("an_allof_enum_with_overridden_default")) - def _parse_a_camel_date_time(data: object) -> Union[datetime.date, datetime.datetime]: + def _parse_a_camel_date_time(data: object) -> datetime.date | datetime.datetime: try: if not isinstance(data, str): raise TypeError() - a_camel_date_time_type_0 = isoparse(data) + a_camel_date_time_type_0 = datetime.datetime.fromisoformat(data) return a_camel_date_time_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass if not isinstance(data, str): raise TypeError() - a_camel_date_time_type_1 = isoparse(data).date() + a_camel_date_time_type_1 = datetime.date.fromisoformat(data) return a_camel_date_time_type_1 a_camel_date_time = _parse_a_camel_date_time(d.pop("aCamelDateTime")) - a_date = isoparse(d.pop("a_date")).date() + a_date = datetime.date.fromisoformat(d.pop("a_date")) - def _parse_a_nullable_date(data: object) -> Union[None, datetime.date]: + def _parse_a_nullable_date(data: object) -> datetime.date | None: if data is None: return data try: if not isinstance(data, str): raise TypeError() - a_nullable_date_type_0 = isoparse(data).date() + a_nullable_date_type_0 = datetime.date.fromisoformat(data) return a_nullable_date_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union[None, datetime.date], data) + return cast(datetime.date | None, data) a_nullable_date = _parse_a_nullable_date(d.pop("a_nullable_date")) a_uuid = UUID(d.pop("a_uuid")) - def _parse_a_nullable_uuid(data: object) -> Union[None, UUID]: + def _parse_a_nullable_uuid(data: object) -> None | UUID: if data is None: return data try: @@ -315,29 +316,29 @@ def _parse_a_nullable_uuid(data: object) -> Union[None, UUID]: a_nullable_uuid_type_0 = UUID(data) return a_nullable_uuid_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union[None, UUID], data) + return cast(None | UUID, data) a_nullable_uuid = _parse_a_nullable_uuid(d.pop("a_nullable_uuid")) - def _parse_required_nullable(data: object) -> Union[None, str]: + def _parse_required_nullable(data: object) -> None | str: if data is None: return data - return cast(Union[None, str], data) + return cast(None | str, data) required_nullable = _parse_required_nullable(d.pop("required_nullable")) required_not_nullable = d.pop("required_not_nullable") - def _parse_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnionProperty", Any]: + def _parse_one_of_models(data: object) -> Any | FreeFormModel | ModelWithUnionProperty: try: if not isinstance(data, dict): raise TypeError() one_of_models_type_0 = FreeFormModel.from_dict(data) return one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, dict): @@ -345,13 +346,13 @@ def _parse_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnion one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return one_of_models_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["FreeFormModel", "ModelWithUnionProperty", Any], data) + return cast(Any | FreeFormModel | ModelWithUnionProperty, data) one_of_models = _parse_one_of_models(d.pop("one_of_models")) - def _parse_nullable_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnionProperty", None]: + def _parse_nullable_one_of_models(data: object) -> FreeFormModel | ModelWithUnionProperty | None: if data is None: return data try: @@ -360,7 +361,7 @@ def _parse_nullable_one_of_models(data: object) -> Union["FreeFormModel", "Model nullable_one_of_models_type_0 = FreeFormModel.from_dict(data) return nullable_one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, dict): @@ -368,15 +369,15 @@ def _parse_nullable_one_of_models(data: object) -> Union["FreeFormModel", "Model nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return nullable_one_of_models_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["FreeFormModel", "ModelWithUnionProperty", None], data) + return cast(FreeFormModel | ModelWithUnionProperty | None, data) nullable_one_of_models = _parse_nullable_one_of_models(d.pop("nullable_one_of_models")) model = ModelWithUnionProperty.from_dict(d.pop("model")) - def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None]: + def _parse_nullable_model(data: object) -> ModelWithUnionProperty | None: if data is None: return data try: @@ -385,42 +386,44 @@ def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None] nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) return nullable_model_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["ModelWithUnionProperty", None], data) + return cast(ModelWithUnionProperty | None, data) nullable_model = _parse_nullable_model(d.pop("nullable_model")) any_value = d.pop("any_value", UNSET) _an_optional_allof_enum = d.pop("an_optional_allof_enum", UNSET) - an_optional_allof_enum: Union[Unset, AnAllOfEnum] + an_optional_allof_enum: AnAllOfEnum | Unset if isinstance(_an_optional_allof_enum, Unset): an_optional_allof_enum = UNSET else: an_optional_allof_enum = AnAllOfEnum(_an_optional_allof_enum) - nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) - for nested_list_of_enums_item_data in _nested_list_of_enums or []: - nested_list_of_enums_item = [] - _nested_list_of_enums_item = nested_list_of_enums_item_data - for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: - nested_list_of_enums_item_item = DifferentEnum(nested_list_of_enums_item_item_data) + nested_list_of_enums: list[list[DifferentEnum]] | Unset = UNSET + if _nested_list_of_enums is not UNSET: + nested_list_of_enums = [] + for nested_list_of_enums_item_data in _nested_list_of_enums: + nested_list_of_enums_item = [] + _nested_list_of_enums_item = nested_list_of_enums_item_data + for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: + nested_list_of_enums_item_item = DifferentEnum(nested_list_of_enums_item_item_data) - nested_list_of_enums_item.append(nested_list_of_enums_item_item) + nested_list_of_enums_item.append(nested_list_of_enums_item_item) - nested_list_of_enums.append(nested_list_of_enums_item) + nested_list_of_enums.append(nested_list_of_enums_item) _a_not_required_date = d.pop("a_not_required_date", UNSET) - a_not_required_date: Union[Unset, datetime.date] + a_not_required_date: datetime.date | Unset if isinstance(_a_not_required_date, Unset): a_not_required_date = UNSET else: - a_not_required_date = isoparse(_a_not_required_date).date() + a_not_required_date = datetime.date.fromisoformat(_a_not_required_date) _a_not_required_uuid = d.pop("a_not_required_uuid", UNSET) - a_not_required_uuid: Union[Unset, UUID] + a_not_required_uuid: UUID | Unset if isinstance(_a_not_required_uuid, Unset): a_not_required_uuid = UNSET else: @@ -430,18 +433,18 @@ def _parse_nullable_model(data: object) -> Union["ModelWithUnionProperty", None] attr_leading_underscore = d.pop("_leading_underscore", UNSET) - def _parse_not_required_nullable(data: object) -> Union[None, Unset, str]: + def _parse_not_required_nullable(data: object) -> None | str | Unset: if data is None: return data if isinstance(data, Unset): return data - return cast(Union[None, Unset, str], data) + return cast(None | str | Unset, data) not_required_nullable = _parse_not_required_nullable(d.pop("not_required_nullable", UNSET)) not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "ModelWithUnionProperty", Unset]: + def _parse_not_required_one_of_models(data: object) -> FreeFormModel | ModelWithUnionProperty | Unset: if isinstance(data, Unset): return data try: @@ -450,7 +453,7 @@ def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "M not_required_one_of_models_type_0 = FreeFormModel.from_dict(data) return not_required_one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass if not isinstance(data, dict): raise TypeError() @@ -462,7 +465,7 @@ def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "M def _parse_not_required_nullable_one_of_models( data: object, - ) -> Union["FreeFormModel", "ModelWithUnionProperty", None, Unset, str]: + ) -> FreeFormModel | ModelWithUnionProperty | None | str | Unset: if data is None: return data if isinstance(data, Unset): @@ -473,7 +476,7 @@ def _parse_not_required_nullable_one_of_models( not_required_nullable_one_of_models_type_0 = FreeFormModel.from_dict(data) return not_required_nullable_one_of_models_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, dict): @@ -481,22 +484,22 @@ def _parse_not_required_nullable_one_of_models( not_required_nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return not_required_nullable_one_of_models_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["FreeFormModel", "ModelWithUnionProperty", None, Unset, str], data) + return cast(FreeFormModel | ModelWithUnionProperty | None | str | Unset, data) not_required_nullable_one_of_models = _parse_not_required_nullable_one_of_models( d.pop("not_required_nullable_one_of_models", UNSET) ) _not_required_model = d.pop("not_required_model", UNSET) - not_required_model: Union[Unset, ModelWithUnionProperty] + not_required_model: ModelWithUnionProperty | Unset if isinstance(_not_required_model, Unset): not_required_model = UNSET else: not_required_model = ModelWithUnionProperty.from_dict(_not_required_model) - def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionProperty", None, Unset]: + def _parse_not_required_nullable_model(data: object) -> ModelWithUnionProperty | None | Unset: if data is None: return data if isinstance(data, Unset): @@ -507,9 +510,9 @@ def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionPro not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) return not_required_nullable_model_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["ModelWithUnionProperty", None, Unset], data) + return cast(ModelWithUnionProperty | None | Unset, data) not_required_nullable_model = _parse_not_required_nullable_model(d.pop("not_required_nullable_model", UNSET)) 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 403de072b..8c85f3e46 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -14,6 +16,7 @@ class FreeFormModel: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/get_location_header_types_string_enum_header.py b/end_to_end_tests/golden-record/my_test_api_client/models/get_location_header_types_string_enum_header.py index cce92dcde..2a5242fe5 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/get_location_header_types_string_enum_header.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/get_location_header_types_string_enum_header.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class GetLocationHeaderTypesStringEnumHeader(str, Enum): +class GetLocationHeaderTypesStringEnumHeader(StrEnum): ONE = "one" THREE = "three" TWO = "two" 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 dce964228..9ec5f68c8 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,26 +20,26 @@ class GetModelsAllofResponse200: """ Attributes: - aliased (Union[Unset, AModel]): A Model for testing all the ways custom objects can be used - extended (Union[Unset, Extended]): - model (Union[Unset, AModel]): A Model for testing all the ways custom objects can be used + aliased (AModel | Unset): A Model for testing all the ways custom objects can be used + extended (Extended | Unset): + model (AModel | Unset): A Model for testing all the ways custom objects can be used """ - aliased: Union[Unset, "AModel"] = UNSET - extended: Union[Unset, "Extended"] = UNSET - model: Union[Unset, "AModel"] = UNSET + aliased: AModel | Unset = UNSET + extended: Extended | Unset = UNSET + model: AModel | Unset = UNSET 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 + aliased: dict[str, Any] | Unset = UNSET if not isinstance(self.aliased, Unset): aliased = self.aliased.to_dict() - extended: Union[Unset, dict[str, Any]] = UNSET + extended: dict[str, Any] | Unset = UNSET if not isinstance(self.extended, Unset): extended = self.extended.to_dict() - model: Union[Unset, dict[str, Any]] = UNSET + model: dict[str, Any] | Unset = UNSET if not isinstance(self.model, Unset): model = self.model.to_dict() @@ -55,26 +57,26 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.a_model import AModel - from ..models.extended import Extended + from ..models.a_model import AModel # noqa: PLC0415 + from ..models.extended import Extended # noqa: PLC0415 d = dict(src_dict) _aliased = d.pop("aliased", UNSET) - aliased: Union[Unset, AModel] + aliased: AModel | Unset if isinstance(_aliased, Unset): aliased = UNSET else: aliased = AModel.from_dict(_aliased) _extended = d.pop("extended", UNSET) - extended: Union[Unset, Extended] + extended: Extended | Unset if isinstance(_extended, Unset): extended = UNSET else: extended = Extended.from_dict(_extended) _model = d.pop("model", UNSET) - model: Union[Unset, AModel] + model: AModel | Unset if isinstance(_model, Unset): model = UNSET else: 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 54531a7f8..a079b954a 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, Literal, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,11 +16,11 @@ class GetModelsOneofWithRequiredConstResponse200Type0: """ Attributes: type_ (Literal['alpha']): - color (Union[Unset, str]): + color (str | Unset): """ type_: Literal["alpha"] - color: Union[Unset, str] = UNSET + color: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 69f11cca0..04670dcba 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, Literal, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,11 +16,11 @@ class GetModelsOneofWithRequiredConstResponse200Type1: """ Attributes: type_ (Literal['beta']): - texture (Union[Unset, str]): + texture (str | Unset): """ type_: Literal["beta"] - texture: Union[Unset, str] = UNSET + texture: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 cfe0a9a0c..7ed970dde 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define @@ -16,13 +18,13 @@ class HTTPValidationError: """ Attributes: - detail (Union[Unset, list['ValidationError']]): + detail (list[ValidationError] | Unset): """ - detail: Union[Unset, list["ValidationError"]] = UNSET + detail: list[ValidationError] | Unset = UNSET def to_dict(self) -> dict[str, Any]: - detail: Union[Unset, list[dict[str, Any]]] = UNSET + detail: list[dict[str, Any]] | Unset = UNSET if not isinstance(self.detail, Unset): detail = [] for detail_item_data in self.detail: @@ -30,6 +32,7 @@ def to_dict(self) -> dict[str, Any]: detail.append(detail_item) field_dict: dict[str, Any] = {} + field_dict.update({}) if detail is not UNSET: field_dict["detail"] = detail @@ -38,15 +41,17 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.validation_error import ValidationError + from ..models.validation_error import ValidationError # noqa: PLC0415 d = dict(src_dict) - detail = [] _detail = d.pop("detail", UNSET) - for detail_item_data in _detail or []: - detail_item = ValidationError.from_dict(detail_item_data) + detail: list[ValidationError] | Unset = UNSET + if _detail is not UNSET: + detail = [] + for detail_item_data in _detail: + detail_item = ValidationError.from_dict(detail_item_data) - detail.append(detail_item) + detail.append(detail_item) http_validation_error = cls( detail=detail, 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 6552fa1f1..b22afa9c8 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -14,6 +16,7 @@ class Import: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) 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 6c1a6a3b0..4e60f7ab6 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class JsonLikeBody: """ Attributes: - a (Union[Unset, str]): + a (str | Unset): """ - a: Union[Unset, str] = UNSET + a: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 1e07a2852..93aead870 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,12 +15,12 @@ class MixedCaseResponse200: """ Attributes: - mixed_case (Union[Unset, str]): - mixedCase (Union[Unset, str]): + mixed_case (str | Unset): + mixedCase (str | Unset): """ - mixed_case: Union[Unset, str] = UNSET - mixedCase: Union[Unset, str] = UNSET + mixed_case: str | Unset = UNSET + mixedCase: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 9fe753d49..a8abde58a 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -15,26 +17,26 @@ class ModelFromAllOf: """ Attributes: - a_sub_property (Union[Unset, str]): - type_ (Union[Unset, AnotherAllOfSubModelType]): - type_enum (Union[Unset, AnotherAllOfSubModelTypeEnum]): - another_sub_property (Union[Unset, str]): + a_sub_property (str | Unset): + type_ (AnotherAllOfSubModelType | Unset): + type_enum (AnotherAllOfSubModelTypeEnum | Unset): + another_sub_property (str | Unset): """ - a_sub_property: Union[Unset, str] = UNSET - type_: Union[Unset, AnotherAllOfSubModelType] = UNSET - type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] = UNSET - another_sub_property: Union[Unset, str] = UNSET + a_sub_property: str | Unset = UNSET + type_: AnotherAllOfSubModelType | Unset = UNSET + type_enum: AnotherAllOfSubModelTypeEnum | Unset = UNSET + another_sub_property: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: a_sub_property = self.a_sub_property - type_: Union[Unset, str] = UNSET + type_: str | Unset = UNSET if not isinstance(self.type_, Unset): type_ = self.type_.value - type_enum: Union[Unset, int] = UNSET + type_enum: int | Unset = UNSET if not isinstance(self.type_enum, Unset): type_enum = self.type_enum.value @@ -60,14 +62,14 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: a_sub_property = d.pop("a_sub_property", UNSET) _type_ = d.pop("type", UNSET) - type_: Union[Unset, AnotherAllOfSubModelType] + type_: AnotherAllOfSubModelType | Unset if isinstance(_type_, Unset): type_ = UNSET else: type_ = AnotherAllOfSubModelType(_type_) _type_enum = d.pop("type_enum", UNSET) - type_enum: Union[Unset, AnotherAllOfSubModelTypeEnum] + type_enum: AnotherAllOfSubModelTypeEnum | Unset if isinstance(_type_enum, Unset): type_enum = UNSET else: 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 3394332c0..68e0ecaa7 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -14,6 +16,7 @@ class ModelName: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) 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 95ad5849a..52caf65bb 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -14,6 +16,7 @@ class ModelReferenceWithPeriods: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) 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 bcdbf868c..c1ca5e44c 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -19,11 +21,11 @@ class ModelWithAdditionalPropertiesInlined: """ Attributes: - a_number (Union[Unset, float]): + a_number (float | Unset): """ - a_number: Union[Unset, float] = UNSET - additional_properties: dict[str, "ModelWithAdditionalPropertiesInlinedAdditionalProperty"] = _attrs_field( + a_number: float | Unset = UNSET + additional_properties: dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperty] = _attrs_field( init=False, factory=dict ) @@ -33,6 +35,7 @@ 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() + field_dict.update({}) if a_number is not UNSET: field_dict["a_number"] = a_number @@ -42,7 +45,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.model_with_additional_properties_inlined_additional_property import ( - ModelWithAdditionalPropertiesInlinedAdditionalProperty, + ModelWithAdditionalPropertiesInlinedAdditionalProperty, # noqa: PLC0415 ) d = dict(src_dict) @@ -65,10 +68,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperty": + def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperty: return self.additional_properties[key] - def __setitem__(self, key: str, value: "ModelWithAdditionalPropertiesInlinedAdditionalProperty") -> None: + def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperty) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 e4fc6a09f..8f877a575 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class ModelWithAdditionalPropertiesInlinedAdditionalProperty: """ Attributes: - extra_props_prop (Union[Unset, str]): + extra_props_prop (str | Unset): """ - extra_props_prop: Union[Unset, str] = UNSET + extra_props_prop: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 2bbd16327..197df9307 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -16,6 +18,7 @@ class ModelWithAdditionalPropertiesRefed: additional_properties: dict[str, AnEnum] = _attrs_field(init=False, factory=dict) 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 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 e1aa63d45..2fed0b38b 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,12 +20,12 @@ class ModelWithAnyJsonProperties: """ """ additional_properties: dict[ - str, Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", bool, float, int, list[str], str] + str, bool | float | int | list[str] | ModelWithAnyJsonPropertiesAdditionalPropertyType0 | 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, + ModelWithAnyJsonPropertiesAdditionalPropertyType0, # noqa: PLC0415 ) field_dict: dict[str, Any] = {} @@ -41,7 +43,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.model_with_any_json_properties_additional_property_type_0 import ( - ModelWithAnyJsonPropertiesAdditionalPropertyType0, + ModelWithAnyJsonPropertiesAdditionalPropertyType0, # noqa: PLC0415 ) d = dict(src_dict) @@ -52,14 +54,14 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def _parse_additional_property( data: object, - ) -> Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", bool, float, int, list[str], str]: + ) -> bool | float | int | list[str] | ModelWithAnyJsonPropertiesAdditionalPropertyType0 | str: try: if not isinstance(data, dict): raise TypeError() additional_property_type_0 = ModelWithAnyJsonPropertiesAdditionalPropertyType0.from_dict(data) return additional_property_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, list): @@ -67,10 +69,10 @@ def _parse_additional_property( additional_property_type_1 = cast(list[str], data) return additional_property_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass return cast( - Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", bool, float, int, list[str], str], data + bool | float | int | list[str] | ModelWithAnyJsonPropertiesAdditionalPropertyType0 | str, data ) additional_property = _parse_additional_property(prop_dict) @@ -86,13 +88,11 @@ def additional_keys(self) -> list[str]: def __getitem__( self, key: str - ) -> Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", bool, float, int, list[str], str]: + ) -> bool | float | int | list[str] | ModelWithAnyJsonPropertiesAdditionalPropertyType0 | str: return self.additional_properties[key] def __setitem__( - self, - key: str, - value: Union["ModelWithAnyJsonPropertiesAdditionalPropertyType0", bool, float, int, list[str], str], + self, key: str, value: bool | float | int | list[str] | ModelWithAnyJsonPropertiesAdditionalPropertyType0 | 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_type_0.py index 9cdda2b79..633d05d68 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_type_0.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -14,6 +16,7 @@ class ModelWithAnyJsonPropertiesAdditionalPropertyType0: additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) 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 5d0c8be3a..b695fe97a 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -16,6 +18,7 @@ class ModelWithBackslashInDescription: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) 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 34ee404f3..a9bde2daf 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,14 +19,14 @@ class ModelWithCircularRefA: """ Attributes: - circular (Union[Unset, ModelWithCircularRefB]): + circular (ModelWithCircularRefB | Unset): """ - circular: Union[Unset, "ModelWithCircularRefB"] = UNSET + circular: ModelWithCircularRefB | Unset = UNSET 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 + circular: dict[str, Any] | Unset = UNSET if not isinstance(self.circular, Unset): circular = self.circular.to_dict() @@ -38,11 +40,11 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.model_with_circular_ref_b import ModelWithCircularRefB + from ..models.model_with_circular_ref_b import ModelWithCircularRefB # noqa: PLC0415 d = dict(src_dict) _circular = d.pop("circular", UNSET) - circular: Union[Unset, ModelWithCircularRefB] + circular: ModelWithCircularRefB | Unset if isinstance(_circular, Unset): circular = UNSET else: 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 94a93f003..c21d33148 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,14 +19,14 @@ class ModelWithCircularRefB: """ Attributes: - circular (Union[Unset, ModelWithCircularRefA]): + circular (ModelWithCircularRefA | Unset): """ - circular: Union[Unset, "ModelWithCircularRefA"] = UNSET + circular: ModelWithCircularRefA | Unset = UNSET 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 + circular: dict[str, Any] | Unset = UNSET if not isinstance(self.circular, Unset): circular = self.circular.to_dict() @@ -38,11 +40,11 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.model_with_circular_ref_a import ModelWithCircularRefA + from ..models.model_with_circular_ref_a import ModelWithCircularRefA # noqa: PLC0415 d = dict(src_dict) _circular = d.pop("circular", UNSET) - circular: Union[Unset, ModelWithCircularRefA] + circular: ModelWithCircularRefA | Unset if isinstance(_circular, Unset): circular = UNSET else: 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 b5c3ca2e1..a0fa52dbd 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import TYPE_CHECKING, Any, TypeVar @@ -15,11 +17,12 @@ 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] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() @@ -29,7 +32,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.model_with_circular_ref_in_additional_properties_b import ( - ModelWithCircularRefInAdditionalPropertiesB, + ModelWithCircularRefInAdditionalPropertiesB, # noqa: PLC0415 ) d = dict(src_dict) @@ -48,10 +51,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> "ModelWithCircularRefInAdditionalPropertiesB": + def __getitem__(self, key: str) -> ModelWithCircularRefInAdditionalPropertiesB: return self.additional_properties[key] - def __setitem__(self, key: str, value: "ModelWithCircularRefInAdditionalPropertiesB") -> None: + def __setitem__(self, key: str, value: ModelWithCircularRefInAdditionalPropertiesB) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 a6e963ca6..080ba46ad 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import TYPE_CHECKING, Any, TypeVar @@ -15,11 +17,12 @@ 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] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() @@ -29,7 +32,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.model_with_circular_ref_in_additional_properties_a import ( - ModelWithCircularRefInAdditionalPropertiesA, + ModelWithCircularRefInAdditionalPropertiesA, # noqa: PLC0415 ) d = dict(src_dict) @@ -48,10 +51,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> "ModelWithCircularRefInAdditionalPropertiesA": + def __getitem__(self, key: str) -> ModelWithCircularRefInAdditionalPropertiesA: return self.additional_properties[key] - def __setitem__(self, key: str, value: "ModelWithCircularRefInAdditionalPropertiesA") -> None: + def __setitem__(self, key: str, value: ModelWithCircularRefInAdditionalPropertiesA) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 7ac821259..b005efa02 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,10 +1,11 @@ +from __future__ import annotations + import datetime from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field -from dateutil.parser import isoparse from ..types import UNSET, Unset @@ -15,14 +16,14 @@ class ModelWithDateTimeProperty: """ Attributes: - datetime_ (Union[Unset, datetime.datetime]): + datetime_ (datetime.datetime | Unset): """ - datetime_: Union[Unset, datetime.datetime] = UNSET + datetime_: datetime.datetime | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: - datetime_: Union[Unset, str] = UNSET + datetime_: str | Unset = UNSET if not isinstance(self.datetime_, Unset): datetime_ = self.datetime_.isoformat() @@ -38,11 +39,11 @@ def to_dict(self) -> dict[str, Any]: def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) _datetime_ = d.pop("datetime", UNSET) - datetime_: Union[Unset, datetime.datetime] + datetime_: datetime.datetime | Unset if isinstance(_datetime_, Unset): datetime_ = UNSET else: - datetime_ = isoparse(_datetime_) + datetime_ = datetime.datetime.fromisoformat(_datetime_) model_with_date_time_property = cls( datetime_=datetime_, 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 index 203e321dd..41b972dc4 100644 --- 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 @@ -1,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -18,17 +20,17 @@ class ModelWithDiscriminatedUnion: """ Attributes: - discriminated_union (Union['ADiscriminatedUnionType1', 'ADiscriminatedUnionType2', None, Unset]): + discriminated_union (ADiscriminatedUnionType1 | ADiscriminatedUnionType2 | None | Unset): """ - discriminated_union: Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset] = UNSET + discriminated_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 + from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1 # noqa: PLC0415 + from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2 # noqa: PLC0415 - discriminated_union: Union[None, Unset, dict[str, Any]] + discriminated_union: dict[str, Any] | None | Unset if isinstance(self.discriminated_union, Unset): discriminated_union = UNSET elif isinstance(self.discriminated_union, ADiscriminatedUnionType1): @@ -48,14 +50,14 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1 - from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2 + from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1 # noqa: PLC0415 + from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2 # noqa: PLC0415 d = dict(src_dict) def _parse_discriminated_union( data: object, - ) -> Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset]: + ) -> ADiscriminatedUnionType1 | ADiscriminatedUnionType2 | None | Unset: if data is None: return data if isinstance(data, Unset): @@ -66,7 +68,7 @@ def _parse_discriminated_union( componentsschemas_a_discriminated_union_type_0 = ADiscriminatedUnionType1.from_dict(data) return componentsschemas_a_discriminated_union_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass try: if not isinstance(data, dict): @@ -74,9 +76,9 @@ def _parse_discriminated_union( componentsschemas_a_discriminated_union_type_1 = ADiscriminatedUnionType2.from_dict(data) return componentsschemas_a_discriminated_union_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset], data) + return cast(ADiscriminatedUnionType1 | ADiscriminatedUnionType2 | None | Unset, data) discriminated_union = _parse_discriminated_union(d.pop("discriminated_union", UNSET)) 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 a740022a6..1ee8b4a61 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,10 +1,11 @@ +from __future__ import annotations + import datetime from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field -from dateutil.parser import isoparse from ..models.model_with_merged_properties_string_to_enum import ModelWithMergedPropertiesStringToEnum from ..types import UNSET, Unset @@ -16,29 +17,29 @@ class ModelWithMergedProperties: """ Attributes: - simple_string (Union[Unset, str]): extended simpleString description Default: 'new default'. - string_to_enum (Union[Unset, ModelWithMergedPropertiesStringToEnum]): Default: + simple_string (str | Unset): extended simpleString description Default: 'new default'. + string_to_enum (ModelWithMergedPropertiesStringToEnum | Unset): Default: ModelWithMergedPropertiesStringToEnum.A. - string_to_date (Union[Unset, datetime.date]): - number_to_int (Union[Unset, int]): - any_to_string (Union[Unset, str]): Default: 'x'. + string_to_date (datetime.date | Unset): + number_to_int (int | Unset): + any_to_string (str | Unset): Default: 'x'. """ - simple_string: Union[Unset, str] = "new default" - string_to_enum: Union[Unset, ModelWithMergedPropertiesStringToEnum] = ModelWithMergedPropertiesStringToEnum.A - string_to_date: Union[Unset, datetime.date] = UNSET - number_to_int: Union[Unset, int] = UNSET - any_to_string: Union[Unset, str] = "x" + simple_string: str | Unset = "new default" + string_to_enum: ModelWithMergedPropertiesStringToEnum | Unset = ModelWithMergedPropertiesStringToEnum.A + string_to_date: datetime.date | Unset = UNSET + number_to_int: int | Unset = UNSET + any_to_string: str | Unset = "x" additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: simple_string = self.simple_string - string_to_enum: Union[Unset, str] = UNSET + string_to_enum: str | Unset = UNSET if not isinstance(self.string_to_enum, Unset): string_to_enum = self.string_to_enum.value - string_to_date: Union[Unset, str] = UNSET + string_to_date: str | Unset = UNSET if not isinstance(self.string_to_date, Unset): string_to_date = self.string_to_date.isoformat() @@ -68,18 +69,18 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: simple_string = d.pop("simpleString", UNSET) _string_to_enum = d.pop("stringToEnum", UNSET) - string_to_enum: Union[Unset, ModelWithMergedPropertiesStringToEnum] + string_to_enum: ModelWithMergedPropertiesStringToEnum | Unset if isinstance(_string_to_enum, Unset): string_to_enum = UNSET else: string_to_enum = ModelWithMergedPropertiesStringToEnum(_string_to_enum) _string_to_date = d.pop("stringToDate", UNSET) - string_to_date: Union[Unset, datetime.date] + string_to_date: datetime.date | Unset if isinstance(_string_to_date, Unset): string_to_date = UNSET else: - string_to_date = isoparse(_string_to_date).date() + string_to_date = datetime.date.fromisoformat(_string_to_date) number_to_int = d.pop("numberToInt", UNSET) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties_string_to_enum.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties_string_to_enum.py index 5e146c5eb..22fb32812 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties_string_to_enum.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties_string_to_enum.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class ModelWithMergedPropertiesStringToEnum(str, Enum): +class ModelWithMergedPropertiesStringToEnum(StrEnum): A = "a" B = "b" 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 741ceb4fd..c74887f5b 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -11,6 +13,7 @@ class ModelWithNoProperties: """ """ def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} return field_dict 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 ccd515142..f4d01fd3f 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -19,14 +21,14 @@ class ModelWithPrimitiveAdditionalProperties: """ Attributes: - a_date_holder (Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder]): + a_date_holder (ModelWithPrimitiveAdditionalPropertiesADateHolder | Unset): """ - a_date_holder: Union[Unset, "ModelWithPrimitiveAdditionalPropertiesADateHolder"] = UNSET + a_date_holder: ModelWithPrimitiveAdditionalPropertiesADateHolder | Unset = UNSET 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 + a_date_holder: dict[str, Any] | Unset = UNSET if not isinstance(self.a_date_holder, Unset): a_date_holder = self.a_date_holder.to_dict() @@ -41,12 +43,12 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.model_with_primitive_additional_properties_a_date_holder import ( - ModelWithPrimitiveAdditionalPropertiesADateHolder, + ModelWithPrimitiveAdditionalPropertiesADateHolder, # noqa: PLC0415 ) d = dict(src_dict) _a_date_holder = d.pop("a_date_holder", UNSET) - a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] + a_date_holder: ModelWithPrimitiveAdditionalPropertiesADateHolder | Unset if isinstance(_a_date_holder, Unset): a_date_holder = UNSET else: 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 9d2776403..ce4be38c0 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,10 +1,11 @@ +from __future__ import annotations + import datetime from collections.abc import Mapping from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field -from dateutil.parser import isoparse T = TypeVar("T", bound="ModelWithPrimitiveAdditionalPropertiesADateHolder") @@ -16,6 +17,7 @@ class ModelWithPrimitiveAdditionalPropertiesADateHolder: additional_properties: dict[str, datetime.datetime] = _attrs_field(init=False, factory=dict) 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() @@ -29,7 +31,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: additional_properties = {} for prop_name, prop_dict in d.items(): - additional_property = isoparse(prop_dict) + additional_property = datetime.datetime.fromisoformat(prop_dict) additional_properties[prop_name] = additional_property 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 9073bf5af..622c94fa4 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,14 +19,14 @@ class ModelWithPropertyRef: """ Attributes: - inner (Union[Unset, ModelName]): + inner (ModelName | Unset): """ - inner: Union[Unset, "ModelName"] = UNSET + inner: ModelName | Unset = UNSET 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 + inner: dict[str, Any] | Unset = UNSET if not isinstance(self.inner, Unset): inner = self.inner.to_dict() @@ -38,11 +40,11 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.model_name import ModelName + from ..models.model_name import ModelName # noqa: PLC0415 d = dict(src_dict) _inner = d.pop("inner", UNSET) - inner: Union[Unset, ModelName] + inner: ModelName | Unset if isinstance(_inner, Unset): inner = UNSET else: 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 5167e7146..d4149434c 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,14 +15,14 @@ class ModelWithRecursiveRef: """ Attributes: - recursive (Union[Unset, ModelWithRecursiveRef]): + recursive (ModelWithRecursiveRef | Unset): """ - recursive: Union[Unset, "ModelWithRecursiveRef"] = UNSET + recursive: ModelWithRecursiveRef | Unset = UNSET 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 + recursive: dict[str, Any] | Unset = UNSET if not isinstance(self.recursive, Unset): recursive = self.recursive.to_dict() @@ -36,7 +38,7 @@ def to_dict(self) -> dict[str, Any]: def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) _recursive = d.pop("recursive", UNSET) - recursive: Union[Unset, ModelWithRecursiveRef] + recursive: ModelWithRecursiveRef | Unset if isinstance(_recursive, Unset): recursive = UNSET else: 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 208111a60..1b7b4d5ea 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -11,11 +13,12 @@ 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] = {} for prop_name, prop in self.additional_properties.items(): field_dict[prop_name] = prop.to_dict() @@ -40,10 +43,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> "ModelWithRecursiveRefInAdditionalProperties": + def __getitem__(self, key: str) -> ModelWithRecursiveRefInAdditionalProperties: return self.additional_properties[key] - def __setitem__(self, key: str, value: "ModelWithRecursiveRefInAdditionalProperties") -> None: + def __setitem__(self, key: str, value: ModelWithRecursiveRefInAdditionalProperties) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: 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 d704baada..176b3eadc 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define @@ -14,13 +16,13 @@ class ModelWithUnionProperty: """ Attributes: - a_property (Union[AnEnum, AnIntEnum, Unset]): + a_property (AnEnum | AnIntEnum | Unset): """ - a_property: Union[AnEnum, AnIntEnum, Unset] = UNSET + a_property: AnEnum | AnIntEnum | Unset = UNSET def to_dict(self) -> dict[str, Any]: - a_property: Union[Unset, int, str] + a_property: int | str | Unset if isinstance(self.a_property, Unset): a_property = UNSET elif isinstance(self.a_property, AnEnum): @@ -29,6 +31,7 @@ def to_dict(self) -> dict[str, Any]: a_property = self.a_property.value field_dict: dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property @@ -39,7 +42,7 @@ def to_dict(self) -> dict[str, Any]: def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) - def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]: + def _parse_a_property(data: object) -> AnEnum | AnIntEnum | Unset: if isinstance(data, Unset): return data try: @@ -48,7 +51,7 @@ def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]: a_property_type_0 = AnEnum(data) return a_property_type_0 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass if not isinstance(data, int): raise TypeError() 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 8011b7707..fbce31507 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,13 +1,15 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from ..types import UNSET, Unset if TYPE_CHECKING: - from ..models.model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0 - from ..models.model_with_union_property_inlined_fruit_type_1 import ModelWithUnionPropertyInlinedFruitType1 + from ..models.model_with_union_property_inlined_apples import ModelWithUnionPropertyInlinedApples + from ..models.model_with_union_property_inlined_bananas import ModelWithUnionPropertyInlinedBananas T = TypeVar("T", bound="ModelWithUnionPropertyInlined") @@ -17,23 +19,26 @@ class ModelWithUnionPropertyInlined: """ Attributes: - fruit (Union['ModelWithUnionPropertyInlinedFruitType0', 'ModelWithUnionPropertyInlinedFruitType1', Unset]): + fruit (ModelWithUnionPropertyInlinedApples | ModelWithUnionPropertyInlinedBananas | Unset): """ - fruit: Union["ModelWithUnionPropertyInlinedFruitType0", "ModelWithUnionPropertyInlinedFruitType1", Unset] = UNSET + fruit: ModelWithUnionPropertyInlinedApples | ModelWithUnionPropertyInlinedBananas | Unset = UNSET def to_dict(self) -> dict[str, Any]: - from ..models.model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0 + from ..models.model_with_union_property_inlined_apples import ( + ModelWithUnionPropertyInlinedApples, # noqa: PLC0415 + ) - fruit: Union[Unset, dict[str, Any]] + fruit: dict[str, Any] | Unset if isinstance(self.fruit, Unset): fruit = UNSET - elif isinstance(self.fruit, ModelWithUnionPropertyInlinedFruitType0): + elif isinstance(self.fruit, ModelWithUnionPropertyInlinedApples): fruit = self.fruit.to_dict() else: fruit = self.fruit.to_dict() field_dict: dict[str, Any] = {} + field_dict.update({}) if fruit is not UNSET: field_dict["fruit"] = fruit @@ -42,29 +47,33 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[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 + from ..models.model_with_union_property_inlined_apples import ( + ModelWithUnionPropertyInlinedApples, # noqa: PLC0415 + ) + from ..models.model_with_union_property_inlined_bananas import ( + ModelWithUnionPropertyInlinedBananas, # noqa: PLC0415 + ) d = dict(src_dict) def _parse_fruit( data: object, - ) -> Union["ModelWithUnionPropertyInlinedFruitType0", "ModelWithUnionPropertyInlinedFruitType1", Unset]: + ) -> ModelWithUnionPropertyInlinedApples | ModelWithUnionPropertyInlinedBananas | Unset: if isinstance(data, Unset): return data try: if not isinstance(data, dict): raise TypeError() - fruit_type_0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(data) + fruit_apples = ModelWithUnionPropertyInlinedApples.from_dict(data) - return fruit_type_0 - except: # noqa: E722 + return fruit_apples + except (TypeError, ValueError, AttributeError, KeyError): pass if not isinstance(data, dict): raise TypeError() - fruit_type_1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(data) + fruit_bananas = ModelWithUnionPropertyInlinedBananas.from_dict(data) - return fruit_type_1 + return fruit_bananas fruit = _parse_fruit(d.pop("fruit", UNSET)) 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_apples.py similarity index 74% rename from end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_0.py rename to end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_apples.py index 1822e85ef..0ad6f0cf2 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_apples.py @@ -1,22 +1,24 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field from ..types import UNSET, Unset -T = TypeVar("T", bound="ModelWithUnionPropertyInlinedFruitType0") +T = TypeVar("T", bound="ModelWithUnionPropertyInlinedApples") @_attrs_define -class ModelWithUnionPropertyInlinedFruitType0: +class ModelWithUnionPropertyInlinedApples: """ Attributes: - apples (Union[Unset, str]): + apples (str | Unset): """ - apples: Union[Unset, str] = UNSET + apples: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -35,12 +37,12 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) apples = d.pop("apples", UNSET) - model_with_union_property_inlined_fruit_type_0 = cls( + model_with_union_property_inlined_apples = cls( apples=apples, ) - model_with_union_property_inlined_fruit_type_0.additional_properties = d - return model_with_union_property_inlined_fruit_type_0 + model_with_union_property_inlined_apples.additional_properties = d + return model_with_union_property_inlined_apples @property def additional_keys(self) -> list[str]: 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_bananas.py similarity index 74% rename from end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type_1.py rename to end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_bananas.py index 389a791eb..68a54e760 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_bananas.py @@ -1,22 +1,24 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field from ..types import UNSET, Unset -T = TypeVar("T", bound="ModelWithUnionPropertyInlinedFruitType1") +T = TypeVar("T", bound="ModelWithUnionPropertyInlinedBananas") @_attrs_define -class ModelWithUnionPropertyInlinedFruitType1: +class ModelWithUnionPropertyInlinedBananas: """ Attributes: - bananas (Union[Unset, str]): + bananas (str | Unset): """ - bananas: Union[Unset, str] = UNSET + bananas: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -35,12 +37,12 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) bananas = d.pop("bananas", UNSET) - model_with_union_property_inlined_fruit_type_1 = cls( + model_with_union_property_inlined_bananas = cls( bananas=bananas, ) - model_with_union_property_inlined_fruit_type_1.additional_properties = d - return model_with_union_property_inlined_fruit_type_1 + model_with_union_property_inlined_bananas.additional_properties = d + return model_with_union_property_inlined_bananas @property def additional_keys(self) -> list[str]: 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 a19a8d1ce..a6956e860 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -14,6 +16,7 @@ class None_: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/octet_stream_tests_octet_stream_post_response_200.py b/end_to_end_tests/golden-record/my_test_api_client/models/octet_stream_tests_octet_stream_post_response_200.py new file mode 100644 index 000000000..8d138937a --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/octet_stream_tests_octet_stream_post_response_200.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from collections.abc import Mapping +from io import BytesIO +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, File, FileTypes, Unset + +T = TypeVar("T", bound="OctetStreamTestsOctetStreamPostResponse200") + + +@_attrs_define +class OctetStreamTestsOctetStreamPostResponse200: + """ + Attributes: + data (File | Unset): + """ + + data: File | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + data: FileTypes | Unset = UNSET + if not isinstance(self.data, Unset): + data = self.data.to_tuple() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if data is not UNSET: + field_dict["data"] = data + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + _data = d.pop("data", UNSET) + data: File | Unset + if isinstance(_data, Unset): + data = UNSET + else: + data = File(payload=BytesIO(_data)) + + octet_stream_tests_octet_stream_post_response_200 = cls( + data=data, + ) + + octet_stream_tests_octet_stream_post_response_200.additional_properties = d + return octet_stream_tests_octet_stream_post_response_200 + + @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/optional_body_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/optional_body_body.py new file mode 100644 index 000000000..1a214a7ad --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/optional_body_body.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="OptionalBodyBody") + + +@_attrs_define +class OptionalBodyBody: + """ """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + 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: Mapping[str, Any]) -> T: + d = dict(src_dict) + optional_body_body = cls() + + optional_body_body.additional_properties = d + return optional_body_body + + @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/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 c8f3d7a17..d83f8d7eb 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class PostBodiesMultipleDataBody: """ Attributes: - a (Union[Unset, str]): + a (str | Unset): """ - a: Union[Unset, str] = UNSET + a: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 5f4aefccc..81d7db7a7 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,9 +1,12 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field +from .. import types from ..types import UNSET, Unset T = TypeVar("T", bound="PostBodiesMultipleFilesBody") @@ -13,10 +16,10 @@ class PostBodiesMultipleFilesBody: """ Attributes: - a (Union[Unset, str]): + a (str | Unset): """ - a: Union[Unset, str] = UNSET + a: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -30,18 +33,16 @@ def to_dict(self) -> dict[str, Any]: return field_dict - def to_multipart(self) -> dict[str, Any]: - a = self.a if isinstance(self.a, Unset) else (None, str(self.a).encode(), "text/plain") + def to_multipart(self) -> types.RequestFiles: + files: types.RequestFiles = [] - field_dict: dict[str, Any] = {} - for prop_name, prop in self.additional_properties.items(): - field_dict[prop_name] = (None, str(prop).encode(), "text/plain") + if not isinstance(self.a, Unset): + files.append(("a", (None, str(self.a).encode(), "text/plain"))) - field_dict.update({}) - if a is not UNSET: - field_dict["a"] = a + for prop_name, prop in self.additional_properties.items(): + files.append((prop_name, (None, str(prop).encode(), "text/plain"))) - return field_dict + return files @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: 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 27b8299ea..a5ec152e9 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,10 +15,10 @@ class PostBodiesMultipleJsonBody: """ Attributes: - a (Union[Unset, str]): + a (str | Unset): """ - a: Union[Unset, str] = UNSET + a: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 1ab60c65c..5d7c3982b 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,11 +16,11 @@ class PostFormDataInlineBody: """ Attributes: a_required_field (str): - an_optional_field (Union[Unset, str]): + an_optional_field (str | Unset): """ a_required_field: str - an_optional_field: Union[Unset, str] = UNSET + an_optional_field: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 e6bc9e86e..2895d97d8 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,12 +15,12 @@ class PostNamingPropertyConflictWithImportBody: """ Attributes: - field (Union[Unset, str]): A python_name of field should not interfere with attrs field - define (Union[Unset, str]): A python_name of define should not interfere with attrs define + field (str | Unset): A python_name of field should not interfere with attrs field + define (str | Unset): A python_name of define should not interfere with attrs define """ - field: Union[Unset, str] = UNSET - define: Union[Unset, str] = UNSET + field: str | Unset = UNSET + define: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 3bfc1bf01..c2a8ac7f3 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,12 +15,12 @@ class PostNamingPropertyConflictWithImportResponse200: """ Attributes: - field (Union[Unset, str]): A python_name of field should not interfere with attrs field - define (Union[Unset, str]): A python_name of define should not interfere with attrs define + field (str | Unset): A python_name of field should not interfere with attrs field + define (str | Unset): A python_name of define should not interfere with attrs define """ - field: Union[Unset, str] = UNSET - define: Union[Unset, str] = UNSET + field: str | Unset = UNSET + define: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[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 66717f670..205a1cd30 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,18 +19,18 @@ class PostResponsesUnionsSimpleBeforeComplexResponse200: """ Attributes: - a (Union['PostResponsesUnionsSimpleBeforeComplexResponse200AType1', str]): + a (PostResponsesUnionsSimpleBeforeComplexResponse200AType1 | str): """ - a: Union["PostResponsesUnionsSimpleBeforeComplexResponse200AType1", str] + a: PostResponsesUnionsSimpleBeforeComplexResponse200AType1 | 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, + PostResponsesUnionsSimpleBeforeComplexResponse200AType1, # noqa: PLC0415 ) - a: Union[dict[str, Any], str] + a: dict[str, Any] | str if isinstance(self.a, PostResponsesUnionsSimpleBeforeComplexResponse200AType1): a = self.a.to_dict() else: @@ -47,21 +49,21 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.post_responses_unions_simple_before_complex_response_200a_type_1 import ( - PostResponsesUnionsSimpleBeforeComplexResponse200AType1, + PostResponsesUnionsSimpleBeforeComplexResponse200AType1, # noqa: PLC0415 ) d = dict(src_dict) - def _parse_a(data: object) -> Union["PostResponsesUnionsSimpleBeforeComplexResponse200AType1", str]: + def _parse_a(data: object) -> PostResponsesUnionsSimpleBeforeComplexResponse200AType1 | str: try: if not isinstance(data, dict): raise TypeError() a_type_1 = PostResponsesUnionsSimpleBeforeComplexResponse200AType1.from_dict(data) return a_type_1 - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(Union["PostResponsesUnionsSimpleBeforeComplexResponse200AType1", str], data) + return cast(PostResponsesUnionsSimpleBeforeComplexResponse200AType1 | str, data) a = _parse_a(d.pop("a")) 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_type_1.py index f2c1d3216..abceb221c 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_type_1.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar @@ -14,6 +16,7 @@ class PostResponsesUnionsSimpleBeforeComplexResponse200AType1: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_2xx.py b/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_2xx.py new file mode 100644 index 000000000..61e806ff9 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_2xx.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..models.status_code_patterns_response_2xx_status import StatusCodePatternsResponse2XXStatus +from ..types import UNSET, Unset + +T = TypeVar("T", bound="StatusCodePatternsResponse2XX") + + +@_attrs_define +class StatusCodePatternsResponse2XX: + """ + Attributes: + status (StatusCodePatternsResponse2XXStatus | Unset): + """ + + status: StatusCodePatternsResponse2XXStatus | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + status: str | Unset = UNSET + if not isinstance(self.status, Unset): + status = self.status.value + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if status is not UNSET: + field_dict["status"] = status + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + _status = d.pop("status", UNSET) + status: StatusCodePatternsResponse2XXStatus | Unset + if isinstance(_status, Unset): + status = UNSET + else: + status = StatusCodePatternsResponse2XXStatus(_status) + + status_code_patterns_response_2xx = cls( + status=status, + ) + + status_code_patterns_response_2xx.additional_properties = d + return status_code_patterns_response_2xx + + @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/status_code_patterns_response_2xx_status.py b/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_2xx_status.py new file mode 100644 index 000000000..9e871d06d --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_2xx_status.py @@ -0,0 +1,9 @@ +from enum import StrEnum + + +class StatusCodePatternsResponse2XXStatus(StrEnum): + FAILURE = "failure" + SUCCESS = "success" + + def __str__(self) -> str: + return str(self.value) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_4xx.py b/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_4xx.py new file mode 100644 index 000000000..f62c4168a --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/status_code_patterns_response_4xx.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="StatusCodePatternsResponse4XX") + + +@_attrs_define +class StatusCodePatternsResponse4XX: + """ + Attributes: + error (str | Unset): + """ + + error: str | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + error = self.error + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if error is not UNSET: + field_dict["error"] = error + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + error = d.pop("error", UNSET) + + status_code_patterns_response_4xx = cls( + error=error, + ) + + status_code_patterns_response_4xx.additional_properties = d + return status_code_patterns_response_4xx + + @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/test_inline_objects_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_body.py index e03ada2ef..73b054b24 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define @@ -12,15 +14,16 @@ class TestInlineObjectsBody: """ Attributes: - a_property (Union[Unset, str]): + a_property (str | Unset): """ - a_property: Union[Unset, str] = UNSET + a_property: str | Unset = UNSET def to_dict(self) -> dict[str, Any]: a_property = self.a_property field_dict: dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property 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 02d7888ea..174a5957b 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define @@ -12,15 +14,16 @@ class TestInlineObjectsResponse200: """ Attributes: - a_property (Union[Unset, str]): + a_property (str | Unset): """ - a_property: Union[Unset, str] = UNSET + a_property: str | Unset = UNSET def to_dict(self) -> dict[str, Any]: a_property = self.a_property field_dict: dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property 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 3c8fc9d04..9bb9070d6 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar, cast @@ -27,6 +29,7 @@ def to_dict(self) -> dict[str, Any]: type_ = self.type_ field_dict: dict[str, Any] = {} + field_dict.update( { "loc": loc, 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 b9ed58b8a..b64af0952 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,8 +1,8 @@ """Contains some shared types for properties""" -from collections.abc import MutableMapping +from collections.abc import Mapping, MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, Optional, TypeVar +from typing import IO, BinaryIO, Generic, Literal, TypeVar from attrs import define @@ -14,7 +14,15 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] +# The types that `httpx.Client(files=)` can accept, copied from that library. +FileContent = IO[bytes] | bytes | str +FileTypes = ( + # (filename, file (or bytes), content_type) + tuple[str | None, FileContent, str | None] + # (filename, file (or bytes), content_type, headers) + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) +RequestFiles = list[tuple[str, FileTypes]] @define @@ -22,10 +30,10 @@ class File: """Contains information for file uploads""" payload: BinaryIO - file_name: Optional[str] = None - mime_type: Optional[str] = None + file_name: str | None = None + mime_type: str | None = None - def to_tuple(self) -> FileJsonType: + def to_tuple(self) -> FileTypes: """Return a tuple representation that httpx will accept for multipart/form-data""" return self.file_name, self.payload, self.mime_type @@ -40,7 +48,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T | None -__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] +__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"] diff --git a/end_to_end_tests/golden-record/pyproject.toml b/end_to_end_tests/golden-record/pyproject.toml index feca06dbd..f2498ce1a 100644 --- a/end_to_end_tests/golden-record/pyproject.toml +++ b/end_to_end_tests/golden-record/pyproject.toml @@ -5,19 +5,17 @@ description = "A client library for accessing My Test API" authors = [] readme = "README.md" packages = [ - {include = "my_test_api_client"}, + { include = "my_test_api_client" }, ] -include = ["CHANGELOG.md", "my_test_api_client/py.typed"] - +include = ["my_test_api_client/py.typed"] [tool.poetry.dependencies] -python = "^3.9" -httpx = ">=0.20.0,<0.29.0" +python = "^3.11" +httpx = ">=0.23.1,<0.29.0" attrs = ">=22.2.0" -python-dateutil = "^2.8.0" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" [tool.ruff] diff --git a/end_to_end_tests/literal-enums-golden-record/README.md b/end_to_end_tests/literal-enums-golden-record/README.md index 2c6268349..025b5d402 100644 --- a/end_to_end_tests/literal-enums-golden-record/README.md +++ b/end_to_end_tests/literal-enums-golden-record/README.md @@ -47,7 +47,7 @@ By default, when you're calling an HTTPS API it will attempt to verify that SSL ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -57,7 +57,7 @@ You can also disable certificate validation altogether, but beware that **this i ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl=False ) 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 52385855c..53b574ebd 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, Optional, Union +from typing import Any import httpx @@ -12,6 +12,7 @@ def _get_kwargs( *, bool_enum: bool, ) -> dict[str, Any]: + params: dict[str, Any] = {} params["bool_enum"] = bool_enum @@ -27,16 +28,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -47,7 +49,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, bool_enum: bool, ) -> Response[Any]: """Bool Enum @@ -76,7 +78,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, bool_enum: bool, ) -> Response[Any]: """Bool Enum 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 af4c4ca22..f936104b5 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, Optional, Union +from typing import Any import httpx @@ -13,6 +13,7 @@ def _get_kwargs( *, int_enum: AnIntEnum, ) -> dict[str, Any]: + params: dict[str, Any] = {} json_int_enum: int = int_enum @@ -29,16 +30,17 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Any | None: 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]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -49,7 +51,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, int_enum: AnIntEnum, ) -> Response[Any]: """Int Enum @@ -78,7 +80,7 @@ def sync_detailed( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, int_enum: AnIntEnum, ) -> Response[Any]: """Int Enum 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 00bc801d9..15b821528 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, Optional, Union +from typing import Any import httpx @@ -18,10 +18,10 @@ def _get_kwargs( *, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[AnEnumWithNull | None], an_enum_value_with_only_null: list[None], - int_enum_header: Union[Unset, GetUserListIntEnumHeader] = UNSET, - string_enum_header: Union[Unset, GetUserListStringEnumHeader] = UNSET, + int_enum_header: GetUserListIntEnumHeader | Unset = UNSET, + string_enum_header: GetUserListStringEnumHeader | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} if not isinstance(int_enum_header, Unset): @@ -41,7 +41,7 @@ def _get_kwargs( json_an_enum_value_with_null = [] for an_enum_value_with_null_item_data in an_enum_value_with_null: - an_enum_value_with_null_item: Union[None, str] + an_enum_value_with_null_item: None | str if isinstance(an_enum_value_with_null_item_data, str): an_enum_value_with_null_item = an_enum_value_with_null_item_data else: @@ -66,9 +66,7 @@ def _get_kwargs( return _kwargs -def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[list["AModel"]]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> list[AModel] | None: if response.status_code == 200: response_200 = [] _response_200 = response.json() @@ -78,15 +76,14 @@ def _parse_response( response_200.append(response_200_item) 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[list["AModel"]]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[list[AModel]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -97,30 +94,30 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[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"]]: + int_enum_header: GetUserListIntEnumHeader | Unset = UNSET, + string_enum_header: GetUserListStringEnumHeader | Unset = UNSET, +) -> 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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - int_enum_header (Union[Unset, GetUserListIntEnumHeader]): - string_enum_header (Union[Unset, GetUserListStringEnumHeader]): + int_enum_header (GetUserListIntEnumHeader | Unset): + string_enum_header (GetUserListStringEnumHeader | Unset): 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[list['AModel']] + Response[list[AModel]] """ kwargs = _get_kwargs( @@ -140,30 +137,30 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[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"]]: + int_enum_header: GetUserListIntEnumHeader | Unset = UNSET, + string_enum_header: GetUserListStringEnumHeader | Unset = UNSET, +) -> list[AModel] | None: """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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - int_enum_header (Union[Unset, GetUserListIntEnumHeader]): - string_enum_header (Union[Unset, GetUserListStringEnumHeader]): + int_enum_header (GetUserListIntEnumHeader | Unset): + string_enum_header (GetUserListStringEnumHeader | Unset): 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: - list['AModel'] + list[AModel] """ return sync_detailed( @@ -178,30 +175,30 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[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"]]: + int_enum_header: GetUserListIntEnumHeader | Unset = UNSET, + string_enum_header: GetUserListStringEnumHeader | Unset = UNSET, +) -> 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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - int_enum_header (Union[Unset, GetUserListIntEnumHeader]): - string_enum_header (Union[Unset, GetUserListStringEnumHeader]): + int_enum_header (GetUserListIntEnumHeader | Unset): + string_enum_header (GetUserListStringEnumHeader | Unset): 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[list['AModel']] + Response[list[AModel]] """ kwargs = _get_kwargs( @@ -219,30 +216,30 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, an_enum_value: list[AnEnum], - an_enum_value_with_null: list[Union[AnEnumWithNull, None]], + an_enum_value_with_null: list[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"]]: + int_enum_header: GetUserListIntEnumHeader | Unset = UNSET, + string_enum_header: GetUserListStringEnumHeader | Unset = UNSET, +) -> list[AModel] | None: """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_null (list[AnEnumWithNull | None]): an_enum_value_with_only_null (list[None]): - int_enum_header (Union[Unset, GetUserListIntEnumHeader]): - string_enum_header (Union[Unset, GetUserListStringEnumHeader]): + int_enum_header (GetUserListIntEnumHeader | Unset): + string_enum_header (GetUserListStringEnumHeader | Unset): 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: - 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 82df1a8f8..911d81248 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, Optional, Union +from typing import Any import httpx @@ -7,12 +7,12 @@ from ...client import AuthenticatedClient, Client from ...models.a_model import AModel from ...models.post_user_list_body import PostUserListBody -from ...types import Response +from ...types import UNSET, Response, Unset def _get_kwargs( *, - body: PostUserListBody, + body: PostUserListBody | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -21,17 +21,16 @@ def _get_kwargs( "url": "/tests/", } - _body = body.to_multipart() + if not isinstance(body, Unset): + _kwargs["files"] = body.to_multipart() - _kwargs["files"] = _body + headers["Content-Type"] = "multipart/form-data; boundary=+++" _kwargs["headers"] = headers return _kwargs -def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[list["AModel"]]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> list[AModel] | None: if response.status_code == 200: response_200 = [] _response_200 = response.json() @@ -41,15 +40,14 @@ def _parse_response( response_200.append(response_200_item) 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[list["AModel"]]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[list[AModel]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -60,22 +58,22 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: PostUserListBody, -) -> Response[list["AModel"]]: + client: AuthenticatedClient | Client, + body: PostUserListBody | Unset = UNSET, +) -> Response[list[AModel]]: """Post List Post a list of things Args: - body (PostUserListBody): + body (PostUserListBody | Unset): 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[list['AModel']] + Response[list[AModel]] """ kwargs = _get_kwargs( @@ -91,22 +89,22 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], - body: PostUserListBody, -) -> Optional[list["AModel"]]: + client: AuthenticatedClient | Client, + body: PostUserListBody | Unset = UNSET, +) -> list[AModel] | None: """Post List Post a list of things Args: - body (PostUserListBody): + body (PostUserListBody | Unset): 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: - list['AModel'] + list[AModel] """ return sync_detailed( @@ -117,22 +115,22 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: PostUserListBody, -) -> Response[list["AModel"]]: + client: AuthenticatedClient | Client, + body: PostUserListBody | Unset = UNSET, +) -> Response[list[AModel]]: """Post List Post a list of things Args: - body (PostUserListBody): + body (PostUserListBody | Unset): 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[list['AModel']] + Response[list[AModel]] """ kwargs = _get_kwargs( @@ -146,22 +144,22 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], - body: PostUserListBody, -) -> Optional[list["AModel"]]: + client: AuthenticatedClient | Client, + body: PostUserListBody | Unset = UNSET, +) -> list[AModel] | None: """Post List Post a list of things Args: - body (PostUserListBody): + body (PostUserListBody | Unset): 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: - 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 e80446f10..edb246d7e 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, Optional, Union +from typing import Any, Self import httpx from attrs import define, evolve, field @@ -38,12 +38,12 @@ class Client: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" @@ -62,14 +62,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "Client": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "Client": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "Client": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -91,7 +91,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "Client": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -100,8 +100,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -122,7 +122,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "Client": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self @@ -168,12 +168,12 @@ class AuthenticatedClient: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) token: str prefix: str = "Bearer" @@ -196,14 +196,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -226,7 +226,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "AuthenticatedClient": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -235,8 +235,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -258,7 +258,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "AuthenticatedClient": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self 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 881286e74..890f7507f 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define @@ -18,16 +20,16 @@ class AModel: Attributes: an_enum_value (AnEnum): For testing Enums in all the ways they can be used 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]]]): + any_value (Any | Unset): + an_optional_allof_enum (AnAllOfEnum | Unset): + nested_list_of_enums (list[list[DifferentEnum]] | Unset): """ 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 + any_value: Any | Unset = UNSET + an_optional_allof_enum: AnAllOfEnum | Unset = UNSET + nested_list_of_enums: list[list[DifferentEnum]] | Unset = UNSET def to_dict(self) -> dict[str, Any]: an_enum_value: str = self.an_enum_value @@ -36,11 +38,11 @@ def to_dict(self) -> dict[str, Any]: any_value = self.any_value - an_optional_allof_enum: Union[Unset, str] = UNSET + an_optional_allof_enum: str | Unset = UNSET 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: list[list[str]] | Unset = 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: @@ -52,6 +54,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.update( { "an_enum_value": an_enum_value, @@ -77,23 +80,25 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: any_value = d.pop("any_value", UNSET) _an_optional_allof_enum = d.pop("an_optional_allof_enum", UNSET) - an_optional_allof_enum: Union[Unset, AnAllOfEnum] + an_optional_allof_enum: AnAllOfEnum | Unset if isinstance(_an_optional_allof_enum, Unset): an_optional_allof_enum = UNSET else: an_optional_allof_enum = check_an_all_of_enum(_an_optional_allof_enum) - nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) - for nested_list_of_enums_item_data in _nested_list_of_enums or []: - nested_list_of_enums_item = [] - _nested_list_of_enums_item = nested_list_of_enums_item_data - for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: - nested_list_of_enums_item_item = check_different_enum(nested_list_of_enums_item_item_data) + nested_list_of_enums: list[list[DifferentEnum]] | Unset = UNSET + if _nested_list_of_enums is not UNSET: + nested_list_of_enums = [] + for nested_list_of_enums_item_data in _nested_list_of_enums: + nested_list_of_enums_item = [] + _nested_list_of_enums_item = nested_list_of_enums_item_data + for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: + nested_list_of_enums_item_item = check_different_enum(nested_list_of_enums_item_item_data) - nested_list_of_enums_item.append(nested_list_of_enums_item_item) + nested_list_of_enums_item.append(nested_list_of_enums_item_item) - nested_list_of_enums.append(nested_list_of_enums_item) + nested_list_of_enums.append(nested_list_of_enums_item) a_model = cls( an_enum_value=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 3455e04d0..87efa8651 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,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal AnAllOfEnum = Literal["a_default", "bar", "foo", "overridden_default"] @@ -12,5 +12,5 @@ def check_an_all_of_enum(value: str) -> AnAllOfEnum: if value in AN_ALL_OF_ENUM_VALUES: - return cast(AnAllOfEnum, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {AN_ALL_OF_ENUM_VALUES!r}") 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 27b5c45f9..1438bc95a 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,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal AnEnum = Literal["FIRST_VALUE", "SECOND_VALUE"] @@ -10,5 +10,5 @@ def check_an_enum(value: str) -> AnEnum: if value in AN_ENUM_VALUES: - return cast(AnEnum, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {AN_ENUM_VALUES!r}") 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 4203876de..0592fa01a 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,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal AnEnumWithNull = Literal["FIRST_VALUE", "SECOND_VALUE"] @@ -10,5 +10,5 @@ def check_an_enum_with_null(value: str) -> AnEnumWithNull: if value in AN_ENUM_WITH_NULL_VALUES: - return cast(AnEnumWithNull, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {AN_ENUM_WITH_NULL_VALUES!r}") 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 9d0abd942..1ea0e852d 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,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal AnIntEnum = Literal[-1, 1, 2] @@ -11,5 +11,5 @@ def check_an_int_enum(value: int) -> AnIntEnum: if value in AN_INT_ENUM_VALUES: - return cast(AnIntEnum, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {AN_INT_ENUM_VALUES!r}") 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 e672a9821..086b9f490 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,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal DifferentEnum = Literal["DIFFERENT", "OTHER"] @@ -10,5 +10,5 @@ def check_different_enum(value: str) -> DifferentEnum: if value in DIFFERENT_ENUM_VALUES: - return cast(DifferentEnum, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {DIFFERENT_ENUM_VALUES!r}") 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 845d6c2a0..9dd4e6d02 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,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal GetUserListIntEnumHeader = Literal[1, 2, 3] @@ -11,5 +11,5 @@ def check_get_user_list_int_enum_header(value: int) -> GetUserListIntEnumHeader: if value in GET_USER_LIST_INT_ENUM_HEADER_VALUES: - return cast(GetUserListIntEnumHeader, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {GET_USER_LIST_INT_ENUM_HEADER_VALUES!r}") 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 55dbbad62..bfe72725c 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,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal GetUserListStringEnumHeader = Literal["one", "three", "two"] @@ -11,5 +11,5 @@ def check_get_user_list_string_enum_header(value: str) -> GetUserListStringEnumHeader: if value in GET_USER_LIST_STRING_ENUM_HEADER_VALUES: - return cast(GetUserListStringEnumHeader, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {GET_USER_LIST_STRING_ENUM_HEADER_VALUES!r}") 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 bfddb8c02..7e8e93950 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,10 +1,12 @@ -import json +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union, cast +from typing import Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field +from .. import types from ..models.an_all_of_enum import AnAllOfEnum, check_an_all_of_enum from ..models.an_enum import AnEnum, check_an_enum from ..models.an_enum_with_null import AnEnumWithNull, check_an_enum_with_null @@ -18,54 +20,54 @@ 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_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]]]): + an_enum_value (list[AnEnum] | Unset): + an_enum_value_with_null (list[AnEnumWithNull | None] | Unset): + an_enum_value_with_only_null (list[None] | Unset): + an_allof_enum_with_overridden_default (AnAllOfEnum | Unset): Default: 'overridden_default'. + an_optional_allof_enum (AnAllOfEnum | Unset): + nested_list_of_enums (list[list[DifferentEnum]] | 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 + an_enum_value: list[AnEnum] | Unset = UNSET + an_enum_value_with_null: list[AnEnumWithNull | None] | Unset = UNSET + an_enum_value_with_only_null: list[None] | Unset = UNSET + an_allof_enum_with_overridden_default: AnAllOfEnum | Unset = "overridden_default" + an_optional_allof_enum: AnAllOfEnum | Unset = UNSET + nested_list_of_enums: list[list[DifferentEnum]] | Unset = 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 + an_enum_value: list[str] | Unset = 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: list[None | str] | Unset = 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: - an_enum_value_with_null_item: Union[None, str] + an_enum_value_with_null_item: None | str if isinstance(an_enum_value_with_null_item_data, str): an_enum_value_with_null_item = an_enum_value_with_null_item_data else: 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: list[None] | Unset = 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 - an_allof_enum_with_overridden_default: Union[Unset, str] = UNSET + an_allof_enum_with_overridden_default: str | Unset = UNSET if not isinstance(self.an_allof_enum_with_overridden_default, Unset): an_allof_enum_with_overridden_default = self.an_allof_enum_with_overridden_default - an_optional_allof_enum: Union[Unset, str] = UNSET + an_optional_allof_enum: str | Unset = UNSET 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: list[list[str]] | Unset = 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: @@ -94,138 +96,129 @@ 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) -> types.RequestFiles: + files: types.RequestFiles = [] + if not isinstance(self.an_enum_value, Unset): - _temp_an_enum_value = [] - for an_enum_value_item_data in self.an_enum_value: - an_enum_value_item: str = an_enum_value_item_data - _temp_an_enum_value.append(an_enum_value_item) - an_enum_value = (None, json.dumps(_temp_an_enum_value).encode(), "application/json") + for an_enum_value_item_element in self.an_enum_value: + files.append(("an_enum_value", (None, str(an_enum_value_item_element).encode(), "text/plain"))) - 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: - an_enum_value_with_null_item: Union[None, str] - if isinstance(an_enum_value_with_null_item_data, str): - an_enum_value_with_null_item = an_enum_value_with_null_item_data + for an_enum_value_with_null_item_element in self.an_enum_value_with_null: + if an_enum_value_with_null_item_element is None: + files.append( + ( + "an_enum_value_with_null", + (None, str(an_enum_value_with_null_item_element).encode(), "text/plain"), + ) + ) else: - an_enum_value_with_null_item = an_enum_value_with_null_item_data - _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") + files.append( + ( + "an_enum_value_with_null", + (None, str(an_enum_value_with_null_item_element).encode(), "text/plain"), + ) + ) - 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 = ( - None, - json.dumps(_temp_an_enum_value_with_only_null).encode(), - "application/json", - ) + for an_enum_value_with_only_null_item_element in self.an_enum_value_with_only_null: + files.append( + ( + "an_enum_value_with_only_null", + (None, str(an_enum_value_with_only_null_item_element).encode(), "text/plain"), + ) + ) - 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, - str(self.an_allof_enum_with_overridden_default).encode(), - "text/plain", + files.append( + ( + "an_allof_enum_with_overridden_default", + (None, str(self.an_allof_enum_with_overridden_default).encode(), "text/plain"), + ) ) - 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") + files.append(("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 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: - nested_list_of_enums_item = [] - for nested_list_of_enums_item_item_data in nested_list_of_enums_item_data: - nested_list_of_enums_item_item: str = nested_list_of_enums_item_item_data - nested_list_of_enums_item.append(nested_list_of_enums_item_item) - - _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") + for nested_list_of_enums_item_element in self.nested_list_of_enums: + for nested_list_of_enums_item_item_element in nested_list_of_enums_item_element: + files.append( + ( + "nested_list_of_enums", + (None, str(nested_list_of_enums_item_item_element).encode(), "text/plain"), + ) + ) - field_dict: dict[str, Any] = {} for prop_name, prop in self.additional_properties.items(): - field_dict[prop_name] = (None, str(prop).encode(), "text/plain") + files.append((prop_name, (None, str(prop).encode(), "text/plain"))) - field_dict.update({}) - if an_enum_value is not UNSET: - field_dict["an_enum_value"] = an_enum_value - if an_enum_value_with_null is not UNSET: - field_dict["an_enum_value_with_null"] = an_enum_value_with_null - if an_enum_value_with_only_null is not UNSET: - field_dict["an_enum_value_with_only_null"] = an_enum_value_with_only_null - if an_allof_enum_with_overridden_default is not UNSET: - field_dict["an_allof_enum_with_overridden_default"] = an_allof_enum_with_overridden_default - if an_optional_allof_enum is not UNSET: - field_dict["an_optional_allof_enum"] = an_optional_allof_enum - if nested_list_of_enums is not UNSET: - field_dict["nested_list_of_enums"] = nested_list_of_enums - - return field_dict + return files @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) - an_enum_value = [] _an_enum_value = d.pop("an_enum_value", UNSET) - for an_enum_value_item_data in _an_enum_value or []: - an_enum_value_item = check_an_enum(an_enum_value_item_data) + an_enum_value: list[AnEnum] | Unset = UNSET + if _an_enum_value is not UNSET: + an_enum_value = [] + for an_enum_value_item_data in _an_enum_value: + an_enum_value_item = check_an_enum(an_enum_value_item_data) - an_enum_value.append(an_enum_value_item) + an_enum_value.append(an_enum_value_item) - an_enum_value_with_null = [] _an_enum_value_with_null = d.pop("an_enum_value_with_null", UNSET) - for an_enum_value_with_null_item_data in _an_enum_value_with_null or []: + an_enum_value_with_null: list[AnEnumWithNull | None] | Unset = UNSET + if _an_enum_value_with_null is not UNSET: + an_enum_value_with_null = [] + for an_enum_value_with_null_item_data in _an_enum_value_with_null: - def _parse_an_enum_value_with_null_item(data: object) -> Union[AnEnumWithNull, None]: - if data is None: - return data - try: - if not isinstance(data, str): - raise TypeError() - componentsschemas_an_enum_with_null_type_1 = check_an_enum_with_null(data) + def _parse_an_enum_value_with_null_item(data: object) -> AnEnumWithNull | None: + if data is None: + return data + try: + if not isinstance(data, str): + raise TypeError() + componentsschemas_an_enum_with_null_type_1 = check_an_enum_with_null(data) - return componentsschemas_an_enum_with_null_type_1 - except: # noqa: E722 - pass - return cast(Union[AnEnumWithNull, None], data) + return componentsschemas_an_enum_with_null_type_1 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(AnEnumWithNull | None, data) - an_enum_value_with_null_item = _parse_an_enum_value_with_null_item(an_enum_value_with_null_item_data) + an_enum_value_with_null_item = _parse_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_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_allof_enum_with_overridden_default = d.pop("an_allof_enum_with_overridden_default", UNSET) - an_allof_enum_with_overridden_default: Union[Unset, AnAllOfEnum] + an_allof_enum_with_overridden_default: AnAllOfEnum | Unset if isinstance(_an_allof_enum_with_overridden_default, Unset): an_allof_enum_with_overridden_default = UNSET else: an_allof_enum_with_overridden_default = check_an_all_of_enum(_an_allof_enum_with_overridden_default) _an_optional_allof_enum = d.pop("an_optional_allof_enum", UNSET) - an_optional_allof_enum: Union[Unset, AnAllOfEnum] + an_optional_allof_enum: AnAllOfEnum | Unset if isinstance(_an_optional_allof_enum, Unset): an_optional_allof_enum = UNSET else: an_optional_allof_enum = check_an_all_of_enum(_an_optional_allof_enum) - nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) - for nested_list_of_enums_item_data in _nested_list_of_enums or []: - nested_list_of_enums_item = [] - _nested_list_of_enums_item = nested_list_of_enums_item_data - for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: - nested_list_of_enums_item_item = check_different_enum(nested_list_of_enums_item_item_data) + nested_list_of_enums: list[list[DifferentEnum]] | Unset = UNSET + if _nested_list_of_enums is not UNSET: + nested_list_of_enums = [] + for nested_list_of_enums_item_data in _nested_list_of_enums: + nested_list_of_enums_item = [] + _nested_list_of_enums_item = nested_list_of_enums_item_data + for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: + nested_list_of_enums_item_item = check_different_enum(nested_list_of_enums_item_item_data) - nested_list_of_enums_item.append(nested_list_of_enums_item_item) + nested_list_of_enums_item.append(nested_list_of_enums_item_item) - nested_list_of_enums.append(nested_list_of_enums_item) + nested_list_of_enums.append(nested_list_of_enums_item) post_user_list_body = cls( an_enum_value=an_enum_value, 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 b9ed58b8a..b64af0952 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,8 +1,8 @@ """Contains some shared types for properties""" -from collections.abc import MutableMapping +from collections.abc import Mapping, MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, Optional, TypeVar +from typing import IO, BinaryIO, Generic, Literal, TypeVar from attrs import define @@ -14,7 +14,15 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] +# The types that `httpx.Client(files=)` can accept, copied from that library. +FileContent = IO[bytes] | bytes | str +FileTypes = ( + # (filename, file (or bytes), content_type) + tuple[str | None, FileContent, str | None] + # (filename, file (or bytes), content_type, headers) + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) +RequestFiles = list[tuple[str, FileTypes]] @define @@ -22,10 +30,10 @@ class File: """Contains information for file uploads""" payload: BinaryIO - file_name: Optional[str] = None - mime_type: Optional[str] = None + file_name: str | None = None + mime_type: str | None = None - def to_tuple(self) -> FileJsonType: + def to_tuple(self) -> FileTypes: """Return a tuple representation that httpx will accept for multipart/form-data""" return self.file_name, self.payload, self.mime_type @@ -40,7 +48,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T | None -__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] +__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "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 2c4d6b4e3..a7b5ed278 100644 --- a/end_to_end_tests/literal-enums-golden-record/pyproject.toml +++ b/end_to_end_tests/literal-enums-golden-record/pyproject.toml @@ -5,19 +5,17 @@ description = "A client library for accessing My Enum API" authors = [] readme = "README.md" packages = [ - {include = "my_enum_api_client"}, + { include = "my_enum_api_client" }, ] -include = ["CHANGELOG.md", "my_enum_api_client/py.typed"] - +include = ["my_enum_api_client/py.typed"] [tool.poetry.dependencies] -python = "^3.9" -httpx = ">=0.20.0,<0.29.0" +python = "^3.11" +httpx = ">=0.23.1,<0.29.0" attrs = ">=22.2.0" -python-dateutil = "^2.8.0" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" [tool.ruff] diff --git a/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml b/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml index c1f8a2a2b..c5262ff1e 100644 --- a/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml +++ b/end_to_end_tests/metadata_snapshots/pdm.pyproject.toml @@ -4,11 +4,10 @@ version = "0.1.0" description = "A client library for accessing Test 3.1 Features" authors = [] readme = "README.md" -requires-python = ">=3.9,<4.0" +requires-python = ">=3.11" dependencies = [ - "httpx>=0.20.0,<0.29.0", + "httpx>=0.23.1,<0.29.0", "attrs>=22.2.0", - "python-dateutil>=2.8.0", ] [tool.pdm] diff --git a/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml b/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml index 2e8cd6c04..39efe0b62 100644 --- a/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml +++ b/end_to_end_tests/metadata_snapshots/poetry.pyproject.toml @@ -5,19 +5,17 @@ description = "A client library for accessing Test 3.1 Features" authors = [] readme = "README.md" packages = [ - {include = "test_3_1_features_client"}, + { include = "test_3_1_features_client" }, ] -include = ["CHANGELOG.md", "test_3_1_features_client/py.typed"] - +include = ["test_3_1_features_client/py.typed"] [tool.poetry.dependencies] -python = "^3.9" -httpx = ">=0.20.0,<0.29.0" +python = "^3.11" +httpx = ">=0.23.1,<0.29.0" attrs = ">=22.2.0" -python-dateutil = "^2.8.0" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" [tool.ruff] diff --git a/end_to_end_tests/metadata_snapshots/setup.py b/end_to_end_tests/metadata_snapshots/setup.py index 6c7a58b97..96e9030b5 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.9, <4", - install_requires=["httpx >= 0.20.0, < 0.29.0", "attrs >= 22.2.0", "python-dateutil >= 2.8.0, < 3"], + python_requires=">=3.11, <4", + install_requires=["httpx >= 0.23.1, < 0.29.0", "attrs >= 22.2.0"], package_data={"test_3_1_features_client": ["py.typed"]}, ) diff --git a/end_to_end_tests/metadata_snapshots/uv.pyproject.toml b/end_to_end_tests/metadata_snapshots/uv.pyproject.toml new file mode 100644 index 000000000..553582723 --- /dev/null +++ b/end_to_end_tests/metadata_snapshots/uv.pyproject.toml @@ -0,0 +1,25 @@ +[project] +name = "test-3-1-features-client" +version = "0.1.0" +description = "A client library for accessing Test 3.1 Features" +authors = [] +requires-python = ">=3.11" +readme = "README.md" +dependencies = [ + "httpx>=0.23.1,<0.29.0", + "attrs>=22.2.0", +] + +[tool.uv.build-backend] +module-name = "test_3_1_features_client" +module-root = "" + +[build-system] +requires = ["uv_build>=0.12.0,<0.13.0"] +build-backend = "uv_build" + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = ["F", "I", "UP"] diff --git a/end_to_end_tests/regen_golden_record.py b/end_to_end_tests/regen_golden_record.py index ddac817ca..e01a84884 100644 --- a/end_to_end_tests/regen_golden_record.py +++ b/end_to_end_tests/regen_golden_record.py @@ -2,7 +2,6 @@ import filecmp import shutil from pathlib import Path -from typing import Optional from typer.testing import CliRunner @@ -13,9 +12,9 @@ def _regenerate( *, spec_file_name: str, output_dir: str = "my-test-api-client", - golden_record_dir: Optional[str] = None, + golden_record_dir: str | None = None, config_file_name: str = "config.yml", - extra_args: Optional[list[str]] = None + extra_args: list[str] | None = None ) -> None: end_to_end_tests_base_path = Path(__file__).parent project_base_path = end_to_end_tests_base_path.parent @@ -38,7 +37,7 @@ def _regenerate( if result.stdout: print(result.stdout) if result.exception: - raise result.exception + raise Exception(f"{result.exception} {result.stderr}") if golden_record_dir: gr_path = end_to_end_tests_base_path / golden_record_dir shutil.rmtree(gr_path, ignore_errors=True) @@ -73,7 +72,7 @@ def regen_metadata_snapshots(): output_path = Path.cwd() / "test-3-1-features-client" snapshots_dir = Path(__file__).parent / "metadata_snapshots" - for (meta, file, rename_to) in (("setup", "setup.py", "setup.py"), ("pdm", "pyproject.toml", "pdm.pyproject.toml"), ("poetry", "pyproject.toml", "poetry.pyproject.toml")): + for (meta, file, rename_to) in (("setup", "setup.py", "setup.py"), ("pdm", "pyproject.toml", "pdm.pyproject.toml"), ("poetry", "pyproject.toml", "poetry.pyproject.toml"), ("uv", "pyproject.toml", "uv.pyproject.toml")): _regenerate( spec_file_name="3.1_specific.openapi.yaml", output_dir="test-3-1-features-client", diff --git a/end_to_end_tests/test-3-1-golden-record/README.md b/end_to_end_tests/test-3-1-golden-record/README.md index dbe8a5c1e..352824967 100644 --- a/end_to_end_tests/test-3-1-golden-record/README.md +++ b/end_to_end_tests/test-3-1-golden-record/README.md @@ -47,7 +47,7 @@ By default, when you're calling an HTTPS API it will attempt to verify that SSL ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -57,7 +57,7 @@ You can also disable certificate validation altogether, but beware that **this i ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl=False ) 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 2e8cd6c04..39efe0b62 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 @@ -5,19 +5,17 @@ description = "A client library for accessing Test 3.1 Features" authors = [] readme = "README.md" packages = [ - {include = "test_3_1_features_client"}, + { include = "test_3_1_features_client" }, ] -include = ["CHANGELOG.md", "test_3_1_features_client/py.typed"] - +include = ["test_3_1_features_client/py.typed"] [tool.poetry.dependencies] -python = "^3.9" -httpx = ">=0.20.0,<0.29.0" +python = "^3.11" +httpx = ">=0.23.1,<0.29.0" attrs = ">=22.2.0" -python-dateutil = "^2.8.0" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" [tool.ruff] 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 0d05b8189..bf3472121 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,6 @@ from http import HTTPStatus -from typing import Any, Literal, Optional, Union, cast +from typing import Any, Literal, cast +from urllib.parse import quote import httpx @@ -14,7 +15,7 @@ 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, + optional_query: Literal["this sometimes goes in the query"] | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -28,13 +29,14 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "post", - "url": f"/const/{path}", + "url": "/const/{path}".format( + path=quote(str(path), safe=""), + ), "params": params, } - _body = body.to_dict() + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers @@ -42,8 +44,8 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Literal["Why have a fixed response? I dunno"]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Literal["Why have a fixed response? I dunno"] | None: if response.status_code == 200: response_200 = cast(Literal["Why have a fixed response? I dunno"], response.json()) if response_200 != "Why have a fixed response? I dunno": @@ -51,6 +53,7 @@ def _parse_response( f"response_200 must match const 'Why have a fixed response? I dunno', got '{response_200}'" ) return response_200 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -58,7 +61,7 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response + *, client: AuthenticatedClient | Client, response: httpx.Response ) -> Response[Literal["Why have a fixed response? I dunno"]]: return Response( status_code=HTTPStatus(response.status_code), @@ -71,16 +74,16 @@ def _build_response( def sync_detailed( path: Literal["this goes in the path"], *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostConstPathBody, required_query: Literal["this always goes in the query"], - optional_query: Union[Literal["this sometimes goes in the query"], Unset] = UNSET, + optional_query: Literal["this sometimes goes in the query"] | Unset = UNSET, ) -> Response[Literal["Why have a fixed response? I dunno"]]: """ Args: path (Literal['this goes in the path']): required_query (Literal['this always goes in the query']): - optional_query (Union[Literal['this sometimes goes in the query'], Unset]): + optional_query (Literal['this sometimes goes in the query'] | Unset): body (PostConstPathBody): Raises: @@ -108,16 +111,16 @@ def sync_detailed( def sync( path: Literal["this goes in the path"], *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostConstPathBody, required_query: Literal["this always goes in the query"], - optional_query: Union[Literal["this sometimes goes in the query"], Unset] = UNSET, -) -> Optional[Literal["Why have a fixed response? I dunno"]]: + optional_query: Literal["this sometimes goes in the query"] | Unset = UNSET, +) -> Literal["Why have a fixed response? I dunno"] | None: """ Args: path (Literal['this goes in the path']): required_query (Literal['this always goes in the query']): - optional_query (Union[Literal['this sometimes goes in the query'], Unset]): + optional_query (Literal['this sometimes goes in the query'] | Unset): body (PostConstPathBody): Raises: @@ -140,16 +143,16 @@ def sync( async def asyncio_detailed( path: Literal["this goes in the path"], *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostConstPathBody, required_query: Literal["this always goes in the query"], - optional_query: Union[Literal["this sometimes goes in the query"], Unset] = UNSET, + optional_query: Literal["this sometimes goes in the query"] | Unset = UNSET, ) -> Response[Literal["Why have a fixed response? I dunno"]]: """ Args: path (Literal['this goes in the path']): required_query (Literal['this always goes in the query']): - optional_query (Union[Literal['this sometimes goes in the query'], Unset]): + optional_query (Literal['this sometimes goes in the query'] | Unset): body (PostConstPathBody): Raises: @@ -175,16 +178,16 @@ async def asyncio_detailed( async def asyncio( path: Literal["this goes in the path"], *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostConstPathBody, required_query: Literal["this always goes in the query"], - optional_query: Union[Literal["this sometimes goes in the query"], Unset] = UNSET, -) -> Optional[Literal["Why have a fixed response? I dunno"]]: + optional_query: Literal["this sometimes goes in the query"] | Unset = UNSET, +) -> Literal["Why have a fixed response? I dunno"] | None: """ Args: path (Literal['this goes in the path']): required_query (Literal['this always goes in the query']): - optional_query (Union[Literal['this sometimes goes in the query'], Unset]): + optional_query (Literal['this sometimes goes in the query'] | Unset): body (PostConstPathBody): Raises: 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 f12d53f18..5b114873e 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, Optional, Union, cast +from typing import Any, cast import httpx @@ -20,26 +20,26 @@ def _get_kwargs( "url": "/prefixItems", } - _body = body.to_dict() + _kwargs["json"] = body.to_dict() - _kwargs["json"] = _body headers["Content-Type"] = "application/json" _kwargs["headers"] = headers return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[str]: +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> str | None: if response.status_code == 200: response_200 = cast(str, 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[str]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[str]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -50,7 +50,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostPrefixItemsBody, ) -> Response[str]: """ @@ -78,9 +78,9 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostPrefixItemsBody, -) -> Optional[str]: +) -> str | None: """ Args: body (PostPrefixItemsBody): @@ -101,7 +101,7 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostPrefixItemsBody, ) -> Response[str]: """ @@ -127,9 +127,9 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, body: PostPrefixItemsBody, -) -> Optional[str]: +) -> str | None: """ Args: body (PostPrefixItemsBody): 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 e80446f10..edb246d7e 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, Optional, Union +from typing import Any, Self import httpx from attrs import define, evolve, field @@ -38,12 +38,12 @@ class Client: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" @@ -62,14 +62,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "Client": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "Client": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "Client": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -91,7 +91,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "Client": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -100,8 +100,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -122,7 +122,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "Client": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self @@ -168,12 +168,12 @@ class AuthenticatedClient: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) token: str prefix: str = "Bearer" @@ -196,14 +196,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -226,7 +226,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "AuthenticatedClient": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -235,8 +235,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -258,7 +258,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "AuthenticatedClient": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self 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 3f910dc89..f59894f08 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, Literal, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -14,19 +16,19 @@ class PostConstPathBody: """ Attributes: 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]): + nullable (Literal['this or null goes in the body'] | None): + optional (Literal['this sometimes goes in the body'] | Unset): """ 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 + nullable: Literal["this or null goes in the body"] | None + optional: Literal["this sometimes goes in the body"] | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: required = self.required - nullable: Union[Literal["this or null goes in the body"], None] + nullable: Literal["this or null goes in the body"] | None nullable = self.nullable optional = self.optional @@ -51,7 +53,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: if required != "this always goes in the body": raise ValueError(f"required must match const 'this always goes in the body', got '{required}'") - def _parse_nullable(data: object) -> Union[Literal["this or null goes in the body"], None]: + def _parse_nullable(data: object) -> Literal["this or null goes in the body"] | None: if data is None: return data nullable_type_1 = cast(Literal["this or null goes in the body"], data) @@ -60,11 +62,11 @@ def _parse_nullable(data: object) -> Union[Literal["this or null goes in the bod f"nullable_type_1 must match const 'this or null goes in the body', got '{nullable_type_1}'" ) return nullable_type_1 - return cast(Union[Literal["this or null goes in the body"], None], data) + return cast(Literal["this or null goes in the body"] | None, data) nullable = _parse_nullable(d.pop("nullable")) - optional = cast(Union[Literal["this sometimes goes in the body"], Unset], d.pop("optional", UNSET)) + optional = cast(Literal["this sometimes goes in the body"] | Unset, d.pop("optional", UNSET)) if optional != "this sometimes goes in the body" and not isinstance(optional, Unset): raise ValueError(f"optional must match const 'this sometimes goes in the body', got '{optional}'") 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 655c607d8..b074bb717 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,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, Literal, TypeVar, Union, cast +from typing import Any, Literal, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,28 +15,28 @@ 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 (list[float | Literal['prefix'] | str] | Unset): + prefix_items_only (list[float | str] | Unset): """ - prefix_items_and_items: Union[Unset, list[Union[Literal["prefix"], float, str]]] = UNSET - prefix_items_only: Union[Unset, list[Union[float, str]]] = UNSET + prefix_items_and_items: list[float | Literal["prefix"] | str] | Unset = UNSET + prefix_items_only: list[float | str] | Unset = 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 + prefix_items_and_items: list[float | Literal["prefix"] | str] | Unset = 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: - prefix_items_and_items_item: Union[Literal["prefix"], float, str] + prefix_items_and_items_item: float | Literal["prefix"] | str 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: list[float | str] | Unset = UNSET if not isinstance(self.prefix_items_only, Unset): prefix_items_only = [] for prefix_items_only_item_data in self.prefix_items_only: - prefix_items_only_item: Union[float, str] + prefix_items_only_item: float | str prefix_items_only_item = prefix_items_only_item_data prefix_items_only.append(prefix_items_only_item) @@ -51,33 +53,37 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) - prefix_items_and_items = [] _prefix_items_and_items = d.pop("prefixItemsAndItems", UNSET) - for prefix_items_and_items_item_data in _prefix_items_and_items or []: + prefix_items_and_items: list[float | Literal["prefix"] | str] | Unset = UNSET + if _prefix_items_and_items is not UNSET: + prefix_items_and_items = [] + for prefix_items_and_items_item_data in _prefix_items_and_items: - def _parse_prefix_items_and_items_item(data: object) -> Union[Literal["prefix"], float, str]: - prefix_items_and_items_item_type_0 = cast(Literal["prefix"], data) - if prefix_items_and_items_item_type_0 != "prefix": - raise ValueError( - f"prefixItemsAndItems_item_type_0 must match const 'prefix', got '{prefix_items_and_items_item_type_0}'" - ) - return prefix_items_and_items_item_type_0 - return cast(Union[Literal["prefix"], float, str], data) + def _parse_prefix_items_and_items_item(data: object) -> float | Literal["prefix"] | str: + prefix_items_and_items_item_type_0 = cast(Literal["prefix"], data) + if prefix_items_and_items_item_type_0 != "prefix": + raise ValueError( + f"prefixItemsAndItems_item_type_0 must match const 'prefix', got '{prefix_items_and_items_item_type_0}'" + ) + return prefix_items_and_items_item_type_0 + return cast(float | Literal["prefix"] | str, data) - prefix_items_and_items_item = _parse_prefix_items_and_items_item(prefix_items_and_items_item_data) + prefix_items_and_items_item = _parse_prefix_items_and_items_item(prefix_items_and_items_item_data) - prefix_items_and_items.append(prefix_items_and_items_item) + prefix_items_and_items.append(prefix_items_and_items_item) - prefix_items_only = [] _prefix_items_only = d.pop("prefixItemsOnly", UNSET) - for prefix_items_only_item_data in _prefix_items_only or []: + prefix_items_only: list[float | str] | Unset = UNSET + if _prefix_items_only is not UNSET: + prefix_items_only = [] + for prefix_items_only_item_data in _prefix_items_only: - def _parse_prefix_items_only_item(data: object) -> Union[float, str]: - return cast(Union[float, str], data) + def _parse_prefix_items_only_item(data: object) -> float | str: + return cast(float | str, data) - prefix_items_only_item = _parse_prefix_items_only_item(prefix_items_only_item_data) + prefix_items_only_item = _parse_prefix_items_only_item(prefix_items_only_item_data) - prefix_items_only.append(prefix_items_only_item) + prefix_items_only.append(prefix_items_only_item) post_prefix_items_body = cls( prefix_items_and_items=prefix_items_and_items, 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 b9ed58b8a..b64af0952 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,8 +1,8 @@ """Contains some shared types for properties""" -from collections.abc import MutableMapping +from collections.abc import Mapping, MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, Optional, TypeVar +from typing import IO, BinaryIO, Generic, Literal, TypeVar from attrs import define @@ -14,7 +14,15 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] +# The types that `httpx.Client(files=)` can accept, copied from that library. +FileContent = IO[bytes] | bytes | str +FileTypes = ( + # (filename, file (or bytes), content_type) + tuple[str | None, FileContent, str | None] + # (filename, file (or bytes), content_type, headers) + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) +RequestFiles = list[tuple[str, FileTypes]] @define @@ -22,10 +30,10 @@ class File: """Contains information for file uploads""" payload: BinaryIO - file_name: Optional[str] = None - mime_type: Optional[str] = None + file_name: str | None = None + mime_type: str | None = None - def to_tuple(self) -> FileJsonType: + def to_tuple(self) -> FileTypes: """Return a tuple representation that httpx will accept for multipart/form-data""" return self.file_name, self.payload, self.mime_type @@ -40,7 +48,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T | None -__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] +__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"] diff --git a/end_to_end_tests/test_custom_templates/models_init.py.jinja b/end_to_end_tests/test_custom_templates/models_init.py.jinja new file mode 100644 index 000000000..8b0a55aee --- /dev/null +++ b/end_to_end_tests/test_custom_templates/models_init.py.jinja @@ -0,0 +1,33 @@ + +# Testing that we can access model-related information via Jinja variables. + +# To avoid having to update this file in the golden record every time the test specs are changed, +# we won't include all the classes in this output - we'll just look for one of them. + +# Using "alls" +{% for name in alls %} +{% if name == "AModel" %} +# {{ name }} +{% endif %} +{% endfor %} + +# Using "imports" +{% for import in imports %} +{% if import.endswith("import AModel") %} +# {{ import }} +{% endif %} +{% endfor %} + +# Using "openapi.models" +{% for model in openapi.models %} +{% if model.class_info.name == "AModel" %} +# {{ model.class_info.name }} ({{ model.class_info.module_name }}) +{% endif %} +{% endfor %} + +# Using "openapi.enums" +{% for enum in openapi.enums %} +{% if enum.class_info.name == "AnEnum" %} +# {{ enum.class_info.name }} ({{ enum.class_info.module_name }}) +{% endif %} +{% endfor %} diff --git a/end_to_end_tests/test_end_to_end.py b/end_to_end_tests/test_end_to_end.py index 044e72fdb..4e463670f 100644 --- a/end_to_end_tests/test_end_to_end.py +++ b/end_to_end_tests/test_end_to_end.py @@ -1,11 +1,11 @@ import shutil +import subprocess +import sys from filecmp import cmpfiles, dircmp from pathlib import Path -from typing import Optional import pytest -from click.testing import Result -from typer.testing import CliRunner +from typer.testing import CliRunner, Result from end_to_end_tests.generated_client import ( _run_command, generate_client, generate_client_from_inline_spec, @@ -16,8 +16,8 @@ def _compare_directories( record: Path, test_subject: Path, - expected_differences: Optional[dict[Path, str]] = None, - expected_missing: Optional[set[str]] = None, + expected_differences: dict[Path, str] | None = None, + expected_missing: set[str] | None = None, ignore: list[str] = None, depth=0, ): @@ -82,10 +82,10 @@ def _compare_directories( def run_e2e_test( openapi_document: str, extra_args: list[str], - expected_differences: Optional[dict[Path, str]] = None, + expected_differences: dict[Path, str] | None = None, golden_record_path: str = "golden-record", output_path: str = "my-test-api-client", - expected_missing: Optional[set[str]] = None, + expected_missing: set[str] | None = None, specify_output_path_explicitly: bool = True, ) -> Result: with generate_client(openapi_document, extra_args, output_path, specify_output_path_explicitly=specify_output_path_explicitly) as g: @@ -100,10 +100,12 @@ def run_e2e_test( gr_path, g.output_path, expected_differences=expected_differences, expected_missing=expected_missing ) - import mypy.api - - out, err, status = mypy.api.run([str(g.output_path), "--strict"]) - assert status == 0, f"Type checking client failed: {out}" + result = subprocess.run( + [sys.executable, "-m", "mypy", str(g.output_path), "--strict"], + capture_output=True, + text=True, + ) + assert result.returncode == 0, f"Type checking client failed: {result.stdout}" return g.generator_result @@ -143,9 +145,10 @@ def test_literal_enums_end_to_end(): ("setup", "setup.py", "setup.py"), ("pdm", "pyproject.toml", "pdm.pyproject.toml"), ("poetry", "pyproject.toml", "poetry.pyproject.toml"), + ("uv", "pyproject.toml", "uv.pyproject.toml"), ) ) -def test_meta(meta: str, generated_file: Optional[str], expected_file: Optional[str]): +def test_meta(meta: str, generated_file: str | None, expected_file: str | None): with generate_client( "3.1_specific.openapi.yaml", extra_args=[f"--meta={meta}"], @@ -185,6 +188,7 @@ def test_custom_templates(): {} ) # key: path relative to generated directory, value: expected generated content api_dir = Path("my_test_api_client").joinpath("api") + models_dir = Path("my_test_api_client").joinpath("models") golden_tpls_root_dir = Path(__file__).parent.joinpath( "custom-templates-golden-record" ) @@ -192,6 +196,7 @@ def test_custom_templates(): expected_difference_paths = [ Path("README.md"), api_dir.joinpath("__init__.py"), + models_dir.joinpath("__init__.py"), ] for expected_difference_path in expected_difference_paths: @@ -218,7 +223,7 @@ def test_bad_url(): runner = CliRunner() result = runner.invoke(app, ["generate", "--url=not_a_url"]) assert result.exit_code == 1 - assert "Could not get OpenAPI document from provided URL" in result.stdout + assert "Could not get OpenAPI document from provided URL" in result.stderr ERROR_DOCUMENTS = [path for path in Path(__file__).parent.joinpath("documents_with_errors").iterdir() if path.is_file()] @@ -234,7 +239,7 @@ def test_documents_with_errors(snapshot, document): ) as g: result = g.generator_result assert result.exit_code == 1 - output = result.stdout.replace(str(g.output_path), "/test-documents-with-errors") + output = (result.stdout + result.stderr).replace(str(g.output_path), "/test-documents-with-errors") assert output == snapshot @@ -246,7 +251,7 @@ def test_custom_post_hooks(): raise_on_error=False, ) as g: assert g.generator_result.exit_code == 1 - assert "this should fail" in g.generator_result.stdout + assert "this should fail" in g.generator_result.stderr def test_generate_dir_already_exists(): @@ -258,13 +263,13 @@ def test_generate_dir_already_exists(): 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 + assert "Directory already exists" in result.stderr finally: shutil.rmtree(Path.cwd() / "my-test-api-client", ignore_errors=True) def test_update_integration_tests(): - url = "https://raw.githubusercontent.com/openapi-generators/openapi-test-server/main/openapi.json" + url = "https://raw.githubusercontent.com/openapi-generators/openapi-test-server/refs/tags/v0.2.1/openapi.yaml" source_path = Path(__file__).parent.parent / "integration-tests" temp_dir = Path.cwd() / "test_update_integration_tests" shutil.rmtree(temp_dir, ignore_errors=True) @@ -279,10 +284,12 @@ def test_update_integration_tests(): config_path=config_path ) _compare_directories(source_path, temp_dir, ignore=["pyproject.toml"]) - import mypy.api - - out, err, status = mypy.api.run([str(temp_dir), "--strict"]) - assert status == 0, f"Type checking client failed: {out}" + result = subprocess.run( + [sys.executable, "-m", "mypy", str(temp_dir), "--strict"], + capture_output=True, + text=True, + ) + assert result.returncode == 0, f"Type checking client failed: {result.stdout=} {result.stderr=}" finally: shutil.rmtree(temp_dir) diff --git a/integration-tests/README.md b/integration-tests/README.md index f92972290..ec94ebc0a 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -47,7 +47,7 @@ By default, when you're calling an HTTPS API it will attempt to verify that SSL ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -57,7 +57,7 @@ You can also disable certificate validation altogether, but beware that **this i ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl=False ) 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 13c943ad7..7ceef67b6 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, Optional, Union +from typing import Any import httpx @@ -8,12 +8,12 @@ from ...models.post_body_multipart_body import PostBodyMultipartBody from ...models.post_body_multipart_response_200 import PostBodyMultipartResponse200 from ...models.public_error import PublicError -from ...types import Response +from ...types import UNSET, Response, Unset def _get_kwargs( *, - body: PostBodyMultipartBody, + body: PostBodyMultipartBody | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -22,25 +22,28 @@ def _get_kwargs( "url": "/body/multipart", } - _body = body.to_multipart() + if not isinstance(body, Unset): + _kwargs["files"] = body.to_multipart() - _kwargs["files"] = _body + headers["Content-Type"] = "multipart/form-data; boundary=+++" _kwargs["headers"] = headers return _kwargs def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[PostBodyMultipartResponse200, PublicError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> PostBodyMultipartResponse200 | PublicError | None: if response.status_code == 200: response_200 = PostBodyMultipartResponse200.from_dict(response.json()) return response_200 + if response.status_code == 400: response_400 = PublicError.from_dict(response.json()) return response_400 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -48,8 +51,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[PostBodyMultipartResponse200, PublicError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[PostBodyMultipartResponse200 | PublicError]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -60,19 +63,19 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], - body: PostBodyMultipartBody, -) -> Response[Union[PostBodyMultipartResponse200, PublicError]]: + client: AuthenticatedClient | Client, + body: PostBodyMultipartBody | Unset = UNSET, +) -> Response[PostBodyMultipartResponse200 | PublicError]: """ Args: - body (PostBodyMultipartBody): + body (PostBodyMultipartBody | Unset): 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[Union[PostBodyMultipartResponse200, PublicError]] + Response[PostBodyMultipartResponse200 | PublicError] """ kwargs = _get_kwargs( @@ -88,19 +91,19 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], - body: PostBodyMultipartBody, -) -> Optional[Union[PostBodyMultipartResponse200, PublicError]]: + client: AuthenticatedClient | Client, + body: PostBodyMultipartBody | Unset = UNSET, +) -> PostBodyMultipartResponse200 | PublicError | None: """ Args: - body (PostBodyMultipartBody): + body (PostBodyMultipartBody | Unset): 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: - Union[PostBodyMultipartResponse200, PublicError] + PostBodyMultipartResponse200 | PublicError """ return sync_detailed( @@ -111,19 +114,19 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], - body: PostBodyMultipartBody, -) -> Response[Union[PostBodyMultipartResponse200, PublicError]]: + client: AuthenticatedClient | Client, + body: PostBodyMultipartBody | Unset = UNSET, +) -> Response[PostBodyMultipartResponse200 | PublicError]: """ Args: - body (PostBodyMultipartBody): + body (PostBodyMultipartBody | Unset): 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[Union[PostBodyMultipartResponse200, PublicError]] + Response[PostBodyMultipartResponse200 | PublicError] """ kwargs = _get_kwargs( @@ -137,19 +140,19 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], - body: PostBodyMultipartBody, -) -> Optional[Union[PostBodyMultipartResponse200, PublicError]]: + client: AuthenticatedClient | Client, + body: PostBodyMultipartBody | Unset = UNSET, +) -> PostBodyMultipartResponse200 | PublicError | None: """ Args: - body (PostBodyMultipartBody): + body (PostBodyMultipartBody | Unset): 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: - Union[PostBodyMultipartResponse200, PublicError] + PostBodyMultipartResponse200 | PublicError """ return ( 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 0981585cc..190b7efe0 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, Optional, Union +from typing import Any import httpx @@ -36,16 +36,18 @@ def _get_kwargs( def _parse_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[PostParametersHeaderResponse200, PublicError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> PostParametersHeaderResponse200 | PublicError | None: if response.status_code == 200: response_200 = PostParametersHeaderResponse200.from_dict(response.json()) return response_200 + if response.status_code == 400: response_400 = PublicError.from_dict(response.json()) return response_400 + if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: @@ -53,8 +55,8 @@ def _parse_response( def _build_response( - *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[PostParametersHeaderResponse200, PublicError]]: + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[PostParametersHeaderResponse200 | PublicError]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -65,12 +67,12 @@ def _build_response( def sync_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, boolean_header: bool, string_header: str, number_header: float, integer_header: int, -) -> Response[Union[PostParametersHeaderResponse200, PublicError]]: +) -> Response[PostParametersHeaderResponse200 | PublicError]: """ Args: boolean_header (bool): @@ -83,7 +85,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[PostParametersHeaderResponse200, PublicError]] + Response[PostParametersHeaderResponse200 | PublicError] """ kwargs = _get_kwargs( @@ -102,12 +104,12 @@ def sync_detailed( def sync( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, boolean_header: bool, string_header: str, number_header: float, integer_header: int, -) -> Optional[Union[PostParametersHeaderResponse200, PublicError]]: +) -> PostParametersHeaderResponse200 | PublicError | None: """ Args: boolean_header (bool): @@ -120,7 +122,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[PostParametersHeaderResponse200, PublicError] + PostParametersHeaderResponse200 | PublicError """ return sync_detailed( @@ -134,12 +136,12 @@ def sync( async def asyncio_detailed( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, boolean_header: bool, string_header: str, number_header: float, integer_header: int, -) -> Response[Union[PostParametersHeaderResponse200, PublicError]]: +) -> Response[PostParametersHeaderResponse200 | PublicError]: """ Args: boolean_header (bool): @@ -152,7 +154,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[PostParametersHeaderResponse200, PublicError]] + Response[PostParametersHeaderResponse200 | PublicError] """ kwargs = _get_kwargs( @@ -169,12 +171,12 @@ async def asyncio_detailed( async def asyncio( *, - client: Union[AuthenticatedClient, Client], + client: AuthenticatedClient | Client, boolean_header: bool, string_header: str, number_header: float, integer_header: int, -) -> Optional[Union[PostParametersHeaderResponse200, PublicError]]: +) -> PostParametersHeaderResponse200 | PublicError | None: """ Args: boolean_header (bool): @@ -187,7 +189,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[PostParametersHeaderResponse200, PublicError] + PostParametersHeaderResponse200 | PublicError """ return ( diff --git a/integration-tests/integration_tests/client.py b/integration-tests/integration_tests/client.py index e80446f10..edb246d7e 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, Optional, Union +from typing import Any, Self import httpx from attrs import define, evolve, field @@ -38,12 +38,12 @@ class Client: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) def with_headers(self, headers: dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" @@ -62,14 +62,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "Client": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "Client": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "Client": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -91,7 +91,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "Client": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -100,8 +100,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -122,7 +122,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "Client": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self @@ -168,12 +168,12 @@ class AuthenticatedClient: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) token: str prefix: str = "Bearer" @@ -196,14 +196,14 @@ def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient": return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: self._async_client.timeout = timeout return evolve(self, timeout=timeout) - def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -226,7 +226,7 @@ def get_httpx_client(self) -> httpx.Client: ) return self._client - def __enter__(self) -> "AuthenticatedClient": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -235,8 +235,8 @@ def __exit__(self, *args: Any, **kwargs: Any) -> None: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -258,7 +258,7 @@ def get_async_httpx_client(self) -> httpx.AsyncClient: ) return self._async_client - async def __aenter__(self) -> "AuthenticatedClient": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self diff --git a/integration-tests/integration_tests/models/__init__.py b/integration-tests/integration_tests/models/__init__.py index 275cf6faa..257cfe9fa 100644 --- a/integration-tests/integration_tests/models/__init__.py +++ b/integration-tests/integration_tests/models/__init__.py @@ -1,5 +1,7 @@ """Contains all the data models used in inputs/outputs""" +from .an_object import AnObject +from .file import File from .post_body_multipart_body import PostBodyMultipartBody from .post_body_multipart_response_200 import PostBodyMultipartResponse200 from .post_parameters_header_response_200 import PostParametersHeaderResponse200 @@ -7,6 +9,8 @@ from .public_error import PublicError __all__ = ( + "AnObject", + "File", "PostBodyMultipartBody", "PostBodyMultipartResponse200", "PostParametersHeaderResponse200", diff --git a/integration-tests/integration_tests/models/an_object.py b/integration-tests/integration_tests/models/an_object.py new file mode 100644 index 000000000..228568b8c --- /dev/null +++ b/integration-tests/integration_tests/models/an_object.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="AnObject") + + +@_attrs_define +class AnObject: + """ + Attributes: + an_int (int): + a_float (float): + """ + + an_int: int + a_float: float + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + an_int = self.an_int + + a_float = self.a_float + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "an_int": an_int, + "a_float": a_float, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + an_int = d.pop("an_int") + + a_float = d.pop("a_float") + + an_object = cls( + an_int=an_int, + a_float=a_float, + ) + + an_object.additional_properties = d + return an_object + + @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/integration-tests/integration_tests/models/file.py b/integration-tests/integration_tests/models/file.py new file mode 100644 index 000000000..2380e8737 --- /dev/null +++ b/integration-tests/integration_tests/models/file.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="File") + + +@_attrs_define +class File: + """ + Attributes: + data (str | Unset): Echo of content of the 'file' input parameter from the form. + name (str | Unset): The name of the file uploaded. + content_type (str | Unset): The content type of the file uploaded. + """ + + data: str | Unset = UNSET + name: str | Unset = UNSET + content_type: str | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + data = self.data + + name = self.name + + content_type = self.content_type + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if data is not UNSET: + field_dict["data"] = data + if name is not UNSET: + field_dict["name"] = name + if content_type is not UNSET: + field_dict["content_type"] = content_type + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + data = d.pop("data", UNSET) + + name = d.pop("name", UNSET) + + content_type = d.pop("content_type", UNSET) + + file = cls( + data=data, + name=name, + content_type=content_type, + ) + + file.additional_properties = d + return file + + @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/integration-tests/integration_tests/models/post_body_multipart_body.py b/integration-tests/integration_tests/models/post_body_multipart_body.py index f2100a10e..0882f881b 100644 --- a/integration-tests/integration_tests/models/post_body_multipart_body.py +++ b/integration-tests/integration_tests/models/post_body_multipart_body.py @@ -1,11 +1,20 @@ +from __future__ import annotations + +import datetime +import json from collections.abc import Mapping from io import BytesIO -from typing import Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field -from ..types import UNSET, File, Unset +from .. import types +from ..types import File + +if TYPE_CHECKING: + from ..models.an_object import AnObject + T = TypeVar("T", bound="PostBodyMultipartBody") @@ -15,75 +24,111 @@ class PostBodyMultipartBody: """ Attributes: a_string (str): - file (File): For the sake of this test, include a file name and content type. The payload should also be valid - UTF-8. - description (Union[Unset, str]): + files (list[File]): + description (str): + objects (list[AnObject]): + times (list[datetime.datetime]): """ a_string: str - file: File - description: Union[Unset, str] = UNSET + files: list[File] + description: str + objects: list[AnObject] + times: list[datetime.datetime] additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: a_string = self.a_string - file = self.file.to_tuple() + files = [] + for files_item_data in self.files: + files_item = files_item_data.to_tuple() + + files.append(files_item) description = self.description + objects = [] + for objects_item_data in self.objects: + objects_item = objects_item_data.to_dict() + objects.append(objects_item) + + times = [] + for times_item_data in self.times: + times_item = times_item_data.isoformat() + times.append(times_item) + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { "a_string": a_string, - "file": file, + "files": files, + "description": description, + "objects": objects, + "times": times, } ) - if description is not UNSET: - field_dict["description"] = description return field_dict - def to_multipart(self) -> dict[str, Any]: - a_string = (None, str(self.a_string).encode(), "text/plain") + def to_multipart(self) -> types.RequestFiles: + files: types.RequestFiles = [] - file = self.file.to_tuple() + files.append(("a_string", (None, str(self.a_string).encode(), "text/plain"))) - description = ( - self.description - if isinstance(self.description, Unset) - else (None, str(self.description).encode(), "text/plain") - ) + for files_item_element in self.files: + files.append(("files", files_item_element.to_tuple())) - field_dict: dict[str, Any] = {} - for prop_name, prop in self.additional_properties.items(): - field_dict[prop_name] = (None, str(prop).encode(), "text/plain") + files.append(("description", (None, str(self.description).encode(), "text/plain"))) - field_dict.update( - { - "a_string": a_string, - "file": file, - } - ) - if description is not UNSET: - field_dict["description"] = description + for objects_item_element in self.objects: + files.append(("objects", (None, json.dumps(objects_item_element.to_dict()).encode(), "application/json"))) - return field_dict + for times_item_element in self.times: + files.append(("times", (None, times_item_element.isoformat().encode(), "text/plain"))) + + for prop_name, prop in self.additional_properties.items(): + files.append((prop_name, (None, str(prop).encode(), "text/plain"))) + + return files @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.an_object import AnObject # noqa: PLC0415 + d = dict(src_dict) a_string = d.pop("a_string") - file = File(payload=BytesIO(d.pop("file"))) + files = [] + _files = d.pop("files") + for files_item_data in _files: + files_item = File(payload=BytesIO(files_item_data)) + + files.append(files_item) + + description = d.pop("description") + + objects = [] + _objects = d.pop("objects") + for objects_item_data in _objects: + objects_item = AnObject.from_dict(objects_item_data) + + objects.append(objects_item) + + times = [] + _times = d.pop("times") + for times_item_data in _times: + times_item = datetime.datetime.fromisoformat(times_item_data) - description = d.pop("description", UNSET) + times.append(times_item) post_body_multipart_body = cls( a_string=a_string, - file=file, + files=files, description=description, + objects=objects, + times=times, ) post_body_multipart_body.additional_properties = d 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 9b4862f0b..be63512d2 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,9 +1,17 @@ +from __future__ import annotations + +import datetime from collections.abc import Mapping -from typing import Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field +if TYPE_CHECKING: + from ..models.an_object import AnObject + from ..models.file import File + + T = TypeVar("T", bound="PostBodyMultipartResponse200") @@ -12,39 +20,48 @@ class PostBodyMultipartResponse200: """ Attributes: a_string (str): Echo of the 'a_string' input parameter from the form. - file_data (str): Echo of content of the 'file' input parameter from the form. description (str): Echo of the 'description' input parameter from the form. - file_name (str): The name of the file uploaded. - file_content_type (str): The content type of the file uploaded. + files (list[File]): + times (list[datetime.datetime]): + objects (list[AnObject]): """ a_string: str - file_data: str description: str - file_name: str - file_content_type: str + files: list[File] + times: list[datetime.datetime] + objects: list[AnObject] additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: a_string = self.a_string - file_data = self.file_data - description = self.description - file_name = self.file_name + files = [] + for files_item_data in self.files: + files_item = files_item_data.to_dict() + files.append(files_item) + + times = [] + for times_item_data in self.times: + times_item = times_item_data.isoformat() + times.append(times_item) - file_content_type = self.file_content_type + objects = [] + for objects_item_data in self.objects: + objects_item = objects_item_data.to_dict() + objects.append(objects_item) field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { "a_string": a_string, - "file_data": file_data, "description": description, - "file_name": file_name, - "file_content_type": file_content_type, + "files": files, + "times": times, + "objects": objects, } ) @@ -52,23 +69,41 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.an_object import AnObject # noqa: PLC0415 + from ..models.file import File # noqa: PLC0415 + d = dict(src_dict) a_string = d.pop("a_string") - file_data = d.pop("file_data") - description = d.pop("description") - file_name = d.pop("file_name") + files = [] + _files = d.pop("files") + for files_item_data in _files: + files_item = File.from_dict(files_item_data) + + files.append(files_item) + + times = [] + _times = d.pop("times") + for times_item_data in _times: + times_item = datetime.datetime.fromisoformat(times_item_data) + + times.append(times_item) + + objects = [] + _objects = d.pop("objects") + for objects_item_data in _objects: + objects_item = AnObject.from_dict(objects_item_data) - file_content_type = d.pop("file_content_type") + objects.append(objects_item) post_body_multipart_response_200 = cls( a_string=a_string, - file_data=file_data, description=description, - file_name=file_name, - file_content_type=file_content_type, + files=files, + times=times, + objects=objects, ) post_body_multipart_response_200.additional_properties = d 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 c97e4d9d3..d5baeabbd 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,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Mapping from typing import Any, TypeVar diff --git a/integration-tests/integration_tests/models/problem.py b/integration-tests/integration_tests/models/problem.py index 93f48173a..a7f270874 100644 --- a/integration-tests/integration_tests/models/problem.py +++ b/integration-tests/integration_tests/models/problem.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Union +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -13,12 +15,12 @@ class Problem: """ Attributes: - parameter_name (Union[Unset, str]): - description (Union[Unset, str]): + parameter_name (str | Unset): + description (str | Unset): """ - parameter_name: Union[Unset, str] = UNSET - description: Union[Unset, str] = UNSET + parameter_name: str | Unset = UNSET + description: str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: diff --git a/integration-tests/integration_tests/models/public_error.py b/integration-tests/integration_tests/models/public_error.py index 6bd821a62..b4ca256d2 100644 --- a/integration-tests/integration_tests/models/public_error.py +++ b/integration-tests/integration_tests/models/public_error.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -17,35 +19,35 @@ 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 (list[str] | Unset): + extra_parameters (list[str] | Unset): + invalid_parameters (list[Problem] | Unset): + missing_parameters (list[str] | Unset): """ - 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 + errors: list[str] | Unset = UNSET + extra_parameters: list[str] | Unset = UNSET + invalid_parameters: list[Problem] | Unset = UNSET + missing_parameters: list[str] | Unset = 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 + errors: list[str] | Unset = UNSET if not isinstance(self.errors, Unset): errors = self.errors - extra_parameters: Union[Unset, list[str]] = UNSET + extra_parameters: list[str] | Unset = UNSET if not isinstance(self.extra_parameters, Unset): extra_parameters = self.extra_parameters - invalid_parameters: Union[Unset, list[dict[str, Any]]] = UNSET + invalid_parameters: list[dict[str, Any]] | Unset = 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: list[str] | Unset = UNSET if not isinstance(self.missing_parameters, Unset): missing_parameters = self.missing_parameters @@ -65,19 +67,21 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.problem import Problem + from ..models.problem import Problem # noqa: PLC0415 d = dict(src_dict) errors = cast(list[str], d.pop("errors", UNSET)) extra_parameters = cast(list[str], d.pop("extra_parameters", UNSET)) - invalid_parameters = [] _invalid_parameters = d.pop("invalid_parameters", UNSET) - for invalid_parameters_item_data in _invalid_parameters or []: - invalid_parameters_item = Problem.from_dict(invalid_parameters_item_data) + invalid_parameters: list[Problem] | Unset = UNSET + if _invalid_parameters is not UNSET: + invalid_parameters = [] + for invalid_parameters_item_data in _invalid_parameters: + invalid_parameters_item = Problem.from_dict(invalid_parameters_item_data) - invalid_parameters.append(invalid_parameters_item) + invalid_parameters.append(invalid_parameters_item) missing_parameters = cast(list[str], d.pop("missing_parameters", UNSET)) diff --git a/integration-tests/integration_tests/types.py b/integration-tests/integration_tests/types.py index b9ed58b8a..b64af0952 100644 --- a/integration-tests/integration_tests/types.py +++ b/integration-tests/integration_tests/types.py @@ -1,8 +1,8 @@ """Contains some shared types for properties""" -from collections.abc import MutableMapping +from collections.abc import Mapping, MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Literal, Optional, TypeVar +from typing import IO, BinaryIO, Generic, Literal, TypeVar from attrs import define @@ -14,7 +14,15 @@ def __bool__(self) -> Literal[False]: UNSET: Unset = Unset() -FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] +# The types that `httpx.Client(files=)` can accept, copied from that library. +FileContent = IO[bytes] | bytes | str +FileTypes = ( + # (filename, file (or bytes), content_type) + tuple[str | None, FileContent, str | None] + # (filename, file (or bytes), content_type, headers) + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) +RequestFiles = list[tuple[str, FileTypes]] @define @@ -22,10 +30,10 @@ class File: """Contains information for file uploads""" payload: BinaryIO - file_name: Optional[str] = None - mime_type: Optional[str] = None + file_name: str | None = None + mime_type: str | None = None - def to_tuple(self) -> FileJsonType: + def to_tuple(self) -> FileTypes: """Return a tuple representation that httpx will accept for multipart/form-data""" return self.file_name, self.payload, self.mime_type @@ -40,7 +48,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T | None -__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] +__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"] diff --git a/integration-tests/pdm.lock b/integration-tests/pdm.lock index b5b0d9c9c..ffae47bd6 100644 --- a/integration-tests/pdm.lock +++ b/integration-tests/pdm.lock @@ -5,48 +5,89 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:a575c5fc1f04f52530c52becbcc2f40498a54612d0eeeaddf6701fe5336986ac" +content_hash = "sha256:eaf8c822d09eaa6cb6b1cb2ac7deb1746c24e404459a1a79f5c35df71b8883c7" [[metadata.targets]] -requires_python = "~=3.9" +requires_python = "~=3.11" [[package]] name = "anyio" -version = "4.9.0" -requires_python = ">=3.9" -summary = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.14.0" +requires_python = ">=3.10" +summary = "High-level concurrency and networking framework on top of asyncio or Trio" groups = ["default"] dependencies = [ "exceptiongroup>=1.0.2; python_version < \"3.11\"", "idna>=2.8", - "sniffio>=1.1", "typing-extensions>=4.5; python_version < \"3.13\"", ] files = [ - {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, - {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, + {file = "anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9"}, + {file = "anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89"}, +] + +[[package]] +name = "ast-serialize" +version = "0.5.0" +requires_python = ">=3.7" +summary = "Python bindings for mypy AST serialization" +groups = ["dev"] +files = [ + {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb"}, + {file = "ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101"}, + {file = "ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d"}, + {file = "ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a"}, + {file = "ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590"}, + {file = "ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642"}, + {file = "ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6"}, ] [[package]] name = "attrs" -version = "25.3.0" -requires_python = ">=3.8" +version = "26.1.0" +requires_python = ">=3.9" summary = "Classes Without Boilerplate" groups = ["default"] files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, + {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"}, + {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"}, ] [[package]] name = "certifi" -version = "2025.1.31" -requires_python = ">=3.6" +version = "2026.6.17" +requires_python = ">=3.7" summary = "Python package for providing Mozilla's CA Bundle." groups = ["default"] files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, + {file = "certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db"}, + {file = "certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432"}, ] [[package]] @@ -61,45 +102,30 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[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" +version = "0.16.0" +requires_python = ">=3.8" 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"}, + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] [[package]] name = "httpcore" -version = "1.0.7" +version = "1.0.9" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." groups = ["default"] dependencies = [ "certifi", - "h11<0.15,>=0.13", + "h11>=0.16", ] files = [ - {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, - {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, ] [[package]] @@ -121,223 +147,255 @@ files = [ [[package]] name = "idna" -version = "3.10" -requires_python = ">=3.6" +version = "3.18" +requires_python = ">=3.9" 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"}, + {file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"}, + {file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"}, ] [[package]] name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" +version = "2.3.0" +requires_python = ">=3.10" 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"}, + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] [[package]] -name = "mypy" -version = "1.15.0" +name = "librt" +version = "0.11.0" requires_python = ">=3.9" +summary = "Mypyc runtime library" +groups = ["dev"] +marker = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29"}, + {file = "librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89"}, + {file = "librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412"}, + {file = "librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d"}, + {file = "librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73"}, + {file = "librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c"}, + {file = "librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46"}, + {file = "librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a"}, + {file = "librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8"}, + {file = "librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a"}, + {file = "librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9"}, + {file = "librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c"}, + {file = "librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894"}, + {file = "librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2"}, + {file = "librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e"}, + {file = "librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e"}, + {file = "librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47"}, + {file = "librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44"}, + {file = "librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd"}, + {file = "librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175"}, + {file = "librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe"}, + {file = "librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f"}, + {file = "librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7"}, + {file = "librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1"}, + {file = "librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72"}, + {file = "librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd"}, + {file = "librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8"}, + {file = "librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c"}, + {file = "librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253"}, + {file = "librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1"}, +] + +[[package]] +name = "mypy" +version = "2.1.0" +requires_python = ">=3.10" summary = "Optional static typing for Python" groups = ["dev"] dependencies = [ + "ast-serialize<1.0.0,>=0.3.0", + "librt>=0.11.0; platform_python_implementation != \"PyPy\"", "mypy-extensions>=1.0.0", + "pathspec>=1.0.0", "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.6.0", + "typing-extensions>=4.14.0; python_version >= \"3.15\"", + "typing-extensions>=4.6.0; python_version < \"3.15\"", ] files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, + {file = "mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41"}, + {file = "mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca"}, + {file = "mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538"}, + {file = "mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398"}, + {file = "mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563"}, + {file = "mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389"}, + {file = "mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666"}, + {file = "mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af"}, + {file = "mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6"}, + {file = "mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211"}, + {file = "mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b"}, + {file = "mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22"}, + {file = "mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b"}, + {file = "mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8"}, + {file = "mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5"}, + {file = "mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e"}, + {file = "mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e"}, + {file = "mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285"}, + {file = "mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5"}, + {file = "mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65"}, + {file = "mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d"}, + {file = "mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2"}, + {file = "mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f"}, + {file = "mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4"}, + {file = "mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef"}, + {file = "mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135"}, + {file = "mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21"}, + {file = "mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57"}, + {file = "mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e"}, + {file = "mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780"}, + {file = "mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd"}, + {file = "mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08"}, + {file = "mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081"}, + {file = "mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7"}, + {file = "mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6"}, + {file = "mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289"}, + {file = "mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633"}, ] [[package]] name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" +version = "1.1.0" +requires_python = ">=3.8" 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"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] name = "packaging" -version = "24.2" +version = "26.2" requires_python = ">=3.8" summary = "Core utilities for Python packages" groups = ["dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, ] [[package]] -name = "pluggy" -version = "1.5.0" -requires_python = ">=3.8" -summary = "plugin and hook calling mechanisms for python" +name = "pathspec" +version = "1.1.1" +requires_python = ">=3.9" +summary = "Utility library for gitignore style pattern matching of file paths." 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"}, + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, ] [[package]] -name = "pytest" -version = "8.3.5" -requires_python = ">=3.8" -summary = "pytest: simple powerful testing with Python" +name = "pluggy" +version = "1.6.0" +requires_python = ">=3.9" +summary = "plugin and hook calling mechanisms for 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.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [[package]] -name = "pytest-asyncio" -version = "0.25.3" +name = "pygments" +version = "2.20.0" requires_python = ">=3.9" -summary = "Pytest support for asyncio" +summary = "Pygments is a syntax highlighting package written in Python." groups = ["dev"] -dependencies = [ - "pytest<9,>=8.2", -] files = [ - {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, - {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, ] [[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"] +name = "pytest" +version = "9.1.1" +requires_python = ">=3.10" +summary = "pytest: simple powerful testing with Python" +groups = ["dev"] 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 = "six" -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.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, + "colorama>=0.4; sys_platform == \"win32\"", + "exceptiongroup>=1; python_version < \"3.11\"", + "iniconfig>=1.0.1", + "packaging>=22", + "pluggy<2,>=1.5", + "pygments>=2.7.2", + "tomli>=1; python_version < \"3.11\"", ] - -[[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"}, + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, ] [[package]] -name = "tomli" -version = "2.2.1" -requires_python = ">=3.8" -summary = "A lil' TOML parser" +name = "pytest-asyncio" +version = "1.4.0" +requires_python = ">=3.10" +summary = "Pytest support for asyncio" groups = ["dev"] -marker = "python_version < \"3.11\"" +dependencies = [ + "backports-asyncio-runner<2,>=1.1; python_version < \"3.11\"", + "pytest<10,>=8.4", + "typing-extensions>=4.12; python_version < \"3.13\"", +] 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"}, + {file = "pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1"}, + {file = "pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42"}, ] [[package]] name = "typing-extensions" -version = "4.12.2" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.15.0" +requires_python = ">=3.9" +summary = "Backported and Experimental Type Hints for Python 3.9+" 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"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] diff --git a/integration-tests/pdm.minimal.lock b/integration-tests/pdm.minimal.lock new file mode 100644 index 000000000..52a719b9f --- /dev/null +++ b/integration-tests/pdm.minimal.lock @@ -0,0 +1,275 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "dev"] +strategy = ["direct_minimal_versions", "inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:eaf8c822d09eaa6cb6b1cb2ac7deb1746c24e404459a1a79f5c35df71b8883c7" + +[[metadata.targets]] +requires_python = "~=3.11" + +[[package]] +name = "anyio" +version = "4.13.0" +requires_python = ">=3.10" +summary = "High-level concurrency and networking framework on top of asyncio or Trio" +groups = ["default"] +dependencies = [ + "exceptiongroup>=1.0.2; python_version < \"3.11\"", + "idna>=2.8", + "typing-extensions>=4.5; python_version < \"3.13\"", +] +files = [ + {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, + {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, +] + +[[package]] +name = "attrs" +version = "22.2.0" +requires_python = ">=3.6" +summary = "Classes Without Boilerplate" +groups = ["default"] +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +requires_python = ">=3.7" +summary = "Python package for providing Mozilla's CA Bundle." +groups = ["default"] +files = [ + {file = "certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897"}, + {file = "certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"}, +] + +[[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 = ["dev"] +marker = "sys_platform == \"win32\"" +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 = "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 = "0.16.3" +requires_python = ">=3.7" +summary = "A minimal low-level HTTP client." +groups = ["default"] +dependencies = [ + "anyio<5.0,>=3.0", + "certifi", + "h11<0.15,>=0.13", + "sniffio==1.*", +] +files = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] + +[[package]] +name = "httpx" +version = "0.23.1" +requires_python = ">=3.7" +summary = "The next generation HTTP client." +groups = ["default"] +dependencies = [ + "certifi", + "httpcore<0.17.0,>=0.15.0", + "rfc3986[idna2008]<2,>=1.3", + "sniffio", +] +files = [ + {file = "httpx-0.23.1-py3-none-any.whl", hash = "sha256:0b9b1f0ee18b9978d637b0776bfd7f54e2ca278e063e3586d8f01cda89e042a8"}, + {file = "httpx-0.23.1.tar.gz", hash = "sha256:202ae15319be24efe9a8bd4ed4360e68fde7b38bcc2ce87088d416f026667d19"}, +] + +[[package]] +name = "idna" +version = "3.17" +requires_python = ">=3.9" +summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["default"] +files = [ + {file = "idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c"}, + {file = "idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f"}, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +requires_python = ">=3.10" +summary = "brain-dead simple config-ini parsing" +groups = ["dev"] +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + +[[package]] +name = "mypy" +version = "1.13.0" +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.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +requires_python = ">=3.8" +summary = "Type system extensions for programs checked with the mypy type checker." +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "26.2" +requires_python = ">=3.8" +summary = "Core utilities for Python packages" +groups = ["dev"] +files = [ + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +requires_python = ">=3.9" +summary = "plugin and hook calling mechanisms for python" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[[package]] +name = "pytest" +version = "8.0.1" +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.0,>=1.3.0", + "tomli>=1.0.0; python_version < \"3.11\"", +] +files = [ + {file = "pytest-8.0.1-py3-none-any.whl", hash = "sha256:3e4f16fe1c0a9dc9d9389161c127c3edc5d810c38d6793042fb81d9f48a59fca"}, + {file = "pytest-8.0.1.tar.gz", hash = "sha256:267f6563751877d772019b13aacbe4e860d73fe8f651f28112e9ac37de7513ae"}, +] + +[[package]] +name = "pytest-asyncio" +version = "0.23.5" +requires_python = ">=3.8" +summary = "Pytest support for asyncio" +groups = ["dev"] +dependencies = [ + "pytest<9,>=7.0.0", +] +files = [ + {file = "pytest-asyncio-0.23.5.tar.gz", hash = "sha256:3a048872a9c4ba14c3e90cc1aa20cbc2def7d01c7c8db3777ec281ba9c057675"}, + {file = "pytest_asyncio-0.23.5-py3-none-any.whl", hash = "sha256:4e7093259ba018d58ede7d5315131d21923a60f8a6e9ee266ce1589685c89eac"}, +] + +[[package]] +name = "rfc3986" +version = "1.5.0" +summary = "Validating URI References per RFC 3986" +groups = ["default"] +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[[package]] +name = "rfc3986" +version = "1.5.0" +extras = ["idna2008"] +summary = "Validating URI References per RFC 3986" +groups = ["default"] +dependencies = [ + "idna", + "rfc3986==1.5.0", +] +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[[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 = "typing-extensions" +version = "4.15.0" +requires_python = ">=3.9" +summary = "Backported and Experimental Type Hints for Python 3.9+" +groups = ["default", "dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] diff --git a/integration-tests/pyproject.toml b/integration-tests/pyproject.toml index 536a55b32..0811b4b42 100644 --- a/integration-tests/pyproject.toml +++ b/integration-tests/pyproject.toml @@ -1,14 +1,13 @@ [project] name = "integration-tests" -version = "0.0.1" +version = "0.1.0" description = "A client library for accessing OpenAPI Test Server" authors = [] readme = "README.md" -requires-python = ">=3.9,<4.0" +requires-python = ">=3.11,<4.0" dependencies = [ - "httpx>=0.20.0,<0.29.0", + "httpx>=0.23.1,<0.29.0", "attrs>=22.2.0", - "python-dateutil>=2.8.0", ] [tool.pdm] @@ -16,15 +15,15 @@ distribution = true [tool.pdm.dev-dependencies] dev = [ - "pytest", - "mypy", + "pytest>8", + "mypy>=1.13", "pytest-asyncio>=0.23.5", ] [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" - + [tool.ruff] line-length = 120 @@ -32,4 +31,4 @@ line-length = 120 select = ["F", "I"] [tool.mypy] -# Just to get mypy to _not_ look at the parent directory's config \ No newline at end of file +# Just to get mypy to _not_ look at the parent directory's config diff --git a/integration-tests/tests/test_api/test_body/test_post_body_multipart.py b/integration-tests/tests/test_api/test_body/test_post_body_multipart.py index 54498ebc5..fbb87a1d0 100644 --- a/integration-tests/tests/test_api/test_body/test_post_body_multipart.py +++ b/integration-tests/tests/test_api/test_body/test_post_body_multipart.py @@ -1,3 +1,4 @@ +from datetime import UTC, datetime, timedelta from io import BytesIO from typing import Any @@ -5,49 +6,67 @@ from integration_tests.api.body import post_body_multipart from integration_tests.client import Client +from integration_tests.models import AnObject, PublicError from integration_tests.models.post_body_multipart_body import PostBodyMultipartBody from integration_tests.models.post_body_multipart_response_200 import PostBodyMultipartResponse200 -from integration_tests.types import File +from integration_tests.types import File, Response + +body = PostBodyMultipartBody( + a_string="a test string", + description="super descriptive thing", + files=[ + File( + payload=BytesIO(b"some file content"), + file_name="cool_stuff.txt", + mime_type="application/openapi-python-client", + ), + File( + payload=BytesIO(b"more file content"), + file_name=None, + mime_type=None, + ), + ], + times=[datetime.now(UTC) - timedelta(days=1), datetime.now(UTC)], + objects=[ + AnObject( + an_int=1, + a_float=2.3, + ), + AnObject( + an_int=4, + a_float=5.6, + ), + ], +) -def test(client: Client) -> None: - a_string = "a test string" - payload = b"some file content" - file_name = "cool_stuff.txt" - mime_type = "application/openapi-python-client" - description = "super descriptive thing" +def check_response(response: Response[PostBodyMultipartResponse200 | PublicError]) -> None: + content = response.parsed + if not isinstance(content, PostBodyMultipartResponse200): + raise AssertionError(f"Received status {response.status_code} from test server with payload: {content!r}") + + assert content.a_string == body.a_string + assert content.description == body.description + assert content.times == body.times + assert content.objects == body.objects + assert len(content.files) == len(body.files) + for i, file in enumerate(content.files): + body.files[i].payload.seek(0) + assert file.data == body.files[i].payload.read().decode() + assert file.name == body.files[i].file_name + assert file.content_type == body.files[i].mime_type + +def test(client: Client) -> None: response = post_body_multipart.sync_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) - content = response.parsed - if not isinstance(content, PostBodyMultipartResponse200): - raise AssertionError(f"Received status {response.status_code} from test server with payload: {content!r}") - - assert content.a_string == a_string - assert content.file_name == file_name - assert content.file_content_type == mime_type - assert content.file_data.encode() == payload - assert content.description == description + check_response(response) def test_custom_hooks() -> None: - a_string = "a test string" - payload = b"some file content" - file_name = "cool_stuff.txt" - mime_type = "application/openapi-python-client" - description = "super descriptive thing" - request_hook_called = False response_hook_called = False @@ -65,15 +84,7 @@ def log_response(*_: Any, **__: Any) -> None: post_body_multipart.sync_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) assert request_hook_called @@ -81,149 +92,51 @@ def log_response(*_: Any, **__: Any) -> None: def test_context_manager(client: Client) -> None: - a_string = "a test string" - payload = b"some file content" - file_name = "cool_stuff.txt" - mime_type = "application/openapi-python-client" - description = "super descriptive thing" - with client as client: post_body_multipart.sync_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) response = post_body_multipart.sync_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) with pytest.raises(RuntimeError): post_body_multipart.sync_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) - content = response.parsed - if not isinstance(content, PostBodyMultipartResponse200): - raise AssertionError(f"Received status {response.status_code} from test server with payload: {content!r}") - - assert content.a_string == a_string - assert content.file_name == file_name - assert content.file_content_type == mime_type - assert content.file_data.encode() == payload - assert content.description == description + check_response(response) @pytest.mark.asyncio async def test_async(client: Client) -> None: - a_string = "a test string" - payload = b"some file content" - file_name = "cool_stuff.txt" - mime_type = "application/openapi-python-client" - description = "super descriptive thing" - response = await post_body_multipart.asyncio_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) - content = response.parsed - if not isinstance(content, PostBodyMultipartResponse200): - raise AssertionError(f"Received status {response.status_code} from test server with payload: {content!r}") - - assert content.a_string == a_string - assert content.file_name == file_name - assert content.file_content_type == mime_type - assert content.file_data.encode() == payload - assert content.description == description + check_response(response) @pytest.mark.asyncio async def test_async_context_manager(client: Client) -> None: - a_string = "a test string" - payload = b"some file content" - file_name = "cool_stuff.txt" - mime_type = "application/openapi-python-client" - description = "super descriptive thing" - async with client as client: await post_body_multipart.asyncio_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) response = await post_body_multipart.asyncio_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) with pytest.raises(RuntimeError): await post_body_multipart.asyncio_detailed( client=client, - body=PostBodyMultipartBody( - a_string=a_string, - file=File( - payload=BytesIO(payload), - file_name=file_name, - mime_type=mime_type, - ), - description=description, - ), + body=body, ) - content = response.parsed - if not isinstance(content, PostBodyMultipartResponse200): - raise AssertionError(f"Received status {response.status_code} from test server with payload: {content!r}") - - assert content.a_string == a_string - assert content.file_name == file_name - assert content.file_content_type == mime_type - assert content.file_data.encode() == payload - assert content.description == description + check_response(response) diff --git a/knope.toml b/knope.toml index 5ea5b3ddb..7859e93b5 100644 --- a/knope.toml +++ b/knope.toml @@ -1,6 +1,9 @@ [package] versioned_files = ["pyproject.toml"] changelog = "CHANGELOG.md" +extra_changelog_sections = [ + { name = "Notes", types = ["note"] } +] [github] owner = "openapi-generators" diff --git a/openapi_python_client/__init__.py b/openapi_python_client/__init__.py index ba6380cd5..cb19d2947 100644 --- a/openapi_python_client/__init__.py +++ b/openapi_python_client/__init__.py @@ -8,7 +8,7 @@ from importlib.metadata import version from pathlib import Path from subprocess import CalledProcessError -from typing import Any, Optional, Union +from typing import Any import httpcore import httpx @@ -42,7 +42,7 @@ def __init__( *, openapi: GeneratorData, config: Config, - custom_template_path: Optional[Path] = None, + custom_template_path: Path | None = None, ) -> None: self.openapi: GeneratorData = openapi self.config = config @@ -126,7 +126,7 @@ def _run_post_hooks(self) -> None: self._run_command(command) def _run_command(self, cmd: str) -> None: - cmd_name = cmd.split(" ")[0] + cmd_name = cmd.split(" ", maxsplit=1)[0] command_exists = shutil.which(cmd_name) if not command_exists: self.errors.append( @@ -184,7 +184,7 @@ def _build_metadata(self) -> None: readme = self.project_dir / "README.md" readme_template = self.env.get_template("README.md.jinja") readme.write_text( - readme_template.render(poetry=self.config.meta_type == MetaType.POETRY), + readme_template.render(meta=self.config.meta_type), encoding=self.config.file_encoding, ) @@ -292,8 +292,8 @@ def _build_api(self) -> None: def _get_project_for_url_or_path( config: Config, - custom_template_path: Optional[Path] = None, -) -> Union[Project, GeneratorError]: + custom_template_path: Path | None = None, +) -> Project | GeneratorError: data_dict = _get_document(source=config.document_source, timeout=config.http_timeout) if isinstance(data_dict, GeneratorError): return data_dict @@ -310,7 +310,7 @@ def _get_project_for_url_or_path( def generate( *, config: Config, - custom_template_path: Optional[Path] = None, + custom_template_path: Path | None = None, ) -> Sequence[GeneratorError]: """ Generate the client library @@ -327,7 +327,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: str | None) -> dict[str, Any] | GeneratorError: if content_type == "application/json": try: return json.loads(data.decode()) @@ -341,9 +341,9 @@ 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: str | Path, timeout: int) -> dict[str, Any] | GeneratorError: yaml_bytes: bytes - content_type: Optional[str] + content_type: str | None if isinstance(source, str): try: response = httpx.get(source, timeout=timeout) diff --git a/openapi_python_client/cli.py b/openapi_python_client/cli.py index 92d91f943..3972703ae 100644 --- a/openapi_python_client/cli.py +++ b/openapi_python_client/cli.py @@ -2,11 +2,10 @@ from collections.abc import Sequence from pathlib import Path from pprint import pformat -from typing import Optional, Union import typer -from openapi_python_client import MetaType +from openapi_python_client import MetaType, __version__ from openapi_python_client.config import Config, ConfigFile from openapi_python_client.parser.errors import ErrorLevel, GeneratorError, ParseError @@ -14,8 +13,6 @@ def _version_callback(value: bool) -> None: - from openapi_python_client import __version__ - if value: typer.echo(f"openapi-python-client version: {__version__}") raise typer.Exit() @@ -23,15 +20,15 @@ def _version_callback(value: bool) -> None: def _process_config( *, - url: Optional[str], - path: Optional[Path], - config_path: Optional[Path], + url: str | None, + path: Path | None, + config_path: Path | None, meta_type: MetaType, file_encoding: str, overwrite: bool, - output_path: Optional[Path], + output_path: Path | None, ) -> Config: - source: Union[Path, str] + source: Path | str if url and not path: source = url elif path and not url: @@ -72,16 +69,16 @@ def cli( def _print_parser_error(err: GeneratorError, color: str) -> None: typer.secho(err.header, bold=True, fg=color, err=True) - typer.echo() + typer.echo(err=True) if err.detail: typer.secho(err.detail, fg=color, err=True) - typer.echo() + typer.echo(err=True) if isinstance(err, ParseError) and err.data is not None: formatted_data = pformat(err.data) typer.secho(formatted_data, fg=color, err=True) - typer.echo() + typer.echo(err=True) def handle_errors(errors: Sequence[GeneratorError], fail_on_warning: bool = False) -> None: @@ -127,9 +124,9 @@ def handle_errors(errors: Sequence[GeneratorError], fail_on_warning: bool = Fals @app.command() def generate( - url: Optional[str] = typer.Option(None, help="A URL to read the OpenAPI document from"), - path: Optional[Path] = typer.Option(None, help="A path to the OpenAPI document"), - custom_template_path: Optional[Path] = typer.Option( + url: str | None = typer.Option(None, help="A URL to read the OpenAPI document from"), + path: Path | None = typer.Option(None, help="A path to the OpenAPI document"), + custom_template_path: Path | None = typer.Option( None, help="A path to a directory containing custom template(s)", file_okay=False, @@ -142,10 +139,10 @@ def generate( help="The type of metadata you want to generate.", ), file_encoding: str = typer.Option("utf-8", help="Encoding used when writing generated"), - config_path: Optional[Path] = typer.Option(None, "--config", help="Path to the config file to use"), + config_path: Path | None = typer.Option(None, "--config", help="Path to the config file to use"), fail_on_warning: bool = False, overwrite: bool = typer.Option(False, help="Overwrite the existing client if it exists"), - output_path: Optional[Path] = typer.Option( + output_path: Path | None = typer.Option( None, help="Path to write the generated code to. " "Defaults to the OpenAPI document title converted to kebab or snake case (depending on meta type). " @@ -153,7 +150,7 @@ def generate( ), ) -> None: """Generate a new OpenAPI Client library""" - from . import generate + from . import generate # noqa: PLC0415 config = _process_config( url=url, diff --git a/openapi_python_client/config.py b/openapi_python_client/config.py index 1616ac785..a30d2ddbf 100644 --- a/openapi_python_client/config.py +++ b/openapi_python_client/config.py @@ -1,8 +1,7 @@ import json import mimetypes -from enum import Enum +from enum import StrEnum from pathlib import Path -from typing import Optional, Union from attr import define from pydantic import BaseModel @@ -15,17 +14,18 @@ class ClassOverride(BaseModel): See https://github.com/openapi-generators/openapi-python-client#class_overrides """ - class_name: Optional[str] = None - module_name: Optional[str] = None + class_name: str | None = None + module_name: str | None = None -class MetaType(str, Enum): +class MetaType(StrEnum): """The types of metadata supported for project generation.""" NONE = "none" POETRY = "poetry" SETUP = "setup" PDM = "pdm" + UV = "uv" class ConfigFile(BaseModel): @@ -34,13 +34,13 @@ 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 - project_name_override: Optional[str] = None - package_name_override: Optional[str] = None - package_version_override: Optional[str] = None + class_overrides: dict[str, ClassOverride] | None = None + content_type_overrides: dict[str, str] | None = None + project_name_override: str | None = None + package_name_override: str | None = None + package_version_override: str | None = None use_path_prefixes_for_title_model_names: bool = True - post_hooks: Optional[list[str]] = None + post_hooks: list[str] | None = None docstrings_on_attributes: bool = False field_prefix: str = "field_" generate_all_tags: bool = False @@ -66,9 +66,9 @@ class Config: meta_type: MetaType class_overrides: dict[str, ClassOverride] - project_name_override: Optional[str] - package_name_override: Optional[str] - package_version_override: Optional[str] + project_name_override: str | None + package_name_override: str | None + package_version_override: str | None use_path_prefixes_for_title_model_names: bool post_hooks: list[str] docstrings_on_attributes: bool @@ -76,31 +76,31 @@ class Config: generate_all_tags: bool http_timeout: int literal_enums: bool - document_source: Union[Path, str] + document_source: Path | str file_encoding: str content_type_overrides: dict[str, str] overwrite: bool - output_path: Optional[Path] + output_path: Path | None @staticmethod def from_sources( config_file: ConfigFile, meta_type: MetaType, - document_source: Union[Path, str], + document_source: Path | str, file_encoding: str, overwrite: bool, - output_path: Optional[Path], + output_path: Path | None, ) -> "Config": if config_file.post_hooks is not None: post_hooks = config_file.post_hooks elif meta_type == MetaType.NONE: post_hooks = [ - "ruff check . --fix --extend-select=I", + "ruff check . --fix-only --extend-select=I", "ruff format .", ] else: post_hooks = [ - "ruff check --fix .", + "ruff check --fix-only .", "ruff format .", ] diff --git a/openapi_python_client/parser/bodies.py b/openapi_python_client/parser/bodies.py index 7d0b12954..41ad8a968 100644 --- a/openapi_python_client/parser/bodies.py +++ b/openapi_python_client/parser/bodies.py @@ -1,5 +1,4 @@ -import sys -from typing import Union +from enum import StrEnum import attr @@ -16,22 +15,12 @@ from ..utils import get_content_type from .errors import ErrorLevel, ParseError -if sys.version_info >= (3, 11): - from enum import StrEnum - class BodyType(StrEnum): - JSON = "json" - DATA = "data" - FILES = "files" - CONTENT = "content" -else: - from enum import Enum - - class BodyType(str, Enum): - JSON = "json" - DATA = "data" - FILES = "files" - CONTENT = "content" +class BodyType(StrEnum): + JSON = "json" + DATA = "data" + FILES = "files" + CONTENT = "content" @attr.define @@ -45,10 +34,10 @@ def body_from_data( *, data: oai.Operation, schemas: Schemas, - request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]], + request_bodies: dict[str, oai.RequestBody | oai.Reference], config: Config, endpoint_name: str, -) -> tuple[list[Union[Body, ParseError]], Schemas]: +) -> tuple[list[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): @@ -56,7 +45,7 @@ def body_from_data( if body is None: return [], schemas - bodies: list[Union[Body, ParseError]] = [] + bodies: list[Body | ParseError] = [] body_content = body.content prefix_type_names = len(body_content) > 1 @@ -100,7 +89,7 @@ def body_from_data( continue prop, schemas = property_from_data( name="body", - required=True, + required=body.required, data=media_type_schema, schemas=schemas, parent_name=f"{endpoint_name}_{body_type}" if prefix_type_names else endpoint_name, @@ -132,8 +121,8 @@ def body_from_data( def _resolve_reference( - body: Union[oai.RequestBody, oai.Reference, None], request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]] -) -> Union[oai.RequestBody, ParseError, None]: + body: oai.RequestBody | oai.Reference | None, request_bodies: dict[str, oai.RequestBody | oai.Reference] +) -> oai.RequestBody | ParseError | None: if body is None: return None references_seen = [] diff --git a/openapi_python_client/parser/errors.py b/openapi_python_client/parser/errors.py index 36322f0cf..00f522b9e 100644 --- a/openapi_python_client/parser/errors.py +++ b/openapi_python_client/parser/errors.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from enum import Enum -from typing import Optional __all__ = ["ErrorLevel", "GeneratorError", "ParameterError", "ParseError", "PropertyError"] @@ -18,7 +17,7 @@ class ErrorLevel(Enum): class GeneratorError: """Base data struct containing info on an error that occurred""" - detail: Optional[str] = None + detail: str | None = None level: ErrorLevel = ErrorLevel.ERROR header: str = "Unable to generate the client" @@ -28,7 +27,7 @@ class ParseError(GeneratorError): """An error raised when there's a problem parsing an OpenAPI document""" level: ErrorLevel = ErrorLevel.WARNING - data: Optional[BaseModel] = None + data: BaseModel | None = None header: str = "Unable to parse this part of your OpenAPI document: " diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index e427cc18d..4f83ae93e 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -2,8 +2,7 @@ from collections.abc import Iterator from copy import deepcopy from dataclasses import dataclass, field -from http import HTTPStatus -from typing import Any, Optional, Protocol, Union +from typing import Any, Protocol from pydantic import ValidationError @@ -26,7 +25,7 @@ property_from_data, ) from .properties.schemas import parameter_from_reference -from .responses import Response, response_from_data +from .responses import HTTPStatusPattern, Responses, response_from_data _PATH_PARAM_REGEX = re.compile("{([a-zA-Z_-][a-zA-Z0-9_-]*)}") @@ -50,8 +49,8 @@ def from_data( data: dict[str, oai.PathItem], schemas: Schemas, parameters: Parameters, - request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]], - responses: dict[str, Union[oai.Response, oai.Reference]], + request_bodies: dict[str, oai.RequestBody | oai.Reference], + responses: dict[str, oai.Response | oai.Reference], config: Config, ) -> tuple[dict[utils.PythonIdentifier, "EndpointCollection"], Schemas, Parameters]: """Parse the openapi paths data to get EndpointCollections by tag""" @@ -61,7 +60,7 @@ def from_data( for path, path_data in data.items(): for method in methods: - operation: Optional[oai.Operation] = getattr(path_data, method) + operation: oai.Operation | None = getattr(path_data, method) if operation is None: continue @@ -126,7 +125,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[Property | PropertyError | None, Schemas]: ... # pragma: no cover @dataclass @@ -137,17 +136,17 @@ class Endpoint: path: str method: str - description: Optional[str] + description: str | None name: str requires_security: bool tags: list[PythonIdentifier] - summary: Optional[str] = "" + summary: str | None = "" 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) + responses: Responses = field(default_factory=lambda: Responses(patterns=[], default=None)) bodies: list[Body] = field(default_factory=list) errors: list[ParseError] = field(default_factory=list) @@ -157,24 +156,14 @@ def _add_responses( endpoint: "Endpoint", data: oai.Responses, schemas: Schemas, - responses: dict[str, Union[oai.Response, oai.Reference]], + responses: dict[str, oai.Response | oai.Reference], config: Config, ) -> tuple["Endpoint", Schemas]: endpoint = deepcopy(endpoint) for code, response_data in data.items(): - status_code: HTTPStatus - try: - status_code = HTTPStatus(int(code)) - except ValueError: - endpoint.errors.append( - ParseError( - detail=( - f"Invalid response status code {code} (not a valid HTTP " - f"status code), response will be omitted from generated " - f"client" - ) - ) - ) + status_code = HTTPStatusPattern.parse(code) + if isinstance(status_code, ParseError): + endpoint.errors.append(status_code) continue response, schemas = response_from_data( @@ -190,7 +179,7 @@ def _add_responses( endpoint.errors.append( ParseError( detail=( - f"Cannot parse response for status code {status_code}{detail_suffix}, " + f"Cannot parse response for status code {code}{detail_suffix}, " f"response will be omitted from generated client" ), data=response.data, @@ -201,18 +190,22 @@ def _add_responses( # No reasons to use lazy imports in endpoints, so add lazy imports to relative here. endpoint.relative_imports |= response.prop.get_lazy_imports(prefix=models_relative_prefix) endpoint.relative_imports |= response.prop.get_imports(prefix=models_relative_prefix) - endpoint.responses.append(response) + if response.is_default(): + endpoint.responses.default = response + else: + endpoint.responses.patterns.append(response) + endpoint.responses.patterns.sort() return endpoint, schemas @staticmethod def add_parameters( *, endpoint: "Endpoint", - data: Union[oai.Operation, oai.PathItem], + data: oai.Operation | oai.PathItem, schemas: Schemas, parameters: Parameters, config: Config, - ) -> tuple[Union["Endpoint", ParseError], Schemas, Parameters]: + ) -> tuple["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 @@ -319,8 +312,8 @@ def _check_parameters_for_conflicts( self, *, config: Config, - previously_modified_params: Optional[set[tuple[oai.ParameterLocation, str]]] = None, - ) -> Union["Endpoint", ParseError]: + previously_modified_params: set[tuple[oai.ParameterLocation, str]] | None = None, + ) -> "Endpoint | ParseError": """Check for conflicting parameters For parameters that have the same python_name but are in different locations, append the location to the @@ -373,7 +366,7 @@ def _check_parameters_for_conflicts( return self @staticmethod - def sort_parameters(*, endpoint: "Endpoint") -> Union["Endpoint", ParseError]: + def sort_parameters(*, endpoint: "Endpoint") -> "Endpoint | ParseError": """ Sorts the path parameters of an `endpoint` so that they match the order declared in `endpoint.path`. @@ -410,10 +403,10 @@ def from_data( tags: list[PythonIdentifier], schemas: Schemas, parameters: Parameters, - request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]], - responses: dict[str, Union[oai.Response, oai.Reference]], + request_bodies: dict[str, oai.RequestBody | oai.Reference], + responses: dict[str, oai.Response | oai.Reference], config: Config, - ) -> tuple[Union["Endpoint", ParseError], Schemas, Parameters]: + ) -> tuple["Endpoint | ParseError", Schemas, Parameters]: """Construct an endpoint from the OpenAPI data""" if data.operationId is None: @@ -476,12 +469,12 @@ def from_data( def response_type(self) -> str: """Get the Python type of any response from this endpoint""" - types = sorted({response.prop.get_type_string(quoted=False) for response in self.responses}) + types = sorted({response.prop.get_type_string() for response in self.responses}) if len(types) == 0: return "Any" if len(types) == 1: - return self.responses[0].prop.get_type_string(quoted=False) - return f"Union[{', '.join(types)}]" + return types[0] + return " | ".join(types) def iter_all_parameters(self) -> Iterator[tuple[oai.ParameterLocation, Property]]: """Iterate through all the parameters of this endpoint""" @@ -506,15 +499,15 @@ class GeneratorData: """All the data needed to generate a client""" title: str - description: Optional[str] + description: str | None version: str - models: Iterator[ModelProperty] + models: list[ModelProperty] errors: list[ParseError] endpoint_collections_by_tag: dict[utils.PythonIdentifier, EndpointCollection] - enums: Iterator[Union[EnumProperty, LiteralEnumProperty]] + enums: list[EnumProperty | LiteralEnumProperty] @staticmethod - def from_dict(data: dict[str, Any], *, config: Config) -> Union["GeneratorData", GeneratorError]: + def from_dict(data: dict[str, Any], *, config: Config) -> "GeneratorData | GeneratorError": """Create an OpenAPI from dict""" try: openapi = oai.OpenAPI.model_validate(data) @@ -546,10 +539,10 @@ def from_dict(data: dict[str, Any], *, config: Config) -> Union["GeneratorData", config=config, ) - enums = ( - prop for prop in schemas.classes_by_name.values() if isinstance(prop, (EnumProperty, LiteralEnumProperty)) - ) - models = (prop for prop in schemas.classes_by_name.values() if isinstance(prop, ModelProperty)) + enums = [ + prop for prop in schemas.classes_by_name.values() if isinstance(prop, EnumProperty | LiteralEnumProperty) + ] + models = [prop for prop in schemas.classes_by_name.values() if isinstance(prop, ModelProperty)] return GeneratorData( title=openapi.info.title, diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index ba667347b..ba81d27c6 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -40,6 +40,7 @@ Parameters, ReferencePath, Schemas, + get_reference_simple_name, parse_reference_path, update_parameters_with_data, update_schemas_with_data, @@ -324,17 +325,30 @@ def _create_schemas( while still_making_progress: still_making_progress = False errors = [] - next_round = [] + next_round: list[tuple[str, oai.Reference | oai.Schema]] = [] # Only accumulate errors from the last round, since we might fix some along the way for name, data in to_process: - if isinstance(data, oai.Reference): - schemas.errors.append(PropertyError(data=data, detail="Reference schemas are not supported.")) - continue + schema_data: oai.Reference | oai.Schema | None = data ref_path = parse_reference_path(f"#/components/schemas/{name}") if isinstance(ref_path, ParseError): schemas.errors.append(PropertyError(detail=ref_path.detail, data=data)) continue - schemas_or_err = update_schemas_with_data(ref_path=ref_path, data=data, schemas=schemas, config=config) + if isinstance(data, oai.Reference): + # Fully dereference reference schemas + seen = [name] + while isinstance(schema_data, oai.Reference): + data_ref_schema = get_reference_simple_name(schema_data.ref) + if data_ref_schema in seen: + schemas.errors.append(PropertyError(detail="Circular schema references found", data=data)) + break + # use derefenced schema definition for this schema + schema_data = components.get(data_ref_schema) + if isinstance(schema_data, oai.Schema): + schemas_or_err = update_schemas_with_data( + ref_path=ref_path, data=schema_data, schemas=schemas, config=config + ) + else: + schemas.errors.append(PropertyError(detail="Referent schema not found", data=data)) if isinstance(schemas_or_err, PropertyError): next_round.append((name, data)) errors.append(schemas_or_err) diff --git a/openapi_python_client/parser/properties/any.py b/openapi_python_client/parser/properties/any.py index b760a1568..65fcf40c4 100644 --- a/openapi_python_client/parser/properties/any.py +++ b/openapi_python_client/parser/properties/any.py @@ -33,7 +33,7 @@ def build( @classmethod def convert_value(cls, value: Any) -> Value | None: - from .string import StringProperty + from .string import StringProperty # noqa: PLC0415 if value is None: return value diff --git a/openapi_python_client/parser/properties/const.py b/openapi_python_client/parser/properties/const.py index 8b9967f19..216941eb8 100644 --- a/openapi_python_client/parser/properties/const.py +++ b/openapi_python_client/parser/properties/const.py @@ -93,13 +93,10 @@ def get_type_string( self, no_optional: bool = False, json: bool = False, - *, - multipart: bool = False, - quoted: bool = False, ) -> str: lit = f"Literal[{self.value.python_code}]" if not no_optional and not self.required: - return f"Union[{lit}, Unset]" + return f"{lit} | Unset" return lit def get_imports(self, *, prefix: str) -> set[str]: @@ -113,6 +110,6 @@ def get_imports(self, *, prefix: str) -> set[str]: if self.required: return {"from typing import Literal, cast"} return { - "from typing import Literal, Union, cast", + "from typing import Literal, cast", f"from {prefix}types import UNSET, Unset", } diff --git a/openapi_python_client/parser/properties/date.py b/openapi_python_client/parser/properties/date.py index 7261698ea..7bdae3d85 100644 --- a/openapi_python_client/parser/properties/date.py +++ b/openapi_python_client/parser/properties/date.py @@ -1,9 +1,9 @@ from __future__ import annotations +import datetime from typing import Any, ClassVar from attr import define -from dateutil.parser import isoparse from ...utils import PythonIdentifier from ..errors import PropertyError @@ -54,10 +54,10 @@ def convert_value(cls, value: Any) -> Value | None | PropertyError: return value if isinstance(value, str): try: - isoparse(value).date() # make sure it's a valid value + datetime.date.fromisoformat(value) # make sure it's a valid value except ValueError as e: return PropertyError(f"Invalid date: {e}") - return Value(python_code=f"isoparse({value!r}).date()", raw_value=value) + return Value(python_code=f"datetime.date.fromisoformat({value!r})", raw_value=value) return PropertyError(f"Cannot convert {value} to a date") def get_imports(self, *, prefix: str) -> set[str]: @@ -69,5 +69,5 @@ def get_imports(self, *, prefix: str) -> set[str]: back to the root of the generated client. """ imports = super().get_imports(prefix=prefix) - imports.update({"import datetime", "from typing import cast", "from dateutil.parser import isoparse"}) + imports.update({"import datetime", "from typing import cast"}) return imports diff --git a/openapi_python_client/parser/properties/datetime.py b/openapi_python_client/parser/properties/datetime.py index 5924d173c..e6839dd86 100644 --- a/openapi_python_client/parser/properties/datetime.py +++ b/openapi_python_client/parser/properties/datetime.py @@ -1,9 +1,9 @@ from __future__ import annotations +import datetime from typing import Any, ClassVar from attr import define -from dateutil.parser import isoparse from ...utils import PythonIdentifier from ..errors import PropertyError @@ -56,10 +56,10 @@ def convert_value(cls, value: Any) -> Value | None | PropertyError: return value if isinstance(value, str): try: - isoparse(value) # make sure it's a valid value + datetime.datetime.fromisoformat(value) # make sure it's a valid value except ValueError as e: return PropertyError(f"Invalid datetime: {e}") - return Value(python_code=f"isoparse({value!r})", raw_value=value) + return Value(python_code=f"datetime.datetime.fromisoformat({value!r})", raw_value=value) return PropertyError(f"Cannot convert {value} to a datetime") def get_imports(self, *, prefix: str) -> set[str]: @@ -71,5 +71,5 @@ def get_imports(self, *, prefix: str) -> set[str]: back to the root of the generated client. """ imports = super().get_imports(prefix=prefix) - imports.update({"import datetime", "from typing import cast", "from dateutil.parser import isoparse"}) + imports.update({"import datetime", "from typing import cast"}) return imports diff --git a/openapi_python_client/parser/properties/enum_property.py b/openapi_python_client/parser/properties/enum_property.py index fc7f20bd9..9845da7f4 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, Union, cast +from typing import Any, ClassVar, cast from attr import evolve from attrs import define @@ -16,7 +16,7 @@ from .schemas import Class, Schemas from .union import UnionProperty -ValueType = Union[str, int] +ValueType = str | int @define @@ -98,9 +98,7 @@ def build( # noqa: PLR0911 value_type = next(iter(value_types)) 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 - ) # We checked this with all the value_types stuff + value_list = cast(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 data.oneOf = [ @@ -121,7 +119,8 @@ def build( # noqa: PLR0911 if parent_name: class_name = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_name)}" class_info = Class.from_string(string=class_name, config=config) - values = EnumProperty.values_from_list(value_list, class_info) + var_names = data.model_extra.get("x-enum-varnames", []) if data.model_extra else [] + values = EnumProperty.values_from_list(value_list, class_info, var_names) if class_info.name in schemas.classes_by_name: existing = schemas.classes_by_name[class_info.name] @@ -164,10 +163,10 @@ def convert_value(self, value: Any) -> Value | PropertyError | None: return PropertyError(detail=f"Value {value} is not valid for enum {self.name}") return PropertyError(detail=f"Cannot convert {value} to enum {self.name} of type {self.value_type}") - def get_base_type_string(self, *, quoted: bool = False) -> str: + def get_base_type_string(self) -> str: return self.class_info.name - def get_base_json_type_string(self, *, quoted: bool = False) -> str: + def get_base_json_type_string(self) -> str: return self.value_type.__name__ def get_imports(self, *, prefix: str) -> set[str]: @@ -183,19 +182,28 @@ def get_imports(self, *, prefix: str) -> set[str]: return imports @staticmethod - def values_from_list(values: list[str] | list[int], class_info: Class) -> dict[str, ValueType]: + def values_from_list( + values: list[str] | list[int], class_info: Class, var_names: list[str] + ) -> dict[str, ValueType]: """Convert a list of values into dict of {name: value}, where value can sometimes be None""" output: dict[str, ValueType] = {} + use_var_names = len(var_names) == len(values) for i, value in enumerate(values): - value = cast(Union[str, int], value) + value = cast(str | int, value) if isinstance(value, int): - if value < 0: + if use_var_names: + key = var_names[i] + sanitized_key = utils.snake_case(key).upper() + output[sanitized_key] = value + elif value < 0: output[f"VALUE_NEGATIVE_{-value}"] = value else: output[f"VALUE_{value}"] = value continue - if value and value[0].isalpha(): + if use_var_names: + key = var_names[i] + elif value and value[0].isalpha(): key = value.upper() else: key = f"VALUE_{i}" diff --git a/openapi_python_client/parser/properties/file.py b/openapi_python_client/parser/properties/file.py index 505876b63..90bbf6aec 100644 --- a/openapi_python_client/parser/properties/file.py +++ b/openapi_python_client/parser/properties/file.py @@ -22,7 +22,7 @@ class FileProperty(PropertyProtocol): _type_string: ClassVar[str] = "File" # Return type of File.to_tuple() - _json_type_string: ClassVar[str] = "FileJsonType" + _json_type_string: ClassVar[str] = "FileTypes" template: ClassVar[str] = "file_property.py.jinja" @classmethod @@ -63,5 +63,5 @@ def get_imports(self, *, prefix: str) -> set[str]: back to the root of the generated client. """ imports = super().get_imports(prefix=prefix) - imports.update({f"from {prefix}types import File, FileJsonType", "from io import BytesIO"}) + imports.update({f"from {prefix}types import File, FileTypes", "from io import BytesIO"}) return imports diff --git a/openapi_python_client/parser/properties/list_property.py b/openapi_python_client/parser/properties/list_property.py index bf0756fe3..26ab0fdab 100644 --- a/openapi_python_client/parser/properties/list_property.py +++ b/openapi_python_client/parser/properties/list_property.py @@ -56,7 +56,7 @@ def build( `(result, schemas)` where `schemas` is an updated version of the input named the same including any inner classes that were defined and `result` is either the `ListProperty` or a `PropertyError`. """ - from . import property_from_data + from . import property_from_data # noqa: PLC0415 if data.items is None and not data.prefixItems: return ( @@ -105,11 +105,11 @@ def build( 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)}]" + def get_base_type_string(self) -> str: + return f"list[{self.inner_property.get_type_string()}]" - 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)}]" + def get_base_json_type_string(self) -> str: + return f"list[{self.inner_property.get_type_string(json=True)}]" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" @@ -137,9 +137,6 @@ def get_type_string( self, no_optional: bool = False, json: bool = False, - *, - multipart: bool = False, - quoted: bool = False, ) -> str: """ Get a string representation of type that should be used when declaring this property @@ -150,11 +147,9 @@ def get_type_string( """ if json: type_string = self.get_base_json_type_string() - elif multipart: - type_string = "tuple[None, bytes, str]" else: type_string = self.get_base_type_string() if no_optional or self.required: return type_string - return f"Union[Unset, {type_string}]" + return f"{type_string} | Unset" diff --git a/openapi_python_client/parser/properties/literal_enum_property.py b/openapi_python_client/parser/properties/literal_enum_property.py index 669b62f58..f0e0306a2 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, Union, cast +from typing import Any, ClassVar, cast from attr import evolve from attrs import define @@ -16,7 +16,7 @@ from .schemas import Class, Schemas from .union import UnionProperty -ValueType = Union[str, int] +ValueType = str | int @define @@ -97,9 +97,7 @@ def build( # noqa: PLR0911 value_type = next(iter(value_types)) 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 - ) # We checked this with all the value_types stuff + value_list = cast(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 data.oneOf = [ @@ -162,10 +160,10 @@ def convert_value(self, value: Any) -> Value | PropertyError | None: return PropertyError(detail=f"Value {value} is not valid for enum {self.name}") return PropertyError(detail=f"Cannot convert {value} to enum {self.name} of type {self.value_type}") - def get_base_type_string(self, *, quoted: bool = False) -> str: + def get_base_type_string(self) -> str: return self.class_info.name - def get_base_json_type_string(self, *, quoted: bool = False) -> str: + def get_base_json_type_string(self) -> str: return self.value_type.__name__ def get_instance_type_string(self) -> str: diff --git a/openapi_python_client/parser/properties/merge_properties.py b/openapi_python_client/parser/properties/merge_properties.py index db6424a7c..57b4bccbe 100644 --- a/openapi_python_client/parser/properties/merge_properties.py +++ b/openapi_python_client/parser/properties/merge_properties.py @@ -105,9 +105,9 @@ def _merge_string_with_format(prop1: Property, prop2: Property) -> Property | No def _merge_numeric(prop1: Property, prop2: Property) -> IntProperty | None | PropertyError: """Merge IntProperty with FloatProperty""" - if isinstance(prop1, IntProperty) and isinstance(prop2, (IntProperty, FloatProperty)): + if isinstance(prop1, IntProperty) and isinstance(prop2, IntProperty | FloatProperty): return _merge_common_attributes(prop1, prop2) - elif isinstance(prop2, IntProperty) and isinstance(prop1, (IntProperty, FloatProperty)): + elif isinstance(prop2, IntProperty) and isinstance(prop1, IntProperty | FloatProperty): # Use the IntProperty as a base since it's more restrictive, but keep the correct override order return _merge_common_attributes(prop2, prop1, prop2) else: diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 762624501..636b71a34 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -140,8 +140,8 @@ def self_import(self) -> str: """Constructs a self import statement from this ModelProperty's attributes""" return f"models.{self.class_info.module_name} import {self.class_info.name}" - def get_base_type_string(self, *, quoted: bool = False) -> str: - return f'"{self.class_info.name}"' if quoted else self.class_info.name + def get_base_type_string(self) -> str: + return self.class_info.name def get_imports(self, *, prefix: str) -> set[str]: """ @@ -188,9 +188,6 @@ def get_type_string( self, no_optional: bool = False, json: bool = False, - *, - multipart: bool = False, - quoted: bool = False, ) -> str: """ Get a string representation of type that should be used when declaring this property @@ -201,18 +198,12 @@ def get_type_string( """ if json: type_string = self.get_base_json_type_string() - elif multipart: - type_string = "tuple[None, bytes, str]" else: type_string = self.get_base_type_string() - if quoted: - if type_string == self.class_info.name: - type_string = f"'{type_string}'" - if no_optional or self.required: return type_string - return f"Union[Unset, {type_string}]" + return f"{type_string} | Unset" from .property import Property # noqa: E402 @@ -245,8 +236,8 @@ def _process_properties( # noqa: PLR0912, PLR0911 config: Config, roots: set[ReferencePath | utils.ClassName], ) -> _PropertyData | PropertyError: - from . import property_from_data - from .merge_properties import merge_properties + from . import property_from_data # noqa: PLC0415 + from .merge_properties import merge_properties # noqa: PLC0415 properties: dict[str, Property] = {} relative_imports: set[str] = set() @@ -301,6 +292,11 @@ def _add_if_no_conflict(new_prop: Property) -> PropertyError | None: unprocessed_props.extend(sub_prop.properties.items() if sub_prop.properties else []) required_set.update(sub_prop.required or []) + # Update properties that are marked as required in the schema + for prop_name in required_set: + if prop_name in properties and not properties[prop_name].required: + properties[prop_name] = evolve(properties[prop_name], required=True) + for key, value in unprocessed_props: prop_required = key in required_set prop_or_error: Property | (PropertyError | None) @@ -356,7 +352,7 @@ def _get_additional_properties( config: Config, roots: set[ReferencePath | utils.ClassName], ) -> tuple[Property | None | PropertyError, Schemas]: - from . import property_from_data + from . import property_from_data # noqa: PLC0415 if schema_additional is None: return ANY_ADDITIONAL_PROPERTY, schemas diff --git a/openapi_python_client/parser/properties/property.py b/openapi_python_client/parser/properties/property.py index 6e73a01ae..5927c6e91 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -1,8 +1,7 @@ __all__ = ["Property"] -from typing import Union -from typing_extensions import TypeAlias +from typing import TypeAlias from .any import AnyProperty from .boolean import BooleanProperty @@ -21,21 +20,21 @@ from .union import UnionProperty from .uuid import UuidProperty -Property: TypeAlias = Union[ - AnyProperty, - BooleanProperty, - ConstProperty, - DateProperty, - DateTimeProperty, - EnumProperty, - LiteralEnumProperty, - FileProperty, - FloatProperty, - IntProperty, - ListProperty, - ModelProperty, - NoneProperty, - StringProperty, - UnionProperty, - UuidProperty, -] +Property: TypeAlias = ( + AnyProperty + | BooleanProperty + | ConstProperty + | DateProperty + | DateTimeProperty + | EnumProperty + | LiteralEnumProperty + | FileProperty + | FloatProperty + | IntProperty + | ListProperty + | ModelProperty + | NoneProperty + | StringProperty + | UnionProperty + | UuidProperty +) diff --git a/openapi_python_client/parser/properties/protocol.py b/openapi_python_client/parser/properties/protocol.py index 9a5b51828..138fa7002 100644 --- a/openapi_python_client/parser/properties/protocol.py +++ b/openapi_python_client/parser/properties/protocol.py @@ -88,21 +88,18 @@ def set_python_name(self, new_name: str, config: Config, skip_snake_case: bool = PythonIdentifier(value=new_name, prefix=config.field_prefix, skip_snake_case=skip_snake_case), ) - def get_base_type_string(self, *, quoted: bool = False) -> str: + def get_base_type_string(self) -> str: """Get the string describing the Python type of this property. Base types no require quoting.""" - return f'"{self._type_string}"' if not self.is_base_type and quoted else self._type_string + return self._type_string - def get_base_json_type_string(self, *, quoted: bool = False) -> str: + def get_base_json_type_string(self) -> str: """Get the string describing the JSON type of this property. Base types no require quoting.""" - return f'"{self._json_type_string}"' if not self.is_base_type and quoted else self._json_type_string + return self._json_type_string def get_type_string( self, no_optional: bool = False, json: bool = False, - *, - multipart: bool = False, - quoted: bool = False, ) -> str: """ Get a string representation of type that should be used when declaring this property @@ -110,23 +107,19 @@ def get_type_string( Args: no_optional: Do not include Optional or Unset even if the value is optional (needed for isinstance checks) json: True if the type refers to the property after JSON serialization - multipart: True if the type should be used in a multipart request - quoted: True if the type should be wrapped in quotes (if not a base type) """ if json: - type_string = self.get_base_json_type_string(quoted=quoted) - elif multipart: - type_string = "tuple[None, bytes, str]" + type_string = self.get_base_json_type_string() else: - type_string = self.get_base_type_string(quoted=quoted) + type_string = self.get_base_type_string() if no_optional or self.required: return type_string - return f"Union[Unset, {type_string}]" + return f"{type_string} | Unset" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" - return self.get_type_string(no_optional=True, quoted=False) + return self.get_type_string(no_optional=True) # noinspection PyUnusedLocal def get_imports(self, *, prefix: str) -> set[str]: @@ -139,7 +132,6 @@ def get_imports(self, *, prefix: str) -> set[str]: """ imports = set() if not self.required: - imports.add("from typing import Union") imports.add(f"from {prefix}types import UNSET, Unset") return imports @@ -163,8 +155,8 @@ def to_string(self) -> str: default = None if default is not None: - return f"{self.python_name}: {self.get_type_string(quoted=True)} = {default}" - return f"{self.python_name}: {self.get_type_string(quoted=True)}" + return f"{self.python_name}: {self.get_type_string()} = {default}" + return f"{self.python_name}: {self.get_type_string()}" def to_docstring(self) -> str: """Returns property docstring""" @@ -174,14 +166,3 @@ def to_docstring(self) -> str: if self.example: doc += f" Example: {self.example}." return doc - - @property - def is_base_type(self) -> bool: - """Base types, represented by any other of `Property` than `ModelProperty` should not be quoted.""" - from . import ListProperty, ModelProperty, UnionProperty - - return self.__class__.__name__ not in { - ModelProperty.__name__, - ListProperty.__name__, - UnionProperty.__name__, - } diff --git a/openapi_python_client/parser/properties/schemas.py b/openapi_python_client/parser/properties/schemas.py index 40dbd7374..dfa7b0f20 100644 --- a/openapi_python_client/parser/properties/schemas.py +++ b/openapi_python_client/parser/properties/schemas.py @@ -10,7 +10,7 @@ "update_schemas_with_data", ] -from typing import TYPE_CHECKING, NewType, Union, cast +from typing import TYPE_CHECKING, NewType, cast from urllib.parse import urlparse from attrs import define, evolve, field @@ -32,7 +32,7 @@ ReferencePath = NewType("ReferencePath", str) -def parse_reference_path(ref_path_raw: str) -> Union[ReferencePath, ParseError]: +def parse_reference_path(ref_path_raw: str) -> ReferencePath | ParseError: """ Takes a raw string provided in a `$ref` and turns it into a validated `_ReferencePath` or a `ParseError` if validation fails. @@ -50,7 +50,7 @@ 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("/")[-1] + return ref_path.rsplit("/", maxsplit=1)[-1] @define @@ -84,12 +84,12 @@ 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) + dependencies: dict[ReferencePath, set[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[ReferencePath | ClassName]) -> None: """Record new dependencies on the given ReferencePath Args: @@ -102,7 +102,7 @@ def add_dependencies(self, ref_path: ReferencePath, roots: set[Union[ReferencePa def update_schemas_with_data( *, ref_path: ReferencePath, data: oai.Schema, schemas: Schemas, config: Config -) -> Union[Schemas, PropertyError]: +) -> Schemas | PropertyError: """ Update a `Schemas` using some new reference. @@ -118,9 +118,9 @@ def update_schemas_with_data( See Also: - https://swagger.io/docs/specification/using-ref/ """ - from . import property_from_data + from . import property_from_data # noqa: PLC0415 - prop: Union[PropertyError, Property] + prop: PropertyError | Property prop, schemas = property_from_data( data=data, name=ref_path, @@ -158,10 +158,10 @@ class Parameters: def parameter_from_data( *, name: str, - data: Union[oai.Reference, oai.Parameter], + data: oai.Reference | oai.Parameter, parameters: Parameters, config: Config, -) -> tuple[Union[Parameter, ParameterError], Parameters]: +) -> tuple[Parameter | ParameterError, Parameters]: """Generates parameters from an OpenAPI Parameter spec.""" if isinstance(data, oai.Reference): @@ -186,7 +186,7 @@ def parameter_from_data( def update_parameters_with_data( *, ref_path: ReferencePath, data: oai.Parameter, parameters: Parameters, config: Config -) -> Union[Parameters, ParameterError]: +) -> Parameters | ParameterError: """ Update a `Parameters` using some new reference. @@ -219,9 +219,9 @@ def update_parameters_with_data( def parameter_from_reference( *, - param: Union[oai.Reference, Parameter], + param: oai.Reference | Parameter, parameters: Parameters, -) -> Union[Parameter, ParameterError]: +) -> Parameter | ParameterError: """ Returns a Parameter from a Reference or the Parameter itself if one was provided. diff --git a/openapi_python_client/parser/properties/union.py b/openapi_python_client/parser/properties/union.py index 8b7b02a48..3091c793b 100644 --- a/openapi_python_client/parser/properties/union.py +++ b/openapi_python_client/parser/properties/union.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections.abc import Iterator from itertools import chain from typing import Any, ClassVar, cast @@ -28,7 +29,14 @@ class UnionProperty(PropertyProtocol): @classmethod def build( - cls, *, data: oai.Schema, name: str, required: bool, schemas: Schemas, parent_name: str, config: Config + cls, + *, + data: oai.Schema, + name: str, + required: bool, + schemas: Schemas, + parent_name: str, + config: Config, ) -> tuple[UnionProperty | PropertyError, Schemas]: """ Create a `UnionProperty` the right way. @@ -45,7 +53,7 @@ def build( `(result, schemas)` where `schemas` is the updated version of the input `schemas` and `result` is the constructed `UnionProperty` or a `PropertyError` describing what went wrong. """ - from . import property_from_data + from . import property_from_data # noqa: PLC0415 sub_properties: list[PropertyProtocol] = [] @@ -55,8 +63,19 @@ def build( type_list_data.append(data.model_copy(update={"type": _type, "default": None})) for i, sub_prop_data in enumerate(chain(data.anyOf, data.oneOf, type_list_data)): + # If a schema has a unique title property, we can use that to carry forward a descriptive name instead of "type_0" + subscript: str + if ( + isinstance(sub_prop_data, oai.Schema) + and sub_prop_data.title is not None + and sub_prop_data.title != data.title + ): + subscript = sub_prop_data.title + else: + subscript = f"type_{i}" + sub_prop, schemas = property_from_data( - name=f"{name}_type_{i}", + name=f"{name}_{subscript}", required=True, data=sub_prop_data, schemas=schemas, @@ -64,25 +83,32 @@ def build( config=config, ) if isinstance(sub_prop, PropertyError): - return PropertyError(detail=f"Invalid property in union {name}", data=sub_prop_data), schemas + return ( + PropertyError(detail=f"Invalid property in union {name}", data=sub_prop_data), + schemas, + ) sub_properties.append(sub_prop) - 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)) + def flatten_union_properties(possibly_nested: list[PropertyProtocol]) -> Iterator[PropertyProtocol]: + for to_flatten in possibly_nested: + if isinstance(to_flatten, UnionProperty): + yield from flatten_union_properties(to_flatten.inner_properties) else: - flattened.append(sub_prop) - return flattened + yield to_flatten - sub_properties = flatten_union_properties(sub_properties) + seen_types = set() + inner_properties: list[PropertyProtocol] = [] + for flattened in flatten_union_properties(sub_properties): + type_string = flattened.get_type_string(no_optional=True) + if type_string not in seen_types: + seen_types.add(type_string) + inner_properties.append(flattened) prop = UnionProperty( name=name, required=required, default=None, - inner_properties=sub_properties, + inner_properties=inner_properties, python_name=PythonIdentifier(value=name, prefix=config.field_prefix), description=data.description, example=data.example, @@ -106,9 +132,12 @@ def convert_value(self, value: Any) -> Value | None | PropertyError: return value_or_error return value_or_error - def _get_inner_type_strings(self, json: bool, multipart: bool) -> set[str]: + def _get_inner_type_strings(self, json: bool) -> set[str]: return { - p.get_type_string(no_optional=True, json=json, multipart=multipart, quoted=not p.is_base_type) + p.get_type_string( + no_optional=True, + json=json, + ) for p in self.inner_properties } @@ -116,15 +145,15 @@ def _get_inner_type_strings(self, json: bool, multipart: bool) -> set[str]: def _get_type_string_from_inner_type_strings(inner_types: set[str]) -> str: if len(inner_types) == 1: return inner_types.pop() - return f"Union[{', '.join(sorted(inner_types))}]" + return " | ".join(sorted(inner_types, key=lambda x: x.lower())) - def get_base_type_string(self, *, quoted: bool = False) -> str: - return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=False, multipart=False)) + def get_base_type_string(self) -> str: + return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=False)) - def get_base_json_type_string(self, *, quoted: bool = False) -> str: - return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=True, multipart=False)) + def get_base_json_type_string(self) -> str: + return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=True)) - def get_type_strings_in_union(self, *, no_optional: bool = False, json: bool, multipart: bool) -> set[str]: + def get_type_strings_in_union(self, *, no_optional: bool = False, json: bool) -> set[str]: """ Get the set of all the types that should appear within the `Union` representing this property. @@ -133,12 +162,11 @@ def get_type_strings_in_union(self, *, no_optional: bool = False, json: bool, mu Args: no_optional: Do not include `None` or `Unset` in this set. json: If True, this returns the JSON types, not the Python types, of this property. - multipart: If True, this returns the multipart types, not the Python types, of this property. Returns: A set of strings containing the types that should appear within `Union`. """ - type_strings = self._get_inner_type_strings(json=json, multipart=multipart) + type_strings = self._get_inner_type_strings(json=json) if no_optional: return type_strings if not self.required: @@ -149,16 +177,13 @@ def get_type_string( self, no_optional: bool = False, json: bool = False, - *, - multipart: bool = False, - quoted: bool = False, ) -> str: """ Get a string representation of type that should be used when declaring this property. This implementation differs slightly from `Property.get_type_string` in order to collapse nested union types. """ - type_strings_in_union = self.get_type_strings_in_union(no_optional=no_optional, json=json, multipart=multipart) + type_strings_in_union = self.get_type_strings_in_union(no_optional=no_optional, json=json) return self._get_type_string_from_inner_type_strings(type_strings_in_union) def get_imports(self, *, prefix: str) -> set[str]: @@ -172,7 +197,7 @@ def get_imports(self, *, prefix: str) -> set[str]: imports = super().get_imports(prefix=prefix) for inner_prop in self.inner_properties: imports.update(inner_prop.get_imports(prefix=prefix)) - imports.add("from typing import cast, Union") + imports.add("from typing import cast") return imports def get_lazy_imports(self, *, prefix: str) -> set[str]: @@ -183,7 +208,7 @@ def get_lazy_imports(self, *, prefix: str) -> set[str]: def validate_location(self, location: oai.ParameterLocation) -> ParseError | None: """Returns an error if this type of property is not allowed in the given location""" - from ..properties import Property + from ..properties import Property # noqa: PLC0415 for inner_prop in self.inner_properties: if evolve(cast(Property, inner_prop), required=self.required).validate_location(location) is not None: diff --git a/openapi_python_client/parser/responses.py b/openapi_python_client/parser/responses.py index ec0f6136b..f05fbce75 100644 --- a/openapi_python_client/parser/responses.py +++ b/openapi_python_client/parser/responses.py @@ -1,7 +1,7 @@ -__all__ = ["Response", "response_from_data"] +__all__ = ["HTTPStatusPattern", "Response", "Responses", "response_from_data"] -from http import HTTPStatus -from typing import Optional, TypedDict, Union +from collections.abc import Iterator +from typing import TypedDict from attrs import define @@ -15,6 +15,20 @@ from .properties import AnyProperty, Property, Schemas, property_from_data +@define +class Responses: + patterns: list["Response"] + default: "Response | None" + + def __iter__(self) -> Iterator["Response"]: + yield from self.patterns + if self.default: + yield self.default + + def __len__(self) -> int: + return len(self.patterns) + (1 if self.default else 0) + + class _ResponseSource(TypedDict): """What data should be pulled from the httpx Response object""" @@ -28,17 +42,93 @@ class _ResponseSource(TypedDict): NONE_SOURCE = _ResponseSource(attribute="None", return_type="None") -@define +class HTTPStatusPattern: + """Status code patterns come in three flavors, in order of precedence: + 1. Specific status codes, such as 200. This is represented by `min` and `max` being the same. + 2. Ranges of status codes, such as 2XX. This is represented by `min` and `max` being different. + 3. The special `default` status code, which is used when no other status codes match. `range` is `None` in this case. + + https://github.com/openapi-generators/openapi-python-client/blob/61b6c54994e2a6285bb422ee3b864c45b5d88c15/openapi_python_client/schema/3.1.0.md#responses-object + """ + + pattern: str + range: tuple[int, int] | None + + def __init__(self, *, pattern: str, code_range: tuple[int, int] | None): + """Initialize with a range of status codes or None for the default case.""" + self.pattern = pattern + self.range = code_range + + @staticmethod + def parse(pattern: str) -> "HTTPStatusPattern | ParseError": + """Parse a status code pattern such as 2XX or 404""" + if pattern == "default": + return HTTPStatusPattern(pattern=pattern, code_range=None) + + if pattern.endswith("XX") and pattern[0].isdigit(): + first_digit = int(pattern[0]) + return HTTPStatusPattern(pattern=pattern, code_range=(first_digit * 100, first_digit * 100 + 99)) + + try: + code = int(pattern) + return HTTPStatusPattern(pattern=pattern, code_range=(code, code)) + except ValueError: + return ParseError( + detail=( + f"Invalid response status code pattern: {pattern}, response will be omitted from generated client" + ) + ) + + def is_range(self) -> bool: + """Check if this is a range of status codes, such as 2XX""" + return self.range is not None and self.range[0] != self.range[1] + + def __lt__(self, other: "HTTPStatusPattern") -> bool: + """Compare two HTTPStatusPattern objects based on the order they should be applied in""" + if self.range is None: + return False # Default gets applied last + if other.range is None: + return True # Other is default, so this one gets applied first + + # Specific codes appear before ranges + if self.is_range() and not other.is_range(): + return False + if not self.is_range() and other.is_range(): + return True + + # Order specific codes numerically + return self.range[0] < other.range[0] + + def __eq__(self, other: object) -> bool: # pragma: no cover + if not isinstance(other, HTTPStatusPattern): + return False + return self.range == other.range + + def __hash__(self) -> int: # pragma: no cover + return hash(self.range) + + def __repr__(self) -> str: # pragma: no cover + return f"" + + +@define(order=False) class Response: """Describes a single response for an endpoint""" - status_code: HTTPStatus + status_code: HTTPStatusPattern prop: Property source: _ResponseSource - data: Union[oai.Response, oai.Reference] # Original data which created this response, useful for custom templates + data: oai.Response | oai.Reference # Original data which created this response, useful for custom templates + + def is_default(self) -> bool: + return self.status_code.range is None + + def __lt__(self, other: "Response") -> bool: + """Compare two responses based on the order in which they should be applied in""" + return self.status_code < other.status_code -def _source_by_content_type(content_type: str, config: Config) -> Optional[_ResponseSource]: +def _source_by_content_type(content_type: str, config: Config) -> _ResponseSource | None: parsed_content_type = utils.get_content_type(content_type, config) if parsed_content_type is None: return None @@ -59,10 +149,10 @@ def _source_by_content_type(content_type: str, config: Config) -> Optional[_Resp def empty_response( *, - status_code: HTTPStatus, + status_code: HTTPStatusPattern, response_name: str, config: Config, - data: Union[oai.Response, oai.Reference], + data: oai.Response | oai.Reference, ) -> Response: """Return an untyped response, for when no response type is defined""" return Response( @@ -82,16 +172,16 @@ def empty_response( def response_from_data( # noqa: PLR0911 *, - status_code: HTTPStatus, - data: Union[oai.Response, oai.Reference], + status_code: HTTPStatusPattern, + data: oai.Response | oai.Reference, schemas: Schemas, - responses: dict[str, Union[oai.Response, oai.Reference]], + responses: dict[str, oai.Response | oai.Reference], parent_name: str, config: Config, -) -> tuple[Union[Response, ParseError], Schemas]: +) -> tuple[Response | ParseError, Schemas]: """Generate a Response from the OpenAPI dictionary representation of it""" - response_name = f"response_{status_code}" + response_name = f"response_{status_code.pattern}" if isinstance(data, oai.Reference): ref_path = parse_reference_path(data.ref) if isinstance(ref_path, ParseError): diff --git a/openapi_python_client/schema/data_type.py b/openapi_python_client/schema/data_type.py index 1c104142e..e80f39803 100644 --- a/openapi_python_client/schema/data_type.py +++ b/openapi_python_client/schema/data_type.py @@ -1,7 +1,7 @@ -from enum import Enum +from enum import StrEnum -class DataType(str, Enum): +class DataType(StrEnum): """The data type of a schema is defined by the type keyword References: diff --git a/openapi_python_client/schema/openapi_schema_pydantic/components.py b/openapi_python_client/schema/openapi_schema_pydantic/components.py index ac5e7648d..3023714c9 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/components.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/components.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict from .callback import Callback @@ -25,15 +23,15 @@ class Components(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#componentsObject """ - schemas: Optional[dict[str, ReferenceOr[Schema]]] = None - responses: Optional[dict[str, ReferenceOr[Response]]] = None - parameters: Optional[dict[str, ReferenceOr[Parameter]]] = None - examples: Optional[dict[str, ReferenceOr[Example]]] = None - requestBodies: Optional[dict[str, ReferenceOr[RequestBody]]] = None - headers: Optional[dict[str, ReferenceOr[Header]]] = None - securitySchemes: Optional[dict[str, ReferenceOr[SecurityScheme]]] = None - links: Optional[dict[str, ReferenceOr[Link]]] = None - callbacks: Optional[dict[str, ReferenceOr[Callback]]] = None + schemas: dict[str, ReferenceOr[Schema]] | None = None + responses: dict[str, ReferenceOr[Response]] | None = None + parameters: dict[str, ReferenceOr[Parameter]] | None = None + examples: dict[str, ReferenceOr[Example]] | None = None + requestBodies: dict[str, ReferenceOr[RequestBody]] | None = None + headers: dict[str, ReferenceOr[Header]] | None = None + securitySchemes: dict[str, ReferenceOr[SecurityScheme]] | None = None + links: dict[str, ReferenceOr[Link]] | None = None + callbacks: dict[str, ReferenceOr[Callback]] | None = None model_config = ConfigDict( # `Callback` contains an unresolvable forward reference, will rebuild in `__init__.py`: defer_build=True, diff --git a/openapi_python_client/schema/openapi_schema_pydantic/contact.py b/openapi_python_client/schema/openapi_schema_pydantic/contact.py index c04fdbbe0..0ff40fa71 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/contact.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/contact.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict @@ -11,9 +9,9 @@ class Contact(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#contactObject """ - name: Optional[str] = None - url: Optional[str] = None - email: Optional[str] = None + name: str | None = None + url: str | None = None + email: str | None = None model_config = ConfigDict( extra="allow", json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py b/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py index 9f36773ba..3fb05ade9 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict @@ -19,7 +17,7 @@ class Discriminator(BaseModel): """ propertyName: str - mapping: Optional[dict[str, str]] = None + mapping: dict[str, str] | None = 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 bb0407ff3..d2725e9f7 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, Optional +from typing import TYPE_CHECKING from pydantic import BaseModel, ConfigDict @@ -16,9 +16,9 @@ class Encoding(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#encodingObject """ - contentType: Optional[str] = None - headers: Optional[dict[str, ReferenceOr["Header"]]] = None - style: Optional[str] = None + contentType: str | None = None + headers: dict[str, ReferenceOr["Header"]] | None = None + style: str | None = None explode: bool = False allowReserved: bool = False model_config = ConfigDict( diff --git a/openapi_python_client/schema/openapi_schema_pydantic/example.py b/openapi_python_client/schema/openapi_schema_pydantic/example.py index 90db2530e..6469457f7 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/example.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/example.py @@ -1,4 +1,4 @@ -from typing import Any, Optional +from typing import Any from pydantic import BaseModel, ConfigDict @@ -11,10 +11,10 @@ class Example(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#exampleObject """ - summary: Optional[str] = None - description: Optional[str] = None - value: Optional[Any] = None - externalValue: Optional[str] = None + summary: str | None = None + description: str | None = None + value: Any | None = None + externalValue: str | None = None model_config = ConfigDict( extra="allow", json_schema_extra={ diff --git a/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py b/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py index 2c0c39b7c..78df42f67 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict @@ -10,7 +8,7 @@ class ExternalDocumentation(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#externalDocumentationObject """ - description: Optional[str] = None + description: str | None = None url: str model_config = ConfigDict( extra="allow", diff --git a/openapi_python_client/schema/openapi_schema_pydantic/info.py b/openapi_python_client/schema/openapi_schema_pydantic/info.py index bec1354da..8b3e59963 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/info.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/info.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict from .contact import Contact @@ -18,10 +16,10 @@ class Info(BaseModel): """ title: str - description: Optional[str] = None - termsOfService: Optional[str] = None - contact: Optional[Contact] = None - license: Optional[License] = None + description: str | None = None + termsOfService: str | None = None + contact: Contact | None = None + license: License | None = None version: str model_config = ConfigDict( extra="allow", diff --git a/openapi_python_client/schema/openapi_schema_pydantic/license.py b/openapi_python_client/schema/openapi_schema_pydantic/license.py index 185eec1db..ee712d92e 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/license.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/license.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict @@ -12,7 +10,7 @@ class License(BaseModel): """ name: str - url: Optional[str] = None + url: str | None = None model_config = ConfigDict( extra="allow", 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 69cdf29c0..77740ceb4 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, Optional +from typing import Any from pydantic import BaseModel, ConfigDict @@ -23,12 +23,12 @@ class Link(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#linkObject """ - operationRef: Optional[str] = None - operationId: Optional[str] = None - parameters: Optional[dict[str, Any]] = None - requestBody: Optional[Any] = None - description: Optional[str] = None - server: Optional[Server] = None + operationRef: str | None = None + operationId: str | None = None + parameters: dict[str, Any] | None = None + requestBody: Any | None = None + description: str | None = None + server: Server | None = None model_config = ConfigDict( extra="allow", json_schema_extra={ 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 48cea8b75..1f4e6090f 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, Optional +from typing import Any from pydantic import BaseModel, ConfigDict, Field @@ -16,10 +16,10 @@ class MediaType(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#mediaTypeObject """ - media_type_schema: Optional[ReferenceOr[Schema]] = Field(default=None, alias="schema") - example: Optional[Any] = None - examples: Optional[dict[str, ReferenceOr[Example]]] = None - encoding: Optional[dict[str, Encoding]] = None + media_type_schema: ReferenceOr[Schema] | None = Field(default=None, alias="schema") + example: Any | None = None + examples: dict[str, ReferenceOr[Example]] | None = None + encoding: dict[str, Encoding] | None = None model_config = ConfigDict( # `Encoding` is not build yet, will rebuild in `__init__.py`: defer_build=True, 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 16e366090..d9066e383 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict @@ -12,9 +10,9 @@ class OAuthFlow(BaseModel): - https://swagger.io/docs/specification/authentication/oauth2/ """ - authorizationUrl: Optional[str] = None - tokenUrl: Optional[str] = None - refreshUrl: Optional[str] = None + authorizationUrl: str | None = None + tokenUrl: str | None = None + refreshUrl: str | None = None scopes: dict[str, str] model_config = ConfigDict( extra="allow", diff --git a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py index dba193713..602a34cb5 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict from .oauth_flow import OAuthFlow @@ -14,8 +12,8 @@ class OAuthFlows(BaseModel): - https://swagger.io/docs/specification/authentication/oauth2/ """ - implicit: Optional[OAuthFlow] = None - password: Optional[OAuthFlow] = None - clientCredentials: Optional[OAuthFlow] = None - authorizationCode: Optional[OAuthFlow] = None + implicit: OAuthFlow | None = None + password: OAuthFlow | None = None + clientCredentials: OAuthFlow | None = None + authorizationCode: OAuthFlow | None = None model_config = ConfigDict(extra="allow") 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 e66ea942c..a98158232 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/open_api.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/open_api.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict, field_validator from .components import Components @@ -24,10 +22,10 @@ class OpenAPI(BaseModel): info: Info servers: list[Server] = [Server(url="/")] paths: Paths - components: Optional[Components] = None - security: Optional[list[SecurityRequirement]] = None - tags: Optional[list[Tag]] = None - externalDocs: Optional[ExternalDocumentation] = None + components: Components | None = None + security: list[SecurityRequirement] | None = None + tags: list[Tag] | None = None + externalDocs: ExternalDocumentation | None = None openapi: str model_config = ConfigDict( # `Components` is not build yet, will rebuild in `__init__.py`: diff --git a/openapi_python_client/schema/openapi_schema_pydantic/operation.py b/openapi_python_client/schema/openapi_schema_pydantic/operation.py index 286ee2143..061be3135 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/operation.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/operation.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict, Field from .callback import Callback @@ -20,19 +18,19 @@ class Operation(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operationObject """ - tags: Optional[list[str]] = None - summary: Optional[str] = None - description: Optional[str] = None - externalDocs: Optional[ExternalDocumentation] = None - operationId: Optional[str] = None - parameters: Optional[list[ReferenceOr[Parameter]]] = None - request_body: Optional[ReferenceOr[RequestBody]] = Field(None, alias="requestBody") + tags: list[str] | None = None + summary: str | None = None + description: str | None = None + externalDocs: ExternalDocumentation | None = None + operationId: str | None = None + parameters: list[ReferenceOr[Parameter]] | None = None + request_body: ReferenceOr[RequestBody] | None = Field(None, alias="requestBody") responses: Responses - callbacks: Optional[dict[str, Callback]] = None + callbacks: dict[str, Callback] | None = None deprecated: bool = False - security: Optional[list[SecurityRequirement]] = None - servers: Optional[list[Server]] = None + security: list[SecurityRequirement] | None = None + servers: list[Server] | None = None model_config = ConfigDict( # `Callback` contains an unresolvable forward reference, will rebuild in `__init__.py`: defer_build=True, diff --git a/openapi_python_client/schema/openapi_schema_pydantic/parameter.py b/openapi_python_client/schema/openapi_schema_pydantic/parameter.py index bf4f4cf02..a963291fc 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, Optional +from typing import Any from pydantic import BaseModel, ConfigDict, Field @@ -23,17 +23,17 @@ class Parameter(BaseModel): name: str param_in: ParameterLocation = Field(alias="in") - description: Optional[str] = None + description: str | None = None required: bool = False deprecated: bool = False allowEmptyValue: bool = False - style: Optional[str] = None + style: str | None = None explode: bool = False allowReserved: bool = False - param_schema: Optional[ReferenceOr[Schema]] = Field(default=None, alias="schema") - example: Optional[Any] = None - examples: Optional[dict[str, ReferenceOr[Example]]] = None - content: Optional[dict[str, MediaType]] = None + param_schema: ReferenceOr[Schema] | None = Field(default=None, alias="schema") + example: Any | None = None + examples: dict[str, ReferenceOr[Example]] | None = None + content: dict[str, MediaType] | None = None model_config = ConfigDict( # `MediaType` is not build yet, will rebuild in `__init__.py`: defer_build=True, 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 44beb2acb..16fee9aeb 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 TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from pydantic import BaseModel, ConfigDict, Field @@ -22,19 +22,19 @@ class PathItem(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#pathItemObject """ - ref: Optional[str] = Field(default=None, alias="$ref") - summary: Optional[str] = None - description: Optional[str] = None - get: Optional["Operation"] = None - put: Optional["Operation"] = None - post: Optional["Operation"] = None - delete: Optional["Operation"] = None - options: Optional["Operation"] = None - head: Optional["Operation"] = None - patch: Optional["Operation"] = None - trace: Optional["Operation"] = None - servers: Optional[list[Server]] = None - parameters: Optional[list[ReferenceOr[Parameter]]] = None + ref: str | None = Field(default=None, alias="$ref") + summary: str | None = None + description: str | None = None + get: "Operation | None" = None + put: "Operation | None" = None + post: "Operation | None" = None + delete: "Operation | None" = None + options: "Operation | None" = None + head: "Operation | None" = None + patch: "Operation | None" = None + trace: "Operation | None" = None + servers: list[Server] | None = None + parameters: list[ReferenceOr[Parameter]] | None = None model_config = ConfigDict( # `Operation` is an unresolvable forward reference, will rebuild in `__init__.py`: defer_build=True, diff --git a/openapi_python_client/schema/openapi_schema_pydantic/reference.py b/openapi_python_client/schema/openapi_schema_pydantic/reference.py index da913c0ce..c1b41e473 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/reference.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/reference.py @@ -1,7 +1,6 @@ -from typing import Annotated, Any, Literal, TypeVar, Union +from typing import Annotated, Any, Literal, TypeAlias, TypeVar from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag -from typing_extensions import TypeAlias class Reference(BaseModel): @@ -39,5 +38,5 @@ def _reference_discriminator(obj: Any) -> Literal["ref", "other"]: ReferenceOr: TypeAlias = Annotated[ - Union[Annotated[Reference, Tag("ref")], Annotated[T, Tag("other")]], Discriminator(_reference_discriminator) + Annotated[Reference, Tag("ref")] | Annotated[T, Tag("other")], Discriminator(_reference_discriminator) ] 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 8cd9bb527..60c454b6d 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/request_body.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/request_body.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict from .media_type import MediaType @@ -13,7 +11,7 @@ class RequestBody(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#requestBodyObject """ - description: Optional[str] = None + description: str | None = None content: dict[str, MediaType] required: bool = False model_config = ConfigDict( diff --git a/openapi_python_client/schema/openapi_schema_pydantic/response.py b/openapi_python_client/schema/openapi_schema_pydantic/response.py index b8e7782a7..e6d8ebb2c 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/response.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/response.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict from .header import Header @@ -19,9 +17,9 @@ class Response(BaseModel): """ description: str - headers: Optional[dict[str, ReferenceOr[Header]]] = None - content: Optional[dict[str, MediaType]] = None - links: Optional[dict[str, ReferenceOr[Link]]] = None + headers: dict[str, ReferenceOr[Header]] | None = None + content: dict[str, MediaType] | None = None + links: dict[str, ReferenceOr[Link]] | None = None model_config = ConfigDict( # `MediaType` is not build yet, will rebuild in `__init__.py`: defer_build=True, diff --git a/openapi_python_client/schema/openapi_schema_pydantic/schema.py b/openapi_python_client/schema/openapi_schema_pydantic/schema.py index e1abdeecb..990f14a3d 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, Optional, Union +from typing import Any from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictFloat, StrictInt, StrictStr, model_validator @@ -20,43 +20,43 @@ class Schema(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schemaObject """ - title: Optional[str] = None - multipleOf: Optional[float] = Field(default=None, gt=0.0) - maximum: Optional[float] = None - exclusiveMaximum: Optional[Union[bool, float]] = None - minimum: Optional[float] = None - exclusiveMinimum: Optional[Union[bool, float]] = None - maxLength: Optional[int] = Field(default=None, ge=0) - minLength: Optional[int] = Field(default=None, ge=0) - pattern: Optional[str] = None - maxItems: Optional[int] = Field(default=None, ge=0) - minItems: Optional[int] = Field(default=None, ge=0) - 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) - const: Union[None, StrictStr, StrictInt, StrictFloat, StrictBool] = None - type: Union[DataType, list[DataType], None] = Field(default=None) + title: str | None = None + multipleOf: float | None = Field(default=None, gt=0.0) + maximum: float | None = None + exclusiveMaximum: bool | float | None = None + minimum: float | None = None + exclusiveMinimum: bool | float | None = None + maxLength: int | None = Field(default=None, ge=0) + minLength: int | None = Field(default=None, ge=0) + pattern: str | None = None + maxItems: int | None = Field(default=None, ge=0) + minItems: int | None = Field(default=None, ge=0) + uniqueItems: bool | None = None + maxProperties: int | None = Field(default=None, ge=0) + minProperties: int | None = Field(default=None, ge=0) + required: list[str] | None = Field(default=None) + enum: None | list[Any] = Field(default=None, min_length=1) + const: None | StrictStr | StrictInt | StrictFloat | StrictBool = None + type: DataType | list[DataType] | None = Field(default=None) allOf: list[ReferenceOr["Schema"]] = Field(default_factory=list) oneOf: list[ReferenceOr["Schema"]] = Field(default_factory=list) anyOf: list[ReferenceOr["Schema"]] = Field(default_factory=list) - schema_not: Optional[ReferenceOr["Schema"]] = Field(default=None, alias="not") - items: Optional[ReferenceOr["Schema"]] = None + schema_not: ReferenceOr["Schema"] | None = Field(default=None, alias="not") + items: ReferenceOr["Schema"] | None = None prefixItems: list[ReferenceOr["Schema"]] = Field(default_factory=list) - properties: Optional[dict[str, ReferenceOr["Schema"]]] = None - additionalProperties: Optional[Union[bool, ReferenceOr["Schema"]]] = None - description: Optional[str] = None - schema_format: Optional[str] = Field(default=None, alias="format") - default: Optional[Any] = None + properties: dict[str, ReferenceOr["Schema"]] | None = None + additionalProperties: bool | ReferenceOr["Schema"] | None = None + description: str | None = None + schema_format: str | None = Field(default=None, alias="format") + default: Any | None = None nullable: bool = Field(default=False) - discriminator: Optional[Discriminator] = None - readOnly: Optional[bool] = None - writeOnly: Optional[bool] = None - xml: Optional[XML] = None - externalDocs: Optional[ExternalDocumentation] = None - example: Optional[Any] = None - deprecated: Optional[bool] = None + discriminator: Discriminator | None = None + readOnly: bool | None = None + writeOnly: bool | None = None + xml: XML | None = None + externalDocs: ExternalDocumentation | None = None + example: Any | None = None + deprecated: bool | None = None model_config = ConfigDict( extra="allow", populate_by_name=True, diff --git a/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py b/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py index df385440c..76b97bd90 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict, Field from .oauth_flows import OAuthFlows @@ -20,13 +18,13 @@ class SecurityScheme(BaseModel): """ type: str - description: Optional[str] = None - name: Optional[str] = None - security_scheme_in: Optional[str] = Field(default=None, alias="in") - scheme: Optional[str] = None - bearerFormat: Optional[str] = None - flows: Optional[OAuthFlows] = None - openIdConnectUrl: Optional[str] = None + description: str | None = None + name: str | None = None + security_scheme_in: str | None = Field(default=None, alias="in") + scheme: str | None = None + bearerFormat: str | None = None + flows: OAuthFlows | None = None + openIdConnectUrl: str | None = None model_config = ConfigDict( extra="allow", populate_by_name=True, diff --git a/openapi_python_client/schema/openapi_schema_pydantic/server.py b/openapi_python_client/schema/openapi_schema_pydantic/server.py index 6bc21766c..abfb77e1b 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/server.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/server.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict from .server_variable import ServerVariable @@ -14,8 +12,8 @@ class Server(BaseModel): """ url: str - description: Optional[str] = None - variables: Optional[dict[str, ServerVariable]] = None + description: str | None = None + variables: dict[str, ServerVariable] | None = 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 8a869c40e..09a705a2b 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict @@ -11,7 +9,7 @@ class ServerVariable(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#serverVariableObject """ - enum: Optional[list[str]] = None + enum: list[str] | None = None default: str - description: Optional[str] = None + description: str | None = None model_config = ConfigDict(extra="allow") diff --git a/openapi_python_client/schema/openapi_schema_pydantic/tag.py b/openapi_python_client/schema/openapi_schema_pydantic/tag.py index acb5fdc28..799810f36 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/tag.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/tag.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict from .external_documentation import ExternalDocumentation @@ -16,8 +14,8 @@ class Tag(BaseModel): """ name: str - description: Optional[str] = None - externalDocs: Optional[ExternalDocumentation] = None + description: str | None = None + externalDocs: ExternalDocumentation | None = None model_config = ConfigDict( extra="allow", json_schema_extra={"examples": [{"name": "pet", "description": "Pets operations"}]} ) diff --git a/openapi_python_client/schema/openapi_schema_pydantic/xml.py b/openapi_python_client/schema/openapi_schema_pydantic/xml.py index 986aa44f4..87082b3c8 100644 --- a/openapi_python_client/schema/openapi_schema_pydantic/xml.py +++ b/openapi_python_client/schema/openapi_schema_pydantic/xml.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel, ConfigDict @@ -16,9 +14,9 @@ class XML(BaseModel): - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xmlObject """ - name: Optional[str] = None - namespace: Optional[str] = None - prefix: Optional[str] = None + name: str | None = None + namespace: str | None = None + prefix: str | None = None attribute: bool = False wrapped: bool = False model_config = ConfigDict( diff --git a/openapi_python_client/schema/parameter_location.py b/openapi_python_client/schema/parameter_location.py index 162a7cb13..61f9dae14 100644 --- a/openapi_python_client/schema/parameter_location.py +++ b/openapi_python_client/schema/parameter_location.py @@ -1,25 +1,10 @@ -# Python 3.11 has StrEnum but breaks the old `str, Enum` hack. -# Unless this gets fixed, we need to have two implementations :( -import sys +from enum import StrEnum -if sys.version_info >= (3, 11): - from enum import StrEnum - class ParameterLocation(StrEnum): - """The places Parameters can be put when calling an Endpoint""" +class ParameterLocation(StrEnum): + """The places Parameters can be put when calling an Endpoint""" - QUERY = "query" - PATH = "path" - HEADER = "header" - COOKIE = "cookie" - -else: - from enum import Enum - - class ParameterLocation(str, Enum): - """The places Parameters can be put when calling an Endpoint""" - - QUERY = "query" - PATH = "path" - HEADER = "header" - COOKIE = "cookie" + QUERY = "query" + PATH = "path" + HEADER = "header" + COOKIE = "cookie" diff --git a/openapi_python_client/templates/README.md.jinja b/openapi_python_client/templates/README.md.jinja index ea31c83d7..66a9c1669 100644 --- a/openapi_python_client/templates/README.md.jinja +++ b/openapi_python_client/templates/README.md.jinja @@ -47,7 +47,7 @@ By default, when you're calling an HTTPS API it will attempt to verify that SSL ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -57,7 +57,7 @@ You can also disable certificate validation altogether, but beware that **this i ```python client = AuthenticatedClient( - base_url="https://internal_api.example.com", + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl=False ) @@ -109,7 +109,7 @@ client = Client( client.set_httpx_client(httpx.Client(base_url="https://api.example.com", proxies="http://localhost:8030")) ``` -{% if poetry %} +{% if meta == "poetry" %} ## Building / publishing this package This project uses [Poetry](https://python-poetry.org/) to manage dependencies and packaging. Here are the basics: 1. Update the metadata in pyproject.toml (e.g. authors, version) @@ -123,4 +123,17 @@ If you want to install this client into another project without publishing it (e 1. If that project is not using Poetry: 1. Build a wheel with `poetry build -f wheel` 1. Install that wheel from the other project `pip install ` +{% elif meta == 'uv' %} +## Building / publishing this package +This project uses [uv](https://github.com/astral-sh/uv) to manage dependencies and packaging. Here are the basics: +1. Update the metadata in `pyproject.toml` (e.g. authors, version). +2. If you're using a private repository: https://docs.astral.sh/uv/guides/integration/alternative-indexes/ +3. Build a distribution with `uv build`, builds `sdist` and `wheel` by default. +1. Publish the client with `uv publish`, see documentation for publishing to private indexes. + +If you want to install this client into another project without publishing it (e.g. for development) then: +1. If that project **is using uv**, you can simply do `uv add ` from that project +1. If that project is not using uv: + 1. Build a wheel with `uv build --wheel`. + 1. Install that wheel from the other project `pip install `. {% endif %} \ No newline at end of file diff --git a/openapi_python_client/templates/client.py.jinja b/openapi_python_client/templates/client.py.jinja index cf0301a9a..a8c4ac5c1 100644 --- a/openapi_python_client/templates/client.py.jinja +++ b/openapi_python_client/templates/client.py.jinja @@ -1,5 +1,5 @@ import ssl -from typing import Any, Union, Optional +from typing import Any, Self from attrs import define, field, evolve import httpx @@ -65,12 +65,12 @@ class Client: _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") - _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") + _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: 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") - _client: Optional[httpx.Client] = field(default=None, init=False) - _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + _client: httpx.Client | None = field(default=None, init=False) + _async_client: httpx.AsyncClient | None = field(default=None, init=False) {% endmacro %}{{ attributes() }} {% macro builders(self) %} def with_headers(self, headers: dict[str, str]) -> "{{ self }}": @@ -90,7 +90,7 @@ class Client: return evolve(self, cookies={**self._cookies, **cookies}) def with_timeout(self, timeout: httpx.Timeout) -> "{{ self }}": - """Get a new client matching this one with a new timeout (in seconds)""" + """Get a new client matching this one with a new timeout configuration""" if self._client is not None: self._client.timeout = timeout if self._async_client is not None: @@ -98,7 +98,7 @@ class Client: return evolve(self, timeout=timeout) {% endmacro %}{{ builders("Client") }} {% macro httpx_stuff(name, custom_constructor=None) %} - def set_httpx_client(self, client: httpx.Client) -> "{{ name }}": + def set_httpx_client(self, client: httpx.Client) -> Self: """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. @@ -123,7 +123,7 @@ class Client: ) return self._client - def __enter__(self) -> "{{ name }}": + def __enter__(self) -> Self: """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" self.get_httpx_client().__enter__() return self @@ -132,8 +132,8 @@ class Client: """Exit a context manager for internal httpx.Client (see httpx docs)""" self.get_httpx_client().__exit__(*args, **kwargs) - def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "{{ name }}": - """Manually the underlying httpx.AsyncClient + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> Self: + """Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -157,7 +157,7 @@ class Client: ) return self._async_client - async def __aenter__(self) -> "{{ name }}": + async def __aenter__(self) -> Self: """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" await self.get_async_httpx_client().__aenter__() return self diff --git a/openapi_python_client/templates/endpoint_macros.py.jinja b/openapi_python_client/templates/endpoint_macros.py.jinja index 2da580af4..9688a4ba2 100644 --- a/openapi_python_client/templates/endpoint_macros.py.jinja +++ b/openapi_python_client/templates/endpoint_macros.py.jinja @@ -58,33 +58,46 @@ params = {k: v for k, v in params.items() if v is not UNSET and v is not None} {% endif %} {% endmacro %} -{% macro body_to_kwarg(body, destination) %} +{% macro body_to_kwarg(body) %} {% if body.body_type == "data" %} -{{ destination }} = body.to_dict() + {% if body.prop.required %} +_kwargs["data"] = body.to_dict() + {% else %} +if not isinstance(body, Unset): + _kwargs["data"] = body.to_dict() + {% endif %} {% elif body.body_type == "files"%} -{{ multipart_body(body, destination) }} +{{ multipart_body(body) }} {% elif body.body_type == "json" %} -{{ json_body(body, destination) }} +{{ json_body(body) }} {% elif body.body_type == "content" %} -{{ destination }} = body.payload + {% if body.prop.required %} +_kwargs["content"] = body.payload + {% else %} +if not isinstance(body, Unset): + _kwargs["content"] = body.payload + {% endif %} {% endif %} {% endmacro %} -{% macro json_body(body, destination) %} +{% macro json_body(body) %} {% set property = body.prop %} {% import "property_templates/" + property.template as prop_template %} {% if prop_template.transform %} -{{ prop_template.transform(property, property.python_name, destination) }} +{{ prop_template.transform(property, property.python_name, "_kwargs[\"json\"]", skip_unset=True, declare_type=False) }} +{% elif property.required %} +_kwargs["json"] = {{ property.python_name }} {% else %} -{{ destination }} = {{ property.python_name }} +if not isinstance({{property.python_name}}, Unset): + _kwargs["json"] = {{ property.python_name }} {% endif %} {% endmacro %} -{% macro multipart_body(body, destination) %} +{% macro multipart_body(body) %} {% set property = body.prop %} {% import "property_templates/" + property.template as prop_template %} {% if prop_template.transform_multipart_body %} -{{ prop_template.transform_multipart_body(property, property.python_name, destination) }} +{{ prop_template.transform_multipart_body(property) }} {% endif %} {% endmacro %} @@ -102,18 +115,18 @@ params = {k: v for k, v in params.items() if v is not UNSET and v is not None} {% if endpoint.requires_security %} client: AuthenticatedClient, {% else %} -client: Union[AuthenticatedClient, Client], +client: AuthenticatedClient | Client, {% endif %} {% endif %} {# Any allowed bodies #} {% if endpoint.bodies | length == 1 %} -body: {{ endpoint.bodies[0].prop.get_type_string() }}, +body: {{ endpoint.bodies[0].prop.get_type_string() }}{% if not endpoint.bodies[0].prop.required %} = UNSET{% endif %}, {% elif endpoint.bodies | length > 1 %} -body: Union[ - {% for body in endpoint.bodies %} - {{ body.prop.get_type_string() }}, - {% endfor %} -], +body: + {%- for body in endpoint.bodies -%}{% set body_required = body_required and body.prop.required %} + {{ body.prop.get_type_string(no_optional=True) }} {% if not loop.last %} | {% endif %} + {%- endfor -%}{% if not body_required %} | Unset = UNSET{% endif %} +, {% endif %} {# query parameters #} {% for parameter in endpoint.query_parameters %} @@ -184,3 +197,18 @@ Returns: {% macro docstring(endpoint, return_string, is_detailed) %} {{ safe_docstring(docstring_content(endpoint, return_string, is_detailed)) }} {% endmacro %} + +{% macro parse_response(parsed_responses, response) %} +{% if parsed_responses %}{% import "property_templates/" + response.prop.template as prop_template %} +{% if prop_template.construct %} +{{ prop_template.construct(response.prop, response.source.attribute) }} +{% elif response.source.return_type == response.prop.get_type_string() %} +{{ response.prop.python_name }} = {{ response.source.attribute }} +{% else %} +{{ response.prop.python_name }} = cast({{ response.prop.get_type_string() }}, {{ response.source.attribute }}) +{% endif %} +return {{ response.prop.python_name }} +{% else %} +return None +{% endif %} +{% endmacro %} diff --git a/openapi_python_client/templates/endpoint_module.py.jinja b/openapi_python_client/templates/endpoint_module.py.jinja index 35090614c..63e617778 100644 --- a/openapi_python_client/templates/endpoint_module.py.jinja +++ b/openapi_python_client/templates/endpoint_module.py.jinja @@ -1,5 +1,6 @@ from http import HTTPStatus -from typing import Any, Optional, Union, cast +from typing import Any, cast +from urllib.parse import quote import httpx @@ -31,7 +32,7 @@ def _get_kwargs( {% if endpoint.path_parameters %} "url": "{{ endpoint.path }}".format( {%- for parameter in endpoint.path_parameters -%} - {{parameter.python_name}}={{parameter.python_name}}, + {{parameter.python_name}}=quote(str({{parameter.python_name}}), safe=""), {%- endfor -%} ), {% else %} @@ -47,17 +48,20 @@ def _get_kwargs( {% if endpoint.bodies | length > 1 %} {% for body in endpoint.bodies %} - if isinstance(body, {{body.prop.get_type_string() }}): - {% set destination = "_" + body.body_type + "_body" %} - {{ body_to_kwarg(body, destination) | indent(8) }} - _kwargs["{{ body.body_type.value }}"] = {{ destination }} + if isinstance(body, {{body.prop.get_type_string(no_optional=True) }}): + {{ body_to_kwarg(body) | indent(8) }} + {%- if body.content_type == "multipart/form-data" %} + headers["Content-Type"] = "multipart/form-data; boundary=+++" + {% else %} headers["Content-Type"] = "{{ body.content_type }}" + {% endif %} {% endfor %} {% elif endpoint.bodies | length == 1 %} {% set body = endpoint.bodies[0] %} - {{ body_to_kwarg(body, "_body") | indent(4) }} - _kwargs["{{ body.body_type.value }}"] = _body - {% if body.content_type != "multipart/form-data" %}{# Need httpx to set the boundary automatically #} + {{ body_to_kwarg(body) | indent(4) }} + {%- if body.content_type == "multipart/form-data" %} + headers["Content-Type"] = "multipart/form-data; boundary=+++" + {% else %} headers["Content-Type"] = "{{ body.content_type }}" {% endif %} {% endif %} @@ -67,30 +71,34 @@ def _get_kwargs( {% endif %} return _kwargs +{% if endpoint.responses.default %} + {% set return_type = return_string %} +{% else %} + {% set return_type = return_string + " | None" %} +{% endif %} -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[{{ return_string }}]: - {% for response in endpoint.responses %} - if response.status_code == {{ response.status_code.value }}: - {% if parsed_responses %}{% import "property_templates/" + response.prop.template as prop_template %} - {% if prop_template.construct %} - {{ prop_template.construct(response.prop, response.source.attribute) | indent(8) }} - {% elif response.source.return_type == response.prop.get_type_string() %} - {{ response.prop.python_name }} = {{ response.source.attribute }} - {% else %} - {{ response.prop.python_name }} = cast({{ response.prop.get_type_string() }}, {{ response.source.attribute }}) - {% endif %} - return {{ response.prop.python_name }} - {% else %} - return None - {% endif %} + +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> {{return_type}}: + {% for response in endpoint.responses.patterns %} + {% set code_range = response.status_code.range %} + {% if code_range[0] == code_range[1] %} + if response.status_code == {{ code_range[0] }}: + {% else %} + if {{ code_range[0] }} <= response.status_code <= {{ code_range[1] }}: + {% endif %} + {{ parse_response(parsed_responses, response) | indent(8) }} {% endfor %} + {% if endpoint.responses.default %} + {{ parse_response(parsed_responses, endpoint.responses.default) | indent(4) }} + {% else %} if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: return None + {% endif %} -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[{{ return_string }}]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[{{ return_string }}]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -117,7 +125,7 @@ def sync_detailed( {% if parsed_responses %} def sync( {{ arguments(endpoint) | indent(4) }} -) -> Optional[{{ return_string }}]: +) -> {{ return_string }} | None: {{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }} return sync_detailed( @@ -143,7 +151,7 @@ async def asyncio_detailed( {% if parsed_responses %} async def asyncio( {{ arguments(endpoint) | indent(4) }} -) -> Optional[{{ return_string }}]: +) -> {{ return_string }} | None: {{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }} return (await asyncio_detailed( diff --git a/openapi_python_client/templates/literal_enum.py.jinja b/openapi_python_client/templates/literal_enum.py.jinja index 72207efa3..a4529292b 100644 --- a/openapi_python_client/templates/literal_enum.py.jinja +++ b/openapi_python_client/templates/literal_enum.py.jinja @@ -1,4 +1,4 @@ -from typing import Literal, cast +from typing import Literal {{ enum.class_info.name }} = Literal{{ "%r" | format(enum.values|list|sort) }} @@ -6,5 +6,5 @@ from typing import Literal, cast 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: - return cast({{enum.class_info.name}}, value) + return value raise TypeError(f"Unexpected value {value!r}. Expected one of {{"{"}}{{ enum.get_class_name_snake_case() | upper }}_VALUES!r}") diff --git a/openapi_python_client/templates/model.py.jinja b/openapi_python_client/templates/model.py.jinja index eed2ea3a0..06a71cb22 100644 --- a/openapi_python_client/templates/model.py.jinja +++ b/openapi_python_client/templates/model.py.jinja @@ -1,10 +1,13 @@ +from __future__ import annotations + from collections.abc import Mapping -from typing import Any, TypeVar, Optional, BinaryIO, TextIO, TYPE_CHECKING +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator from attrs import define as _attrs_define from attrs import field as _attrs_field {% if model.is_multipart_body %} import json +from .. import types {% endif %} from ..types import UNSET, Unset @@ -13,7 +16,7 @@ from ..types import UNSET, Unset {{ relative }} {% endfor %} -{% for lazy_import in model.lazy_imports %} +{% for lazy_import in model.lazy_imports | sort %} {% if loop.first %} if TYPE_CHECKING: {% endif %} @@ -22,7 +25,7 @@ if TYPE_CHECKING: {% if model.additional_properties %} -{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string(quoted=not model.additional_properties.is_base_type) %} +{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %} {% endif %} {% set class_name = model.class_info.name %} @@ -82,32 +85,45 @@ class {{ class_name }}: additional_properties: dict[str, {{ additional_property_type }}] = _attrs_field(init=False, factory=dict) {% endif %} -{% macro _to_dict(multipart=False) %} -{% for property in model.required_properties + model.optional_properties %} +{% macro _transform_property(property, content) %} +{% import "property_templates/" + property.template as prop_template %} +{%- if prop_template.transform -%} +{{ prop_template.transform(property=property, source=content, destination=property.python_name) }} +{%- else -%} +{{ property.python_name }} = {{ content }} +{%- endif -%} +{% endmacro %} + +{% macro multipart(property, source, destination) %} {% import "property_templates/" + property.template as prop_template %} -{% if multipart %} -{{ prop_template.transform_multipart(property, "self." + property.python_name, property.python_name) }} -{% elif prop_template.transform %} -{{ prop_template.transform(property=property, source="self." + property.python_name, destination=property.python_name) }} +{% if not property.required %} +if not isinstance({{source}}, Unset): + {{ prop_template.multipart(property, source, destination) | indent(4) }} {% else %} -{{ property.python_name }} = self.{{ property.python_name }} +{{ prop_template.multipart(property, source, destination) }} {% endif %} +{% endmacro %} -{% endfor %} - +{% macro _prepare_field_dict() %} field_dict: dict[str, Any] = {} {% if model.additional_properties %} {% import "property_templates/" + model.additional_properties.template as prop_template %} -{% if multipart %} -for prop_name, prop in self.additional_properties.items(): - {{ prop_template.transform_multipart(model.additional_properties, "prop", "field_dict[prop_name]") | indent(4) }} -{% elif prop_template.transform %} +{% if prop_template.transform %} for prop_name, prop in self.additional_properties.items(): {{ prop_template.transform(model.additional_properties, "prop", "field_dict[prop_name]", declare_type=false) | indent(4) }} {% else %} field_dict.update(self.additional_properties) -{% endif %} -{% endif %} +{%- endif -%} +{%- endif -%} +{% endmacro %} + +{% macro _to_dict() %} +{% for property in model.required_properties + model.optional_properties -%} +{{ _transform_property(property, "self." + property.python_name) }} + +{% endfor %} + +{{ _prepare_field_dict() }} {% if model.required_properties | length > 0 or model.optional_properties | length > 0 %} field_dict.update({ {% for property in model.required_properties + model.optional_properties %} @@ -128,20 +144,37 @@ return field_dict {% endmacro %} def to_dict(self) -> dict[str, Any]: - {% for lazy_import in model.lazy_imports %} - {{ lazy_import }} + {% for lazy_import in model.lazy_imports | sort %} + {{ lazy_import }} # noqa: PLC0415 {% endfor %} {{ _to_dict() | indent(8) }} {% if model.is_multipart_body %} - def to_multipart(self) -> dict[str, Any]: - {{ _to_dict(multipart=True) | indent(8) }} + def to_multipart(self) -> types.RequestFiles: + {% for lazy_import in model.lazy_imports | sort %} + {{ lazy_import }} # noqa: PLC0415 + {% endfor %} + files: types.RequestFiles = [] + + {% for property in model.required_properties + model.optional_properties %} + {% set destination = "\"" + property.name + "\"" %} + {{ multipart(property, "self." + property.python_name, destination) | indent(8) }} + + {% endfor %} + + {% if model.additional_properties %} + for prop_name, prop in self.additional_properties.items(): + {{ multipart(model.additional_properties, "prop", "prop_name") | indent(4) }} + {% endif %} + + return files + {% endif %} @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - {% for lazy_import in model.lazy_imports %} - {{ lazy_import }} + {% for lazy_import in model.lazy_imports | sort %} + {{ lazy_import }} # noqa: PLC0415 {% endfor %} {% if (model.required_properties or model.optional_properties or model.additional_properties) %} d = dict(src_dict) @@ -171,8 +204,8 @@ return field_dict {% import "property_templates/" + model.additional_properties.template as prop_template %} {% if model.additional_properties.lazy_imports %} - {% for lazy_import in model.additional_properties.lazy_imports %} - {{ lazy_import }} + {% for lazy_import in model.additional_properties.lazy_imports | sort %} + {{ lazy_import }} # noqa: PLC0415 {% endfor %} {% endif %} {% else %} diff --git a/openapi_python_client/templates/property_templates/any_property.py.jinja b/openapi_python_client/templates/property_templates/any_property.py.jinja index 25a16516a..ad3f195a4 100644 --- a/openapi_python_client/templates/property_templates/any_property.py.jinja +++ b/openapi_python_client/templates/property_templates/any_property.py.jinja @@ -1,7 +1,3 @@ -{% macro transform_multipart(property, source, destination) %} -{% if not property.required %} -{{ destination }} = {{source}} if isinstance({{source}}, Unset) else (None, str({{source}}).encode(), "text/plain") -{% else %} -{{ destination }} = (None, str({{ source }}).encode(), "text/plain") -{% endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, str({{source}}).encode(), "text/plain"))) {% endmacro %} \ No newline at end of file diff --git a/openapi_python_client/templates/property_templates/boolean_property.py.jinja b/openapi_python_client/templates/property_templates/boolean_property.py.jinja index 52fda08e1..e2c3392a1 100644 --- a/openapi_python_client/templates/property_templates/boolean_property.py.jinja +++ b/openapi_python_client/templates/property_templates/boolean_property.py.jinja @@ -2,10 +2,6 @@ "true" if {{ source }} else "false" {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% if not property.required %} -{{ destination }} = {{source}} if isinstance({{source}}, Unset) else (None, str({{source}}).encode(), "text/plain") -{% else %} -{{ destination }} = (None, str({{ source }}).encode(), "text/plain") -{% endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, str({{source}}).encode(), "text/plain"))) {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/const_property.py.jinja b/openapi_python_client/templates/property_templates/const_property.py.jinja index fa635b5b9..d348de0ff 100644 --- a/openapi_python_client/templates/property_templates/const_property.py.jinja +++ b/openapi_python_client/templates/property_templates/const_property.py.jinja @@ -3,3 +3,7 @@ if {{ property.python_name }} != {{ property.value.python_code }}{% if not property.required %}and not isinstance({{ property.python_name }}, Unset){% endif %}: raise ValueError(f"{{ property.name }} must match const {{ property.value.python_code }}, got '{{'{' + property.python_name + '}' }}'") {%- endmacro %} + +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, {{ source }}, "text/plain"))) +{% endmacro %} diff --git a/openapi_python_client/templates/property_templates/date_property.py.jinja b/openapi_python_client/templates/property_templates/date_property.py.jinja index b5da665f7..a11f3d3b1 100644 --- a/openapi_python_client/templates/property_templates/date_property.py.jinja +++ b/openapi_python_client/templates/property_templates/date_property.py.jinja @@ -1,5 +1,5 @@ {% macro construct_function(property, source) %} -isoparse({{ source }}).date() +datetime.date.fromisoformat({{ source }}) {% endmacro %} {% from "property_templates/property_macros.py.jinja" import construct_template %} @@ -10,30 +10,24 @@ isoparse({{ source }}).date() {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set transformed = source + ".isoformat()" %} {% if property.required %} {{ destination }} = {{ transformed }} {%- else %} -{% if declare_type %} -{% set type_annotation = property.get_type_string(json=True) %} +{% if not skip_unset %} + {% if declare_type %} + {% set type_annotation = property.get_type_string(json=True) %} {{ destination }}: {{ type_annotation }} = UNSET -{% else %} + {% else %} {{ destination }} = UNSET + {% endif %} {% endif %} if not isinstance({{ source }}, Unset): {{ destination }} = {{ transformed }} {%- endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% set transformed = source + ".isoformat().encode()" %} -{% if property.required %} -{{ destination }} = {{ transformed }} -{%- else %} -{% set type_annotation = property.get_type_string(json=True) | replace("str", "bytes") %} -{{ destination }}: {{ type_annotation }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = {{ transformed }} -{%- endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, {{ source }}.isoformat().encode(), "text/plain"))) {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/datetime_property.py.jinja b/openapi_python_client/templates/property_templates/datetime_property.py.jinja index 32e3f2ee6..e669edb08 100644 --- a/openapi_python_client/templates/property_templates/datetime_property.py.jinja +++ b/openapi_python_client/templates/property_templates/datetime_property.py.jinja @@ -1,5 +1,5 @@ {% macro construct_function(property, source) %} -isoparse({{ source }}) +datetime.datetime.fromisoformat({{ source }}) {% endmacro %} {% from "property_templates/property_macros.py.jinja" import construct_template %} @@ -10,30 +10,24 @@ isoparse({{ source }}) {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set transformed = source + ".isoformat()" %} {% if property.required %} {{ destination }} = {{ transformed }} {%- else %} -{% if declare_type %} -{% set type_annotation = property.get_type_string(json=True) %} + {% if not skip_unset %} + {% if declare_type %} + {% set type_annotation = property.get_type_string(json=True) %} {{ destination }}: {{ type_annotation }} = UNSET -{% else %} + {% else %} {{ destination }} = UNSET -{% endif %} + {% endif %} + {% endif %} if not isinstance({{ source }}, Unset): {{ destination }} = {{ transformed }} {%- endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% set transformed = source + ".isoformat().encode()" %} -{% if property.required %} -{{ destination }} = {{ transformed }} -{%- else %} -{% set type_annotation = property.get_type_string(json=True) | replace("str", "bytes") %} -{{ destination }}: {{ type_annotation }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = {{ transformed }} -{%- endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, {{ source }}.isoformat().encode(), "text/plain"))) {% endmacro %} 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 c46538eac..19ea03b28 100644 --- a/openapi_python_client/templates/property_templates/enum_property.py.jinja +++ b/openapi_python_client/templates/property_templates/enum_property.py.jinja @@ -10,28 +10,20 @@ {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.value_type.__name__ }}){% endmacro %} -{% macro transform(property, source, destination, declare_type=True, multipart=False) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set transformed = source + ".value" %} {% set type_string = property.get_type_string(json=True) %} {% if property.required %} {{ destination }} = {{ transformed }} {%- else %} -{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET +{% if not skip_unset %}{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET{% endif +%} if not isinstance({{ source }}, Unset): {{ destination }} = {{ transformed }} {% endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% set transformed = "(None, str(" + source + ".value" + ").encode(), \"text/plain\")" %} -{% set type_string = "Union[Unset, tuple[None, bytes, str]]" %} -{% if property.required %} -{{ destination }} = {{ transformed }} -{%- else %} -{{ destination }}: {{ type_string }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = {{ transformed }} -{% endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, str({{ source }}.value).encode(), "text/plain"))) {% endmacro %} {% macro transform_header(source) %} diff --git a/openapi_python_client/templates/property_templates/file_property.py.jinja b/openapi_python_client/templates/property_templates/file_property.py.jinja index 8d27ae617..ac76470a7 100644 --- a/openapi_python_client/templates/property_templates/file_property.py.jinja +++ b/openapi_python_client/templates/property_templates/file_property.py.jinja @@ -12,22 +12,16 @@ File( {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, bytes){% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% if property.required %} {{ destination }} = {{ source }}.to_tuple() {% else %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET +{% if not skip_unset %}{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET{% endif +%} if not isinstance({{ source }}, Unset): {{ destination }} = {{ source }}.to_tuple() {% endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% if property.required %} -{{ destination }} = {{ source }}.to_tuple() -{% else %} -{{ destination }}: {{ property.get_type_string(json=True) }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = {{ source }}.to_tuple() -{% endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, {{ source }}.to_tuple())) {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/float_property.py.jinja b/openapi_python_client/templates/property_templates/float_property.py.jinja index 12ffb0fb4..dc982cb68 100644 --- a/openapi_python_client/templates/property_templates/float_property.py.jinja +++ b/openapi_python_client/templates/property_templates/float_property.py.jinja @@ -2,10 +2,6 @@ str({{ source }}) {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% if not property.required %} -{{ destination }} = {{source}} if isinstance({{source}}, Unset) else (None, str({{source}}).encode(), "text/plain") -{% else %} -{{ destination }} = (None, str({{ source }}).encode(), "text/plain") -{% endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, str({{source}}).encode(), "text/plain"))) {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/int_property.py.jinja b/openapi_python_client/templates/property_templates/int_property.py.jinja index 12ffb0fb4..dc982cb68 100644 --- a/openapi_python_client/templates/property_templates/int_property.py.jinja +++ b/openapi_python_client/templates/property_templates/int_property.py.jinja @@ -2,10 +2,6 @@ str({{ source }}) {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% if not property.required %} -{{ destination }} = {{source}} if isinstance({{source}}, Unset) else (None, str({{source}}).encode(), "text/plain") -{% else %} -{{ destination }} = (None, str({{ source }}).encode(), "text/plain") -{% endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, str({{source}}).encode(), "text/plain"))) {% endmacro %} 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 94a9c6d65..e765ed3b2 100644 --- a/openapi_python_client/templates/property_templates/list_property.py.jinja +++ b/openapi_python_client/templates/property_templates/list_property.py.jinja @@ -3,26 +3,28 @@ {% import "property_templates/" + inner_property.template as inner_template %} {% if inner_template.construct %} {% set inner_source = inner_property.python_name + "_data" %} +{% if property.required %} {{ property.python_name }} = [] _{{ property.python_name }} = {{ source }} -{% if property.required %} for {{ inner_source }} in (_{{ property.python_name }}): -{% else %} -for {{ inner_source }} in (_{{ property.python_name }} or []): -{% endif %} {{ inner_template.construct(inner_property, inner_source) | indent(4) }} {{ property.python_name }}.append({{ inner_property.python_name }}) {% else %} +_{{ property.python_name }} = {{ source }} +{{ property.python_name }}: {{ property.get_type_string() }} = UNSET +if _{{ property.python_name }} is not UNSET: + {{ property.python_name }} = [] + for {{ inner_source }} in _{{ property.python_name }}: + {{ inner_template.construct(inner_property, inner_source) | indent(8) }} + {{ property.python_name }}.append({{ inner_property.python_name }}) +{% endif %} +{% else %} {{ property.python_name }} = cast({{ property.get_type_string(no_optional=True) }}, {{ source }}) {% endif %} {% endmacro %} -{% macro _transform(property, source, destination, multipart, transform_method) %} +{% macro _transform(property, source, destination, transform_method) %} {% set inner_property = property.inner_property %} -{% if multipart %} -{% set multipart_destination = destination %} -{% set destination = "_temp_" + destination %} -{% endif %} {% import "property_templates/" + inner_property.template as inner_template %} {% if inner_template.transform %} {% set inner_source = inner_property.python_name + "_data" %} @@ -33,45 +35,26 @@ for {{ inner_source }} in {{ source }}: {% else %} {{ destination }} = {{ source }} {% endif %} -{% if multipart %} -{{ multipart_destination }} = (None, json.dumps({{ destination }}).encode(), 'application/json') -{% endif %} {% endmacro %} {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, list){% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set inner_property = property.inner_property %} {% set type_string = property.get_type_string(json=True) %} {% if property.required %} -{{ _transform(property, source, destination, False, "to_dict") }} +{{ _transform(property, source, destination, "to_dict") }} {% else %} -{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET +{% if not skip_unset %}{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET{% endif +%} if not isinstance({{ source }}, Unset): - {{ _transform(property, source, destination, False, "to_dict") | indent(4)}} + {{ _transform(property, source, destination, "to_dict") | indent(4)}} {% endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} +{% macro multipart(property, source, destination) %} {% set inner_property = property.inner_property %} -{% set type_string = "Union[Unset, tuple[None, bytes, str]]" %} -{% if property.required %} -{{ _transform(property, source, destination, True, "to_dict") }} -{% else %} -{{ destination }}: {{ type_string }} = UNSET -if not isinstance({{ source }}, Unset): - {{ _transform(property, source, destination, True, "to_dict") | indent(4)}} -{% endif %} -{% endmacro %} - -{% macro transform_multipart_body(property, source, destination) %} -{% set inner_property = property.inner_property %} -{% set type_string = property.get_type_string(json=True) %} -{% if property.required %} -{{ _transform(property, source, destination, False, "to_multipart") }} -{% else %} -{{ destination }}: {{ type_string }} = UNSET -if not isinstance({{ source }}, Unset): - {{ _transform(property, source, destination, False, "to_multipart") | indent(4)}} -{% endif %} +{% import "property_templates/" + inner_property.template as inner_template %} +{% set inner_source = inner_property.python_name + "_element" %} +for {{ inner_source }} in {{ source }}: + {{ inner_template.multipart(inner_property, inner_source, destination) | indent(4) }} {% endmacro %} 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 1506284d7..a0dd3d19b 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 @@ -10,27 +10,19 @@ check_{{ property.get_class_name_snake_case() }}({{ source }}) {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.get_instance_type_string() }}){% endmacro %} -{% macro transform(property, source, destination, declare_type=True, multipart=False) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set type_string = property.get_type_string(json=True) %} {% if property.required %} {{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = {{ source }} {%- else %} -{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET +{% if not skip_unset %}{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET{% endif +%} if not isinstance({{ source }}, Unset): {{ destination }} = {{ source }} {% endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% set transformed = "(None, str(" + source + ").encode(), \"text/plain\")" %} -{% set type_string = "Union[Unset, tuple[None, bytes, str]]" %} -{% if property.required %} -{{ destination }} = {{ transformed }} -{%- else %} -{{ destination }}: {{ type_string }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = {{ transformed }} -{% endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, str({{ source }}).encode(), "text/plain"))) {% endmacro %} {% macro transform_header(source) %} diff --git a/openapi_python_client/templates/property_templates/model_property.py.jinja b/openapi_python_client/templates/property_templates/model_property.py.jinja index 308b7478b..a442ccb70 100644 --- a/openapi_python_client/templates/property_templates/model_property.py.jinja +++ b/openapi_python_client/templates/property_templates/model_property.py.jinja @@ -10,38 +10,29 @@ {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, dict){% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set transformed = source + ".to_dict()" %} {% set type_string = property.get_type_string(json=True) %} {% if property.required %} {{ destination }} = {{ transformed }} {%- else %} -{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET +{% if not skip_unset %}{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET{% endif %} + if not isinstance({{ source }}, Unset): {{ destination }} = {{ transformed }} {%- endif %} {% endmacro %} -{% macro transform_multipart_body(property, source, destination) %} -{% set transformed = source + ".to_multipart()" %} -{% set type_string = property.get_type_string(multipart=True) %} +{% macro transform_multipart_body(property) %} +{% set transformed = property.python_name + ".to_multipart()" %} {% if property.required %} -{{ destination }} = {{ transformed }} +_kwargs["files"] = {{ transformed }} {%- else %} -{{ destination }}: {{ type_string }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = {{ transformed }} +if not isinstance({{ property.python_name }}, Unset): + _kwargs["files"] = {{ transformed }} {%- endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% set transformed = "(None, json.dumps(" + source + ".to_dict()" + ").encode(), 'application/json')" %} -{% set type_string = property.get_type_string(multipart=True) %} -{% if property.required %} -{{ destination }} = {{ transformed }} -{%- else %} -{{ destination }}: {{ type_string }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = {{ transformed }} -{%- endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, json.dumps( {{source}}.to_dict()).encode(), "application/json"))) {% endmacro %} 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..f4fd19f34 100644 --- a/openapi_python_client/templates/property_templates/union_property.py.jinja +++ b/openapi_python_client/templates/property_templates/union_property.py.jinja @@ -1,10 +1,10 @@ {% 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) %} + {% if "None" in property.get_type_strings_in_union(json=True) %} if data is None: return data {% endif %} - {% if "Unset" in property.get_type_strings_in_union(json=True, multipart=False) %} + {% if "Unset" in property.get_type_strings_in_union(json=True) %} if isinstance(data, Unset): return data {% endif %} @@ -21,7 +21,7 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri raise TypeError() {{ inner_template.construct(inner_property, "data") | indent(8) }} return {{ inner_property.python_name }} - except: # noqa: E722 + except (TypeError, ValueError, AttributeError, KeyError): 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 %} @@ -39,11 +39,11 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri {{ property.python_name }} = _parse_{{ property.python_name }}({{ source }}) {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set ns = namespace(contains_properties_without_transform = false, contains_modified_properties = not property.required, has_if = false) %} -{% if declare_type %}{{ destination }}: {{ property.get_type_string(json=True, multipart=False) }}{% endif %} +{% if declare_type %}{{ destination }}: {{ property.get_type_string(json=True) }}{% endif %} -{% if not property.required %} +{% if not property.required and not skip_unset %} if isinstance({{ source }}, Unset): {{ destination }} = UNSET {% set ns.has_if = true %} @@ -75,25 +75,28 @@ else: {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% set ns = namespace(has_if = false) %} -{{ destination }}: {{ property.get_type_string(json=False, multipart=True) }} - -{% if not property.required %} -if isinstance({{ source }}, Unset): - {{ destination }} = UNSET - {% set ns.has_if = true %} +{% macro instance_check(inner_property, source) %} +{% if inner_property.get_instance_type_string() == "None" %} +if {{ source }} is None: +{% else %} +if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}): {% endif %} +{% endmacro %} + +{% macro multipart(property, source, destination) %} +{% set ns = namespace(has_if = false) %} {% for inner_property in property.inner_properties %} {% if not ns.has_if %} -if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}): +{{ instance_check(inner_property, source) }} {% set ns.has_if = true %} {% elif not loop.last %} -elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}): + +el{{ instance_check(inner_property, source) }} {% else %} + else: {% endif %} {% import "property_templates/" + inner_property.template as inner_template %} - {{ inner_template.transform_multipart(inner_property, source, destination) | indent(4) | trim }} -{% endfor %} + {{ inner_template.multipart(inner_property, source, destination) | indent(4) | trim }} +{%- endfor -%} {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/uuid_property.py.jinja b/openapi_python_client/templates/property_templates/uuid_property.py.jinja index 1f91c1e3a..828b12b7f 100644 --- a/openapi_python_client/templates/property_templates/uuid_property.py.jinja +++ b/openapi_python_client/templates/property_templates/uuid_property.py.jinja @@ -10,29 +10,24 @@ UUID({{ source }}) {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %} {% set transformed = "str(" + source + ")" %} {% if property.required %} {{ destination }} = {{ transformed }} {%- else %} -{% if declare_type %} -{% set type_annotation = property.get_type_string(json=True) %} +{% if not skip_unset %} + {% if declare_type %} + {% set type_annotation = property.get_type_string(json=True) %} {{ destination }}: {{ type_annotation }} = UNSET -{% else %} + {% else %} {{ destination }} = UNSET + {% endif %} {% endif %} if not isinstance({{ source }}, Unset): {{ destination }} = {{ transformed }} {%- endif %} {% endmacro %} -{% macro transform_multipart(property, source, destination) %} -{% if property.required %} -{{ destination }} = str({{ source }}) -{%- else %} -{% set type_annotation = property.get_type_string(json=True) | replace("str", "bytes") %} -{{ destination }}: {{ type_annotation }} = UNSET -if not isinstance({{ source }}, Unset): - {{ destination }} = str({{ source }}) -{%- endif %} +{% macro multipart(property, source, name) %} +files.append(({{ name }}, (None, str({{ source }}), "text/plain"))) {% endmacro %} diff --git a/openapi_python_client/templates/pyproject.toml.jinja b/openapi_python_client/templates/pyproject.toml.jinja index e9344b436..9f21f8043 100644 --- a/openapi_python_client/templates/pyproject.toml.jinja +++ b/openapi_python_client/templates/pyproject.toml.jinja @@ -1,49 +1,9 @@ -{% set poetry = meta == "poetry" %} -{% set pdm = meta == "pdm" %} -{% if poetry or pdm %} -{% if poetry %}[tool.poetry] -{% elif pdm %}[project] +{% if meta == "poetry" %} +{% include "pyproject_poetry.toml.jinja" %} +{% elif meta == "pdm" %} +{% include "pyproject_pdm.toml.jinja" %} +{% elif meta == "uv" %} +{% include "pyproject_uv.toml.jinja" %} {% endif %} -name = "{{ project_name }}" -version = "{{ package_version }}" -description = "{{ package_description }}" -authors = [] -readme = "README.md" -{% if pdm %}requires-python = ">=3.9,<4.0"{% endif %} -{% if poetry %} -packages = [ - {include = "{{ package_name }}"}, -] -include = ["CHANGELOG.md", "{{ package_name }}/py.typed"] -{% endif %} - -{% if pdm %} -dependencies = [ - "httpx>=0.20.0,<0.29.0", - "attrs>=22.2.0", - "python-dateutil>=2.8.0", -] - -[tool.pdm] -distribution = true -{% endif %} -{% if poetry %} - -[tool.poetry.dependencies] -python = "^3.9" -httpx = ">=0.20.0,<0.29.0" -attrs = ">=22.2.0" -python-dateutil = "^2.8.0" -{% endif %} - -[build-system] -{% if poetry %} -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" -{% elif pdm %} -requires = ["pdm-backend"] -build-backend = "pdm.backend" -{% endif %} -{% endif %}{# poetry or pdm #} {% include "pyproject_ruff.toml.jinja" %} diff --git a/openapi_python_client/templates/pyproject_pdm.toml.jinja b/openapi_python_client/templates/pyproject_pdm.toml.jinja new file mode 100644 index 000000000..1ce8b3ac5 --- /dev/null +++ b/openapi_python_client/templates/pyproject_pdm.toml.jinja @@ -0,0 +1,18 @@ +[project] +name = "{{ project_name }}" +version = "{{ package_version }}" +description = "{{ package_description }}" +authors = [] +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "httpx>=0.23.1,<0.29.0", + "attrs>=22.2.0", +] + +[tool.pdm] +distribution = true + +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" diff --git a/openapi_python_client/templates/pyproject_poetry.toml.jinja b/openapi_python_client/templates/pyproject_poetry.toml.jinja new file mode 100644 index 000000000..d06578b17 --- /dev/null +++ b/openapi_python_client/templates/pyproject_poetry.toml.jinja @@ -0,0 +1,19 @@ +[tool.poetry] +name = "{{ project_name }}" +version = "{{ package_version }}" +description = "{{ package_description }}" +authors = [] +readme = "README.md" +packages = [ + { include = "{{ package_name }}" }, +] +include = ["{{ package_name }}/py.typed"] + +[tool.poetry.dependencies] +python = "^3.11" +httpx = ">=0.23.1,<0.29.0" +attrs = ">=22.2.0" + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/openapi_python_client/templates/pyproject_uv.toml.jinja b/openapi_python_client/templates/pyproject_uv.toml.jinja new file mode 100644 index 000000000..dc5861af3 --- /dev/null +++ b/openapi_python_client/templates/pyproject_uv.toml.jinja @@ -0,0 +1,19 @@ +[project] +name = "{{ project_name }}" +version = "{{ package_version }}" +description = "{{ package_description }}" +authors = [] +requires-python = ">=3.11" +readme = "README.md" +dependencies = [ + "httpx>=0.23.1,<0.29.0", + "attrs>=22.2.0", +] + +[tool.uv.build-backend] +module-name = "{{ package_name }}" +module-root = "" + +[build-system] +requires = ["uv_build>=0.12.0,<0.13.0"] +build-backend = "uv_build" diff --git a/openapi_python_client/templates/setup.py.jinja b/openapi_python_client/templates/setup.py.jinja index c7c1a5a94..74a7d45a0 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.9, <4", - install_requires=["httpx >= 0.20.0, < 0.29.0", "attrs >= 22.2.0", "python-dateutil >= 2.8.0, < 3"], + python_requires=">=3.11, <4", + install_requires=["httpx >= 0.23.1, < 0.29.0", "attrs >= 22.2.0"], package_data={"{{ package_name }}": ["py.typed"]}, ) diff --git a/openapi_python_client/templates/str_enum.py.jinja b/openapi_python_client/templates/str_enum.py.jinja index e0da5ed0f..ba47032a5 100644 --- a/openapi_python_client/templates/str_enum.py.jinja +++ b/openapi_python_client/templates/str_enum.py.jinja @@ -1,6 +1,6 @@ -from enum import Enum +from enum import StrEnum -class {{ enum.class_info.name }}(str, Enum): +class {{ enum.class_info.name }}(StrEnum): {% for key, value in enum.values|dictsort(true) %} {{ key }} = "{{ value }}" {% endfor %} diff --git a/openapi_python_client/templates/types.py.jinja b/openapi_python_client/templates/types.py.jinja index 6e0d6206c..f74db0ad7 100644 --- a/openapi_python_client/templates/types.py.jinja +++ b/openapi_python_client/templates/types.py.jinja @@ -1,8 +1,8 @@ """ Contains some shared types for properties """ -from collections.abc import MutableMapping +from collections.abc import Mapping, MutableMapping from http import HTTPStatus -from typing import BinaryIO, Generic, Optional, TypeVar, Literal +from typing import BinaryIO, Generic, TypeVar, Literal, IO from attrs import define @@ -14,19 +14,25 @@ class Unset: UNSET: Unset = Unset() -{# Used as `FileProperty._json_type_string` #} -FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]] - +# The types that `httpx.Client(files=)` can accept, copied from that library. +FileContent = IO[bytes] | bytes | str +FileTypes = ( + # (filename, file (or bytes), content_type) + tuple[str | None, FileContent, str | None] + # (filename, file (or bytes), content_type, headers) + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) +RequestFiles = list[tuple[str, FileTypes]] @define class File: """ Contains information for file uploads """ payload: BinaryIO - file_name: Optional[str] = None - mime_type: Optional[str] = None + file_name: str | None = None + mime_type: str | None = None - def to_tuple(self) -> FileJsonType: + def to_tuple(self) -> FileTypes: """ Return a tuple representation that httpx will accept for multipart/form-data """ return self.file_name, self.payload, self.mime_type @@ -41,7 +47,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T | None -__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] +__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"] diff --git a/pdm.lock b/pdm.lock index 1f446befd..a3e3f3f17 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,10 +5,21 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:18a45e099de16a3f298e7c5bf873869d842e647755df674a9d2c53c4ce9b0d71" +content_hash = "sha256:d12cbaca943c462ff9d65a03ded89356166f0506764a1068b836f7737a04d25e" [[metadata.targets]] -requires_python = "~=3.9" +requires_python = "~=3.11" + +[[package]] +name = "annotated-doc" +version = "0.0.4" +requires_python = ">=3.8" +summary = "Document parameters, class attributes, return types, and variables inline, with Annotated." +groups = ["default"] +files = [ + {file = "annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320"}, + {file = "annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4"}, +] [[package]] name = "annotated-types" @@ -26,56 +37,82 @@ files = [ [[package]] name = "anyio" -version = "4.9.0" -requires_python = ">=3.9" -summary = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.14.0" +requires_python = ">=3.10" +summary = "High-level concurrency and networking framework on top of asyncio or Trio" groups = ["default"] dependencies = [ "exceptiongroup>=1.0.2; python_version < \"3.11\"", "idna>=2.8", - "sniffio>=1.1", "typing-extensions>=4.5; python_version < \"3.13\"", ] files = [ - {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, - {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, + {file = "anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9"}, + {file = "anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89"}, ] [[package]] -name = "attrs" -version = "25.3.0" -requires_python = ">=3.8" -summary = "Classes Without Boilerplate" -groups = ["default"] +name = "ast-serialize" +version = "0.5.0" +requires_python = ">=3.7" +summary = "Python bindings for mypy AST serialization" +groups = ["dev"] files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c"}, + {file = "ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb"}, + {file = "ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101"}, + {file = "ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43"}, + {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27"}, + {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d"}, + {file = "ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a"}, + {file = "ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590"}, + {file = "ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642"}, + {file = "ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6"}, ] [[package]] -name = "certifi" -version = "2025.1.31" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." +name = "attrs" +version = "26.1.0" +requires_python = ">=3.9" +summary = "Classes Without Boilerplate" groups = ["default"] files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, + {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"}, + {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"}, ] [[package]] -name = "click" -version = "8.1.8" +name = "certifi" +version = "2026.6.17" requires_python = ">=3.7" -summary = "Composable command line interface toolkit" +summary = "Python package for providing Mozilla's CA Bundle." groups = ["default"] -dependencies = [ - "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", -] files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, + {file = "certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db"}, + {file = "certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432"}, ] [[package]] @@ -92,192 +129,205 @@ files = [ [[package]] name = "coverage" -version = "7.7.0" -requires_python = ">=3.9" +version = "7.14.2" +requires_python = ">=3.10" summary = "Code coverage measurement for Python" groups = ["dev"] files = [ - {file = "coverage-7.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a538a23119d1e2e2ce077e902d02ea3d8e0641786ef6e0faf11ce82324743944"}, - {file = "coverage-7.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1586ad158523f4133499a4f322b230e2cfef9cc724820dbd58595a5a236186f4"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6c96d69928a3a6767fab8dc1ce8a02cf0156836ccb1e820c7f45a423570d98"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f18d47641282664276977c604b5a261e51fefc2980f5271d547d706b06a837f"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a1e18a85bd066c7c556d85277a7adf4651f259b2579113844835ba1a74aafd"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70f0925c4e2bfc965369f417e7cc72538fd1ba91639cf1e4ef4b1a6b50439b3b"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b0fac2088ec4aaeb5468b814bd3ff5e5978364bfbce5e567c44c9e2854469f6c"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3e212a894d8ae07fde2ca8b43d666a6d49bbbddb10da0f6a74ca7bd31f20054"}, - {file = "coverage-7.7.0-cp310-cp310-win32.whl", hash = "sha256:f32b165bf6dfea0846a9c9c38b7e1d68f313956d60a15cde5d1709fddcaf3bee"}, - {file = "coverage-7.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:a2454b12a3f12cc4698f3508912e6225ec63682e2ca5a96f80a2b93cef9e63f3"}, - {file = "coverage-7.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a0a207c87a9f743c8072d059b4711f8d13c456eb42dac778a7d2e5d4f3c253a7"}, - {file = "coverage-7.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d673e3add00048215c2cc507f1228a7523fd8bf34f279ac98334c9b07bd2656"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f81fe93dc1b8e5673f33443c0786c14b77e36f1025973b85e07c70353e46882b"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8c7524779003d59948c51b4fcbf1ca4e27c26a7d75984f63488f3625c328b9b"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c124025430249118d018dcedc8b7426f39373527c845093132196f2a483b6dd"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7f559c36d5cdc448ee13e7e56ed7b6b5d44a40a511d584d388a0f5d940977ba"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:37cbc7b0d93dfd133e33c7ec01123fbb90401dce174c3b6661d8d36fb1e30608"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7d2a65876274acf544703e943c010b60bd79404e3623a1e5d52b64a6e2728de5"}, - {file = "coverage-7.7.0-cp311-cp311-win32.whl", hash = "sha256:f5a2f71d6a91238e7628f23538c26aa464d390cbdedf12ee2a7a0fb92a24482a"}, - {file = "coverage-7.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ae8006772c6b0fa53c33747913473e064985dac4d65f77fd2fdc6474e7cd54e4"}, - {file = "coverage-7.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:056d3017ed67e7ddf266e6f57378ece543755a4c9231e997789ab3bd11392c94"}, - {file = "coverage-7.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33c1394d8407e2771547583b66a85d07ed441ff8fae5a4adb4237ad39ece60db"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fbb7a0c3c21908520149d7751cf5b74eb9b38b54d62997b1e9b3ac19a8ee2fe"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb356e7ae7c2da13f404bf8f75be90f743c6df8d4607022e759f5d7d89fe83f8"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce730d484038e97f27ea2dbe5d392ec5c2261f28c319a3bb266f6b213650135"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa4dff57fc21a575672176d5ab0ef15a927199e775c5e8a3d75162ab2b0c7705"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b667b91f4f714b17af2a18e220015c941d1cf8b07c17f2160033dbe1e64149f0"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:693d921621a0c8043bfdc61f7d4df5ea6d22165fe8b807cac21eb80dd94e4bbd"}, - {file = "coverage-7.7.0-cp312-cp312-win32.whl", hash = "sha256:52fc89602cde411a4196c8c6894afb384f2125f34c031774f82a4f2608c59d7d"}, - {file = "coverage-7.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ce8cf59e09d31a4915ff4c3b94c6514af4c84b22c4cc8ad7c3c546a86150a92"}, - {file = "coverage-7.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4545485fef7a8a2d8f30e6f79ce719eb154aab7e44217eb444c1d38239af2072"}, - {file = "coverage-7.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1393e5aa9441dafb0162c36c8506c648b89aea9565b31f6bfa351e66c11bcd82"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:316f29cc3392fa3912493ee4c83afa4a0e2db04ff69600711f8c03997c39baaa"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1ffde1d6bc2a92f9c9207d1ad808550873748ac2d4d923c815b866baa343b3f"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:416e2a8845eaff288f97eaf76ab40367deafb9073ffc47bf2a583f26b05e5265"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5efdeff5f353ed3352c04e6b318ab05c6ce9249c25ed3c2090c6e9cadda1e3b2"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:57f3bd0d29bf2bd9325c0ff9cc532a175110c4bf8f412c05b2405fd35745266d"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ab7090f04b12dc6469882ce81244572779d3a4b67eea1c96fb9ecc8c607ef39"}, - {file = "coverage-7.7.0-cp313-cp313-win32.whl", hash = "sha256:180e3fc68ee4dc5af8b33b6ca4e3bb8aa1abe25eedcb958ba5cff7123071af68"}, - {file = "coverage-7.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:55143aa13c49491f5606f05b49ed88663446dce3a4d3c5d77baa4e36a16d3573"}, - {file = "coverage-7.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc41374d2f27d81d6558f8a24e5c114580ffefc197fd43eabd7058182f743322"}, - {file = "coverage-7.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:89078312f06237417adda7c021c33f80f7a6d2db8572a5f6c330d89b080061ce"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b2f144444879363ea8834cd7b6869d79ac796cb8f864b0cfdde50296cd95816"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60e6347d1ed882b1159ffea172cb8466ee46c665af4ca397edbf10ff53e9ffaf"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb203c0afffaf1a8f5b9659a013f8f16a1b2cad3a80a8733ceedc968c0cf4c57"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ad0edaa97cb983d9f2ff48cadddc3e1fb09f24aa558abeb4dc9a0dbacd12cbb4"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c5f8a5364fc37b2f172c26a038bc7ec4885f429de4a05fc10fdcb53fb5834c5c"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4e09534037933bf6eb31d804e72c52ec23219b32c1730f9152feabbd7499463"}, - {file = "coverage-7.7.0-cp313-cp313t-win32.whl", hash = "sha256:1b336d06af14f8da5b1f391e8dec03634daf54dfcb4d1c4fb6d04c09d83cef90"}, - {file = "coverage-7.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b54a1ee4c6f1905a436cbaa04b26626d27925a41cbc3a337e2d3ff7038187f07"}, - {file = "coverage-7.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c8fbce80b2b8bf135d105aa8f5b36eae0c57d702a1cc3ebdea2a6f03f6cdde5"}, - {file = "coverage-7.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d9710521f07f526de30ccdead67e6b236fe996d214e1a7fba8b36e2ba2cd8261"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7789e700f33f2b133adae582c9f437523cd5db8de845774988a58c360fc88253"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c36093aca722db73633cf2359026ed7782a239eb1c6db2abcff876012dc4cf"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c075d167a6ec99b798c1fdf6e391a1d5a2d054caffe9593ba0f97e3df2c04f0e"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d013c07061751ae81861cae6ec3a4fe04e84781b11fd4b6b4201590234b25c7b"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:104bf640f408f4e115b85110047c7f27377e1a8b7ba86f7db4fa47aa49dc9a8e"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:39abcacd1ed54e2c33c54bdc488b310e8ef6705833f7148b6eb9a547199d375d"}, - {file = "coverage-7.7.0-cp39-cp39-win32.whl", hash = "sha256:8e336b56301774ace6be0017ff85c3566c556d938359b61b840796a0202f805c"}, - {file = "coverage-7.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:8c938c6ae59be67ac19a7204e079efc94b38222cd7d0269f96e45e18cddeaa59"}, - {file = "coverage-7.7.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:3b0e6e54591ae0d7427def8a4d40fca99df6b899d10354bab73cd5609807261c"}, - {file = "coverage-7.7.0-py3-none-any.whl", hash = "sha256:708f0a1105ef2b11c79ed54ed31f17e6325ac936501fc373f24be3e6a578146a"}, - {file = "coverage-7.7.0.tar.gz", hash = "sha256:cd879d4646055a573775a1cec863d00c9ff8c55860f8b17f6d8eee9140c06166"}, + {file = "coverage-7.14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909f265c8c41f04c824bf741b2601fdcb56cab4bf56e018996b6494192ba0f58"}, + {file = "coverage-7.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c8102deaf911938233f760426e6a5e287388521de95111d5c8de26c8a1028924"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:851f49e7bd7d1cdaf328f3133942b252d5e3d3380690131f423cba8e435b87f5"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04cb445bed86aaf00aaa97d41a8b6e30f100f21e81c34caaec4efc684cb57768"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7471bc920d97c51c37ea8127f13b2adca43c3d78c53313b26a1f428e99d2c254"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:da5057e1bb257c967feee8ba67f3ebf379e801c7717f238b3d8c9caf00fc8f93"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33c0da852e8a40246cd8e20cf3b2fc17ca52a45e9b5f7983c93db26f5d24b87b"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f48a85bb437fab7782021c40bfee6b15146928b96960d008ace41b6901a0f21d"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f44e7579a769a21d5b5e3166916bfe30ee175aaffff750324cbb11be2dbec5ad"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:78853ca3c6ca2f012daa2b07dbabbb8db0f09d4dbe8ee828d294b3445d3f4cd8"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c9c2795ee3692097ff226ab806005d36bb9691fca9b35353542b57ea749cc830"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2f5cc48a845d755b6db236f8c29c2b54773eb4c7e4ee2ead43812d73718784b0"}, + {file = "coverage-7.14.2-cp311-cp311-win32.whl", hash = "sha256:9c61cb7eaabcfa609c5bc0f5ff5869d72a2f02f17994e5fba5f971de516f3c82"}, + {file = "coverage-7.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:e715909b0966d1774d8a26e14e2f4a3ae75909dca526901c6306286b2dcbfbdc"}, + {file = "coverage-7.14.2-cp311-cp311-win_arm64.whl", hash = "sha256:9193f7150937a4fd836b10eaa123e15d98e961d1fabac07e60adf2d4785f888a"}, + {file = "coverage-7.14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:37c94712e533ea06f0b1e4d934811c520b1914ce0e4da3916220717aa7a86bc6"}, + {file = "coverage-7.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c050bbc7bba94c77e4ed7438f4fda1babe98ab145691d80aa6f60df934a1468b"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a7af571767a2ee342a171c16fc1b1a07a0bf511606d381703fb7cf397fe49d46"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8b4910cce599cd2438f8da65f5ef199a70a1cdb6ab314926df78271ca5954240"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c33e9e4878972f430b0cc06de3bf2a28d054a9efb4f8426d27de0d9cb81396ff"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7967ea55c6dea6becba4d5870e2fa0aa4915a8be7ebff1bb79e6207aa75ce8d"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1322f237c2979b84096f4239c17828ff17fea6b3bbe96c44381c5f587c44c26"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:77849525340c99f516d793dddbcee16b18d50af892ac43c8de1a6f343d41e3b5"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef11695493ec3f06f7b2678ca274bcabb4ca04057317df268ddbfd8b05f661a8"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8134f0e0723e080d1c27bbe8fc149f0162e429fa1852482150015d0fce83eaf1"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:914eead2b843fc357f733b3fe39cc94f1b53d466e8cfe03080b1ed9d24ccfc73"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e4b2d5e847fb7958583b74910cc19e5ec4ece514487385677b26433b2546116e"}, + {file = "coverage-7.14.2-cp312-cp312-win32.whl", hash = "sha256:e753db9e40dda7302e0ac3e1e6e1325fb7f7b4694f87a7314ab15dd5d57911a7"}, + {file = "coverage-7.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:d32e5ca5f16dafb269ee50b60d32b00c704b3f6f78e238105f1d94a3a5f24bf5"}, + {file = "coverage-7.14.2-cp312-cp312-win_arm64.whl", hash = "sha256:dc366f158e2fb2add9d4e57338ca48f12611024278688ee657eb0b853fcb5de5"}, + {file = "coverage-7.14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e5f077641a6713ce9d38df9e85d4fb9e008677fc0775cbaeb32ddfc3b319d4ca"}, + {file = "coverage-7.14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0907f39b49ae818fe8af50aaa0f19afbc8ca164aea0865181ca7af17a3ac690b"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734d47669118d75c28981e562d4530ceb77342d31ffef6def5edd5ad4f05d7b"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1d9a1b5813d00ea6151f6ccf64d1fa16892771dfdda12ba87162d15ec4ea3e1e"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f0a80f4c8ac3f774210b1cc1bc0e31e75502f2818dda9a144ff90e702c4d91d"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e66f3f22d6c1515ce70f2e7c3e9c6f3ff0ff33480125c9f9c53e8f6508e30f"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6a2c37c3114f87ca7f10113756026eecb49656514debad600dcbec21f355ccea"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b16a7959d04b1497281c062c180413565c3f3469211d78799ad5b9a75f67796"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6466c6999545cf00c4c142dfcbbf2db396dc735f005dcf8f91d57e351a79472b"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c60915ebb8f562317ba5ff6b8c32e25c0882289b201a9f2fb2987f91efd95d8"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:33b830850488acbcd358c78a4fecfafe7031667b4da8ddff5546295dc962cdeb"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d0f845539230b8269aec902bc978b0cc403f52f002d18a04492efc943404d0bc"}, + {file = "coverage-7.14.2-cp313-cp313-win32.whl", hash = "sha256:a8ac51a2e441e9119b9395f4d893fbc4934c64c8ba58be9b9eaa85591249e548"}, + {file = "coverage-7.14.2-cp313-cp313-win_amd64.whl", hash = "sha256:039b264cdb31c44b48f9821e2afbf8f37df49e0fb837e24a942918b36c567e31"}, + {file = "coverage-7.14.2-cp313-cp313-win_arm64.whl", hash = "sha256:7f2ef591e381cc36b8e53334e1b842c760c520c8a52d01e8626209400e93fe6a"}, + {file = "coverage-7.14.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7a0d1f026b72d627fa5c8a57cbc86ad209b64aa2a65833c83b290ace5cbee126"}, + {file = "coverage-7.14.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4d2b86f81c1c9310a7e774e3cc9e927a3d0bf583ecbfa01498dd626930025428"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d76bdc1f9396ae70a55d050cf9743d88141c62ce0a22a3f627fab1d11c2f8bc6"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cda36d8e7bfd63b3e44e75163265429caa5d935b672b00f71bccc8c010518c64"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0904f3b79d7b845bef0715afe1900da634d12b97f05b9479cb472880ca07cb9c"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b6795ca4198d6cb7fc2c6163214f6555a6bc5f0ae1e268e76139dec4b37c4499"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c41e9b60fc0fa57f5d73306417d2f9d668202cca6944f9435878c55a5e7ae213"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:419d2aadd5746efc2e9df0f33c05570d8192e6f6a6098ab05acce586f44ce8a5"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1c5d273c5f1411c0d26c4f066c398d4a434b1f97bb5fa409189bedce86d4add4"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5fe465bc691264adce601527a972990c1174075d86bcbe9968fd20c95e0b1948"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6fbb61617af1c56f95d53170ae9fa6c9aef6de1abd02fcc50064bfc672efb18d"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e1eff22b831dfd5694989cc1f0789980f18391f614ac67c851af9a8e6d25e9ba"}, + {file = "coverage-7.14.2-cp314-cp314-win32.whl", hash = "sha256:58e91be0a233adef698d3e6be54f10401bb91fd7854c0d4c4d50e0d3711e72f1"}, + {file = "coverage-7.14.2-cp314-cp314-win_amd64.whl", hash = "sha256:d8429bf97906bfe6c61f9dbfb3342e0d88120da61939da8bd04f830cc3eab3b8"}, + {file = "coverage-7.14.2-cp314-cp314-win_arm64.whl", hash = "sha256:13609d9d77249447aa73357b14831b0f3b95f275026c9ff20dd105f981f53a0c"}, + {file = "coverage-7.14.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9818486c2bac88ae931df7e04905ee29bef49fd218c00f5f02bed4855254a101"}, + {file = "coverage-7.14.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:58055adffabfa243516a197aa9f85f0dd56d905b0fba1a10193269759c29ccb0"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:535747dbc200349d7fb434cffcb28e770f0290f69b225f56dc3803aa7210cdea"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:420c66e35d85c0ca5dc6a38147d83ef239762542900e5921ebbdb89333c540ea"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2cf17b33773be446a588551ea6a746b2d70dd0bc90dc31f1dd7648975a63c6b"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:adb4a5fef041f7179bb264203add873c147d169cf2f8d0adae89ff2e51271bac"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9c012ec357dec9408a83dad5541172a63c5cfa1421709f2e5811480d31ae1b28"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dacd0ecd08fda3cb2f85b60cabea7da326dcb2fc15fbb23a88830a80144cc9f2"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f27e980f2feba5dfe7a32b22b125470de69c0bd113c75e16165de909a777f512"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:105c00efb65c863630b2b63cbf7b8267e4da2d44b62284efbb19a03b04c337d4"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:571173fa04c8e8d6235ab32ae67fecca97777e2e1b4a1a30f3022c34e397c1c1"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e532f34d42d1a421fa00ed6b7735d14ac2e340256c1bad26a5e1dc1252b0bed7"}, + {file = "coverage-7.14.2-cp314-cp314t-win32.whl", hash = "sha256:243971550fb46c3039257f75e65610002d84304c505f609bbd9779e20a653a0a"}, + {file = "coverage-7.14.2-cp314-cp314t-win_amd64.whl", hash = "sha256:60fb0ca084a92da96474b8b405a7ea76dfecac3c68db54383e7934b6f3871169"}, + {file = "coverage-7.14.2-cp314-cp314t-win_arm64.whl", hash = "sha256:36a0a3f42ed7dfdbca2a69a541519ffd5064a5692152fc0018109e74370d7345"}, + {file = "coverage-7.14.2-py3-none-any.whl", hash = "sha256:04d92589e481a8b68a005a5a1e0646a91c76f322c397c4635298c57cf63699b5"}, + {file = "coverage-7.14.2.tar.gz", hash = "sha256:7a2da3d81cfe17c18038c6d98e6592aa9147d596d056119b0ee612c3c8bd5230"}, ] [[package]] name = "coverage" -version = "7.7.0" +version = "7.14.2" extras = ["toml"] -requires_python = ">=3.9" +requires_python = ">=3.10" summary = "Code coverage measurement for Python" groups = ["dev"] dependencies = [ - "coverage==7.7.0", + "coverage==7.14.2", "tomli; python_full_version <= \"3.11.0a6\"", ] files = [ - {file = "coverage-7.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a538a23119d1e2e2ce077e902d02ea3d8e0641786ef6e0faf11ce82324743944"}, - {file = "coverage-7.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1586ad158523f4133499a4f322b230e2cfef9cc724820dbd58595a5a236186f4"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6c96d69928a3a6767fab8dc1ce8a02cf0156836ccb1e820c7f45a423570d98"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f18d47641282664276977c604b5a261e51fefc2980f5271d547d706b06a837f"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a1e18a85bd066c7c556d85277a7adf4651f259b2579113844835ba1a74aafd"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70f0925c4e2bfc965369f417e7cc72538fd1ba91639cf1e4ef4b1a6b50439b3b"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b0fac2088ec4aaeb5468b814bd3ff5e5978364bfbce5e567c44c9e2854469f6c"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3e212a894d8ae07fde2ca8b43d666a6d49bbbddb10da0f6a74ca7bd31f20054"}, - {file = "coverage-7.7.0-cp310-cp310-win32.whl", hash = "sha256:f32b165bf6dfea0846a9c9c38b7e1d68f313956d60a15cde5d1709fddcaf3bee"}, - {file = "coverage-7.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:a2454b12a3f12cc4698f3508912e6225ec63682e2ca5a96f80a2b93cef9e63f3"}, - {file = "coverage-7.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a0a207c87a9f743c8072d059b4711f8d13c456eb42dac778a7d2e5d4f3c253a7"}, - {file = "coverage-7.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d673e3add00048215c2cc507f1228a7523fd8bf34f279ac98334c9b07bd2656"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f81fe93dc1b8e5673f33443c0786c14b77e36f1025973b85e07c70353e46882b"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8c7524779003d59948c51b4fcbf1ca4e27c26a7d75984f63488f3625c328b9b"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c124025430249118d018dcedc8b7426f39373527c845093132196f2a483b6dd"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7f559c36d5cdc448ee13e7e56ed7b6b5d44a40a511d584d388a0f5d940977ba"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:37cbc7b0d93dfd133e33c7ec01123fbb90401dce174c3b6661d8d36fb1e30608"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7d2a65876274acf544703e943c010b60bd79404e3623a1e5d52b64a6e2728de5"}, - {file = "coverage-7.7.0-cp311-cp311-win32.whl", hash = "sha256:f5a2f71d6a91238e7628f23538c26aa464d390cbdedf12ee2a7a0fb92a24482a"}, - {file = "coverage-7.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ae8006772c6b0fa53c33747913473e064985dac4d65f77fd2fdc6474e7cd54e4"}, - {file = "coverage-7.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:056d3017ed67e7ddf266e6f57378ece543755a4c9231e997789ab3bd11392c94"}, - {file = "coverage-7.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33c1394d8407e2771547583b66a85d07ed441ff8fae5a4adb4237ad39ece60db"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fbb7a0c3c21908520149d7751cf5b74eb9b38b54d62997b1e9b3ac19a8ee2fe"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb356e7ae7c2da13f404bf8f75be90f743c6df8d4607022e759f5d7d89fe83f8"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce730d484038e97f27ea2dbe5d392ec5c2261f28c319a3bb266f6b213650135"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa4dff57fc21a575672176d5ab0ef15a927199e775c5e8a3d75162ab2b0c7705"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b667b91f4f714b17af2a18e220015c941d1cf8b07c17f2160033dbe1e64149f0"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:693d921621a0c8043bfdc61f7d4df5ea6d22165fe8b807cac21eb80dd94e4bbd"}, - {file = "coverage-7.7.0-cp312-cp312-win32.whl", hash = "sha256:52fc89602cde411a4196c8c6894afb384f2125f34c031774f82a4f2608c59d7d"}, - {file = "coverage-7.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ce8cf59e09d31a4915ff4c3b94c6514af4c84b22c4cc8ad7c3c546a86150a92"}, - {file = "coverage-7.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4545485fef7a8a2d8f30e6f79ce719eb154aab7e44217eb444c1d38239af2072"}, - {file = "coverage-7.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1393e5aa9441dafb0162c36c8506c648b89aea9565b31f6bfa351e66c11bcd82"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:316f29cc3392fa3912493ee4c83afa4a0e2db04ff69600711f8c03997c39baaa"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1ffde1d6bc2a92f9c9207d1ad808550873748ac2d4d923c815b866baa343b3f"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:416e2a8845eaff288f97eaf76ab40367deafb9073ffc47bf2a583f26b05e5265"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5efdeff5f353ed3352c04e6b318ab05c6ce9249c25ed3c2090c6e9cadda1e3b2"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:57f3bd0d29bf2bd9325c0ff9cc532a175110c4bf8f412c05b2405fd35745266d"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ab7090f04b12dc6469882ce81244572779d3a4b67eea1c96fb9ecc8c607ef39"}, - {file = "coverage-7.7.0-cp313-cp313-win32.whl", hash = "sha256:180e3fc68ee4dc5af8b33b6ca4e3bb8aa1abe25eedcb958ba5cff7123071af68"}, - {file = "coverage-7.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:55143aa13c49491f5606f05b49ed88663446dce3a4d3c5d77baa4e36a16d3573"}, - {file = "coverage-7.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc41374d2f27d81d6558f8a24e5c114580ffefc197fd43eabd7058182f743322"}, - {file = "coverage-7.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:89078312f06237417adda7c021c33f80f7a6d2db8572a5f6c330d89b080061ce"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b2f144444879363ea8834cd7b6869d79ac796cb8f864b0cfdde50296cd95816"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60e6347d1ed882b1159ffea172cb8466ee46c665af4ca397edbf10ff53e9ffaf"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb203c0afffaf1a8f5b9659a013f8f16a1b2cad3a80a8733ceedc968c0cf4c57"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ad0edaa97cb983d9f2ff48cadddc3e1fb09f24aa558abeb4dc9a0dbacd12cbb4"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c5f8a5364fc37b2f172c26a038bc7ec4885f429de4a05fc10fdcb53fb5834c5c"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4e09534037933bf6eb31d804e72c52ec23219b32c1730f9152feabbd7499463"}, - {file = "coverage-7.7.0-cp313-cp313t-win32.whl", hash = "sha256:1b336d06af14f8da5b1f391e8dec03634daf54dfcb4d1c4fb6d04c09d83cef90"}, - {file = "coverage-7.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b54a1ee4c6f1905a436cbaa04b26626d27925a41cbc3a337e2d3ff7038187f07"}, - {file = "coverage-7.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c8fbce80b2b8bf135d105aa8f5b36eae0c57d702a1cc3ebdea2a6f03f6cdde5"}, - {file = "coverage-7.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d9710521f07f526de30ccdead67e6b236fe996d214e1a7fba8b36e2ba2cd8261"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7789e700f33f2b133adae582c9f437523cd5db8de845774988a58c360fc88253"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c36093aca722db73633cf2359026ed7782a239eb1c6db2abcff876012dc4cf"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c075d167a6ec99b798c1fdf6e391a1d5a2d054caffe9593ba0f97e3df2c04f0e"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d013c07061751ae81861cae6ec3a4fe04e84781b11fd4b6b4201590234b25c7b"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:104bf640f408f4e115b85110047c7f27377e1a8b7ba86f7db4fa47aa49dc9a8e"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:39abcacd1ed54e2c33c54bdc488b310e8ef6705833f7148b6eb9a547199d375d"}, - {file = "coverage-7.7.0-cp39-cp39-win32.whl", hash = "sha256:8e336b56301774ace6be0017ff85c3566c556d938359b61b840796a0202f805c"}, - {file = "coverage-7.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:8c938c6ae59be67ac19a7204e079efc94b38222cd7d0269f96e45e18cddeaa59"}, - {file = "coverage-7.7.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:3b0e6e54591ae0d7427def8a4d40fca99df6b899d10354bab73cd5609807261c"}, - {file = "coverage-7.7.0-py3-none-any.whl", hash = "sha256:708f0a1105ef2b11c79ed54ed31f17e6325ac936501fc373f24be3e6a578146a"}, - {file = "coverage-7.7.0.tar.gz", hash = "sha256:cd879d4646055a573775a1cec863d00c9ff8c55860f8b17f6d8eee9140c06166"}, -] - -[[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"}, + {file = "coverage-7.14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909f265c8c41f04c824bf741b2601fdcb56cab4bf56e018996b6494192ba0f58"}, + {file = "coverage-7.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c8102deaf911938233f760426e6a5e287388521de95111d5c8de26c8a1028924"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:851f49e7bd7d1cdaf328f3133942b252d5e3d3380690131f423cba8e435b87f5"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04cb445bed86aaf00aaa97d41a8b6e30f100f21e81c34caaec4efc684cb57768"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7471bc920d97c51c37ea8127f13b2adca43c3d78c53313b26a1f428e99d2c254"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:da5057e1bb257c967feee8ba67f3ebf379e801c7717f238b3d8c9caf00fc8f93"}, + {file = "coverage-7.14.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33c0da852e8a40246cd8e20cf3b2fc17ca52a45e9b5f7983c93db26f5d24b87b"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f48a85bb437fab7782021c40bfee6b15146928b96960d008ace41b6901a0f21d"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f44e7579a769a21d5b5e3166916bfe30ee175aaffff750324cbb11be2dbec5ad"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:78853ca3c6ca2f012daa2b07dbabbb8db0f09d4dbe8ee828d294b3445d3f4cd8"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c9c2795ee3692097ff226ab806005d36bb9691fca9b35353542b57ea749cc830"}, + {file = "coverage-7.14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2f5cc48a845d755b6db236f8c29c2b54773eb4c7e4ee2ead43812d73718784b0"}, + {file = "coverage-7.14.2-cp311-cp311-win32.whl", hash = "sha256:9c61cb7eaabcfa609c5bc0f5ff5869d72a2f02f17994e5fba5f971de516f3c82"}, + {file = "coverage-7.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:e715909b0966d1774d8a26e14e2f4a3ae75909dca526901c6306286b2dcbfbdc"}, + {file = "coverage-7.14.2-cp311-cp311-win_arm64.whl", hash = "sha256:9193f7150937a4fd836b10eaa123e15d98e961d1fabac07e60adf2d4785f888a"}, + {file = "coverage-7.14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:37c94712e533ea06f0b1e4d934811c520b1914ce0e4da3916220717aa7a86bc6"}, + {file = "coverage-7.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c050bbc7bba94c77e4ed7438f4fda1babe98ab145691d80aa6f60df934a1468b"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a7af571767a2ee342a171c16fc1b1a07a0bf511606d381703fb7cf397fe49d46"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8b4910cce599cd2438f8da65f5ef199a70a1cdb6ab314926df78271ca5954240"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c33e9e4878972f430b0cc06de3bf2a28d054a9efb4f8426d27de0d9cb81396ff"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7967ea55c6dea6becba4d5870e2fa0aa4915a8be7ebff1bb79e6207aa75ce8d"}, + {file = "coverage-7.14.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1322f237c2979b84096f4239c17828ff17fea6b3bbe96c44381c5f587c44c26"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:77849525340c99f516d793dddbcee16b18d50af892ac43c8de1a6f343d41e3b5"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef11695493ec3f06f7b2678ca274bcabb4ca04057317df268ddbfd8b05f661a8"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8134f0e0723e080d1c27bbe8fc149f0162e429fa1852482150015d0fce83eaf1"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:914eead2b843fc357f733b3fe39cc94f1b53d466e8cfe03080b1ed9d24ccfc73"}, + {file = "coverage-7.14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e4b2d5e847fb7958583b74910cc19e5ec4ece514487385677b26433b2546116e"}, + {file = "coverage-7.14.2-cp312-cp312-win32.whl", hash = "sha256:e753db9e40dda7302e0ac3e1e6e1325fb7f7b4694f87a7314ab15dd5d57911a7"}, + {file = "coverage-7.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:d32e5ca5f16dafb269ee50b60d32b00c704b3f6f78e238105f1d94a3a5f24bf5"}, + {file = "coverage-7.14.2-cp312-cp312-win_arm64.whl", hash = "sha256:dc366f158e2fb2add9d4e57338ca48f12611024278688ee657eb0b853fcb5de5"}, + {file = "coverage-7.14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e5f077641a6713ce9d38df9e85d4fb9e008677fc0775cbaeb32ddfc3b319d4ca"}, + {file = "coverage-7.14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0907f39b49ae818fe8af50aaa0f19afbc8ca164aea0865181ca7af17a3ac690b"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734d47669118d75c28981e562d4530ceb77342d31ffef6def5edd5ad4f05d7b"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1d9a1b5813d00ea6151f6ccf64d1fa16892771dfdda12ba87162d15ec4ea3e1e"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f0a80f4c8ac3f774210b1cc1bc0e31e75502f2818dda9a144ff90e702c4d91d"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e66f3f22d6c1515ce70f2e7c3e9c6f3ff0ff33480125c9f9c53e8f6508e30f"}, + {file = "coverage-7.14.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6a2c37c3114f87ca7f10113756026eecb49656514debad600dcbec21f355ccea"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b16a7959d04b1497281c062c180413565c3f3469211d78799ad5b9a75f67796"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6466c6999545cf00c4c142dfcbbf2db396dc735f005dcf8f91d57e351a79472b"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c60915ebb8f562317ba5ff6b8c32e25c0882289b201a9f2fb2987f91efd95d8"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:33b830850488acbcd358c78a4fecfafe7031667b4da8ddff5546295dc962cdeb"}, + {file = "coverage-7.14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d0f845539230b8269aec902bc978b0cc403f52f002d18a04492efc943404d0bc"}, + {file = "coverage-7.14.2-cp313-cp313-win32.whl", hash = "sha256:a8ac51a2e441e9119b9395f4d893fbc4934c64c8ba58be9b9eaa85591249e548"}, + {file = "coverage-7.14.2-cp313-cp313-win_amd64.whl", hash = "sha256:039b264cdb31c44b48f9821e2afbf8f37df49e0fb837e24a942918b36c567e31"}, + {file = "coverage-7.14.2-cp313-cp313-win_arm64.whl", hash = "sha256:7f2ef591e381cc36b8e53334e1b842c760c520c8a52d01e8626209400e93fe6a"}, + {file = "coverage-7.14.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7a0d1f026b72d627fa5c8a57cbc86ad209b64aa2a65833c83b290ace5cbee126"}, + {file = "coverage-7.14.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4d2b86f81c1c9310a7e774e3cc9e927a3d0bf583ecbfa01498dd626930025428"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d76bdc1f9396ae70a55d050cf9743d88141c62ce0a22a3f627fab1d11c2f8bc6"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cda36d8e7bfd63b3e44e75163265429caa5d935b672b00f71bccc8c010518c64"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0904f3b79d7b845bef0715afe1900da634d12b97f05b9479cb472880ca07cb9c"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b6795ca4198d6cb7fc2c6163214f6555a6bc5f0ae1e268e76139dec4b37c4499"}, + {file = "coverage-7.14.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c41e9b60fc0fa57f5d73306417d2f9d668202cca6944f9435878c55a5e7ae213"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:419d2aadd5746efc2e9df0f33c05570d8192e6f6a6098ab05acce586f44ce8a5"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1c5d273c5f1411c0d26c4f066c398d4a434b1f97bb5fa409189bedce86d4add4"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5fe465bc691264adce601527a972990c1174075d86bcbe9968fd20c95e0b1948"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6fbb61617af1c56f95d53170ae9fa6c9aef6de1abd02fcc50064bfc672efb18d"}, + {file = "coverage-7.14.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e1eff22b831dfd5694989cc1f0789980f18391f614ac67c851af9a8e6d25e9ba"}, + {file = "coverage-7.14.2-cp314-cp314-win32.whl", hash = "sha256:58e91be0a233adef698d3e6be54f10401bb91fd7854c0d4c4d50e0d3711e72f1"}, + {file = "coverage-7.14.2-cp314-cp314-win_amd64.whl", hash = "sha256:d8429bf97906bfe6c61f9dbfb3342e0d88120da61939da8bd04f830cc3eab3b8"}, + {file = "coverage-7.14.2-cp314-cp314-win_arm64.whl", hash = "sha256:13609d9d77249447aa73357b14831b0f3b95f275026c9ff20dd105f981f53a0c"}, + {file = "coverage-7.14.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9818486c2bac88ae931df7e04905ee29bef49fd218c00f5f02bed4855254a101"}, + {file = "coverage-7.14.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:58055adffabfa243516a197aa9f85f0dd56d905b0fba1a10193269759c29ccb0"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:535747dbc200349d7fb434cffcb28e770f0290f69b225f56dc3803aa7210cdea"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:420c66e35d85c0ca5dc6a38147d83ef239762542900e5921ebbdb89333c540ea"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2cf17b33773be446a588551ea6a746b2d70dd0bc90dc31f1dd7648975a63c6b"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:adb4a5fef041f7179bb264203add873c147d169cf2f8d0adae89ff2e51271bac"}, + {file = "coverage-7.14.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9c012ec357dec9408a83dad5541172a63c5cfa1421709f2e5811480d31ae1b28"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dacd0ecd08fda3cb2f85b60cabea7da326dcb2fc15fbb23a88830a80144cc9f2"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f27e980f2feba5dfe7a32b22b125470de69c0bd113c75e16165de909a777f512"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:105c00efb65c863630b2b63cbf7b8267e4da2d44b62284efbb19a03b04c337d4"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:571173fa04c8e8d6235ab32ae67fecca97777e2e1b4a1a30f3022c34e397c1c1"}, + {file = "coverage-7.14.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e532f34d42d1a421fa00ed6b7735d14ac2e340256c1bad26a5e1dc1252b0bed7"}, + {file = "coverage-7.14.2-cp314-cp314t-win32.whl", hash = "sha256:243971550fb46c3039257f75e65610002d84304c505f609bbd9779e20a653a0a"}, + {file = "coverage-7.14.2-cp314-cp314t-win_amd64.whl", hash = "sha256:60fb0ca084a92da96474b8b405a7ea76dfecac3c68db54383e7934b6f3871169"}, + {file = "coverage-7.14.2-cp314-cp314t-win_arm64.whl", hash = "sha256:36a0a3f42ed7dfdbca2a69a541519ffd5064a5692152fc0018109e74370d7345"}, + {file = "coverage-7.14.2-py3-none-any.whl", hash = "sha256:04d92589e481a8b68a005a5a1e0646a91c76f322c397c4635298c57cf63699b5"}, + {file = "coverage-7.14.2.tar.gz", hash = "sha256:7a2da3d81cfe17c18038c6d98e6592aa9147d596d056119b0ee612c3c8bd5230"}, ] [[package]] name = "h11" -version = "0.14.0" -requires_python = ">=3.7" +version = "0.16.0" +requires_python = ">=3.8" 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"}, + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] [[package]] name = "httpcore" -version = "1.0.7" +version = "1.0.9" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." groups = ["default"] dependencies = [ "certifi", - "h11<0.15,>=0.13", + "h11>=0.16", ] files = [ - {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, - {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, ] [[package]] @@ -299,24 +349,24 @@ files = [ [[package]] name = "idna" -version = "3.10" -requires_python = ">=3.6" +version = "3.18" +requires_python = ">=3.9" 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"}, + {file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"}, + {file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"}, ] [[package]] name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" +version = "2.3.0" +requires_python = ">=3.10" 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"}, + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] [[package]] @@ -333,88 +383,170 @@ files = [ {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] +[[package]] +name = "librt" +version = "0.11.0" +requires_python = ">=3.9" +summary = "Mypyc runtime library" +groups = ["dev"] +marker = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29"}, + {file = "librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89"}, + {file = "librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412"}, + {file = "librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d"}, + {file = "librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73"}, + {file = "librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c"}, + {file = "librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46"}, + {file = "librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a"}, + {file = "librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8"}, + {file = "librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a"}, + {file = "librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9"}, + {file = "librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c"}, + {file = "librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894"}, + {file = "librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2"}, + {file = "librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e"}, + {file = "librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e"}, + {file = "librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47"}, + {file = "librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44"}, + {file = "librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd"}, + {file = "librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175"}, + {file = "librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe"}, + {file = "librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f"}, + {file = "librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7"}, + {file = "librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1"}, + {file = "librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72"}, + {file = "librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd"}, + {file = "librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8"}, + {file = "librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c"}, + {file = "librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253"}, + {file = "librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1"}, +] + [[package]] name = "markdown-it-py" -version = "3.0.0" -requires_python = ">=3.8" +version = "4.2.0" +requires_python = ">=3.10" 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"}, + {file = "markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a"}, + {file = "markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49"}, ] [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" requires_python = ">=3.9" summary = "Safely add untrusted strings to HTML/XML markup." groups = ["default"] 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"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] @@ -430,393 +562,339 @@ files = [ [[package]] name = "mypy" -version = "1.15.0" -requires_python = ">=3.9" +version = "2.1.0" +requires_python = ">=3.10" summary = "Optional static typing for Python" groups = ["dev"] dependencies = [ + "ast-serialize<1.0.0,>=0.3.0", + "librt>=0.11.0; platform_python_implementation != \"PyPy\"", "mypy-extensions>=1.0.0", + "pathspec>=1.0.0", "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.6.0", -] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, + "typing-extensions>=4.14.0; python_version >= \"3.15\"", + "typing-extensions>=4.6.0; python_version < \"3.15\"", +] +files = [ + {file = "mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41"}, + {file = "mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca"}, + {file = "mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538"}, + {file = "mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398"}, + {file = "mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563"}, + {file = "mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389"}, + {file = "mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666"}, + {file = "mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af"}, + {file = "mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6"}, + {file = "mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211"}, + {file = "mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b"}, + {file = "mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22"}, + {file = "mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b"}, + {file = "mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8"}, + {file = "mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5"}, + {file = "mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e"}, + {file = "mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e"}, + {file = "mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285"}, + {file = "mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5"}, + {file = "mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65"}, + {file = "mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d"}, + {file = "mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2"}, + {file = "mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f"}, + {file = "mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4"}, + {file = "mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef"}, + {file = "mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135"}, + {file = "mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21"}, + {file = "mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57"}, + {file = "mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e"}, + {file = "mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780"}, + {file = "mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd"}, + {file = "mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08"}, + {file = "mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081"}, + {file = "mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7"}, + {file = "mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6"}, + {file = "mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289"}, + {file = "mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633"}, ] [[package]] name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" +version = "1.1.0" +requires_python = ">=3.8" 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"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] name = "packaging" -version = "24.2" +version = "26.2" requires_python = ">=3.8" summary = "Core utilities for Python packages" groups = ["dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +requires_python = ">=3.9" +summary = "Utility library for gitignore style pattern matching of file paths." +groups = ["dev"] +files = [ + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, ] [[package]] name = "pluggy" -version = "1.5.0" -requires_python = ">=3.8" +version = "1.6.0" +requires_python = ">=3.9" 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"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [[package]] name = "pydantic" -version = "2.10.6" -requires_python = ">=3.8" +version = "2.13.4" +requires_python = ">=3.9" summary = "Data validation using Python type hints" groups = ["default"] dependencies = [ "annotated-types>=0.6.0", - "pydantic-core==2.27.2", - "typing-extensions>=4.12.2", + "pydantic-core==2.46.4", + "typing-extensions>=4.14.1", + "typing-inspection>=0.4.2", ] files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, + {file = "pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba"}, + {file = "pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6"}, ] [[package]] name = "pydantic-core" -version = "2.27.2" -requires_python = ">=3.8" +version = "2.46.4" +requires_python = ">=3.9" summary = "Core functionality for Pydantic validation and serialization" groups = ["default"] dependencies = [ - "typing-extensions!=4.7.0,>=4.6.0", -] -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-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"}, + "typing-extensions>=4.14.1", +] +files = [ + {file = "pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594"}, + {file = "pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3"}, + {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848"}, + {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3"}, + {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109"}, + {file = "pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda"}, + {file = "pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33"}, + {file = "pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d"}, + {file = "pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2"}, + {file = "pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b"}, + {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458"}, + {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b"}, + {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c"}, + {file = "pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894"}, + {file = "pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89"}, + {file = "pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a"}, + {file = "pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008"}, + {file = "pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e"}, + {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd"}, + {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be"}, + {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d"}, + {file = "pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb"}, + {file = "pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292"}, + {file = "pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d"}, + {file = "pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb"}, + {file = "pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb"}, + {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898"}, + {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e"}, + {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519"}, + {file = "pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4"}, + {file = "pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac"}, + {file = "pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983"}, + {file = "pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1"}, ] [[package]] name = "pygments" -version = "2.19.1" -requires_python = ">=3.8" +version = "2.20.0" +requires_python = ">=3.9" summary = "Pygments is a syntax highlighting package written in Python." -groups = ["default"] +groups = ["default", "dev"] files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, ] [[package]] name = "pytest" -version = "8.3.5" -requires_python = ">=3.8" +version = "9.1.1" +requires_python = ">=3.10" summary = "pytest: simple powerful testing with Python" groups = ["dev"] dependencies = [ - "colorama; sys_platform == \"win32\"", - "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", - "iniconfig", - "packaging", + "colorama>=0.4; sys_platform == \"win32\"", + "exceptiongroup>=1; python_version < \"3.11\"", + "iniconfig>=1.0.1", + "packaging>=22", "pluggy<2,>=1.5", + "pygments>=2.7.2", "tomli>=1; python_version < \"3.11\"", ] files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, ] [[package]] name = "pytest-cov" -version = "6.0.0" +version = "7.1.0" requires_python = ">=3.9" summary = "Pytest plugin for measuring coverage." groups = ["dev"] dependencies = [ - "coverage[toml]>=7.5", - "pytest>=4.6", + "coverage[toml]>=7.10.6", + "pluggy>=1.2", + "pytest>=7", ] 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"}, + {file = "pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678"}, + {file = "pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2"}, ] [[package]] name = "pytest-mock" -version = "3.14.0" -requires_python = ">=3.8" +version = "3.15.1" +requires_python = ">=3.9" 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"}, + {file = "pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d"}, + {file = "pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f"}, ] [[package]] name = "python-multipart" -version = "0.0.20" -requires_python = ">=3.8" +version = "0.0.32" +requires_python = ">=3.10" summary = "A streaming multipart parser for Python" groups = ["dev"] 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"}, + {file = "python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23"}, + {file = "python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e"}, ] [[package]] name = "rich" -version = "13.9.4" -requires_python = ">=3.8.0" +version = "15.0.0" +requires_python = ">=3.9.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.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, - {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, + {file = "rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb"}, + {file = "rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36"}, ] [[package]] name = "ruamel-yaml" -version = "0.18.10" -requires_python = ">=3.7" +version = "0.19.1" +requires_python = ">=3.9" summary = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" groups = ["default"] -dependencies = [ - "ruamel-yaml-clib>=0.2.7; platform_python_implementation == \"CPython\" and python_version < \"3.13\"", -] 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]] -name = "ruamel-yaml-clib" -version = "0.2.12" -requires_python = ">=3.9" -summary = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -groups = ["default"] -marker = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" -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"}, + {file = "ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93"}, + {file = "ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993"}, ] [[package]] name = "ruff" -version = "0.11.0" +version = "0.15.18" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["default"] files = [ - {file = "ruff-0.11.0-py3-none-linux_armv6l.whl", hash = "sha256:dc67e32bc3b29557513eb7eeabb23efdb25753684b913bebb8a0c62495095acb"}, - {file = "ruff-0.11.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38c23fd9bdec4eb437b4c1e3595905a0a8edfccd63a790f818b28c78fe345639"}, - {file = "ruff-0.11.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7c8661b0be91a38bd56db593e9331beaf9064a79028adee2d5f392674bbc5e88"}, - {file = "ruff-0.11.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6c0e8d3d2db7e9f6efd884f44b8dc542d5b6b590fc4bb334fdbc624d93a29a2"}, - {file = "ruff-0.11.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c3156d3f4b42e57247275a0a7e15a851c165a4fc89c5e8fa30ea6da4f7407b8"}, - {file = "ruff-0.11.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:490b1e147c1260545f6d041c4092483e3f6d8eba81dc2875eaebcf9140b53905"}, - {file = "ruff-0.11.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1bc09a7419e09662983b1312f6fa5dab829d6ab5d11f18c3760be7ca521c9329"}, - {file = "ruff-0.11.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcfa478daf61ac8002214eb2ca5f3e9365048506a9d52b11bea3ecea822bb844"}, - {file = "ruff-0.11.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fbb2aed66fe742a6a3a0075ed467a459b7cedc5ae01008340075909d819df1e"}, - {file = "ruff-0.11.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92c0c1ff014351c0b0cdfdb1e35fa83b780f1e065667167bb9502d47ca41e6db"}, - {file = "ruff-0.11.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e4fd5ff5de5f83e0458a138e8a869c7c5e907541aec32b707f57cf9a5e124445"}, - {file = "ruff-0.11.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:96bc89a5c5fd21a04939773f9e0e276308be0935de06845110f43fd5c2e4ead7"}, - {file = "ruff-0.11.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a9352b9d767889ec5df1483f94870564e8102d4d7e99da52ebf564b882cdc2c7"}, - {file = "ruff-0.11.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:049a191969a10897fe052ef9cc7491b3ef6de79acd7790af7d7897b7a9bfbcb6"}, - {file = "ruff-0.11.0-py3-none-win32.whl", hash = "sha256:3191e9116b6b5bbe187447656f0c8526f0d36b6fd89ad78ccaad6bdc2fad7df2"}, - {file = "ruff-0.11.0-py3-none-win_amd64.whl", hash = "sha256:c58bfa00e740ca0a6c43d41fb004cd22d165302f360aaa56f7126d544db31a21"}, - {file = "ruff-0.11.0-py3-none-win_arm64.whl", hash = "sha256:868364fc23f5aa122b00c6f794211e85f7e78f5dffdf7c590ab90b8c4e69b657"}, - {file = "ruff-0.11.0.tar.gz", hash = "sha256:e55c620690a4a7ee6f1cccb256ec2157dc597d109400ae75bbf944fc9d6462e2"}, + {file = "ruff-0.15.18-py3-none-linux_armv6l.whl", hash = "sha256:8b6850172348c8381b8b3084c5915a4393c2373b9b54cd5b5e1ea15812bc10df"}, + {file = "ruff-0.15.18-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3fccc153a85417dcd976883160cacce486997b0a0058dd18f54b8aaaac7d1ce2"}, + {file = "ruff-0.15.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08d4c86a68f2c3ec2c9d56380a71fb4a4f65373055cbb8caabd645e9102f38d4"}, + {file = "ruff-0.15.18-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37e5108745c2c0705da916d7d4de533ddf547051ef45f62888c31bae73f66318"}, + {file = "ruff-0.15.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56949a6ce8b3abde54c0bcb22cebfe57e8771cadc84b407ae8b8eaf67ebdcd43"}, + {file = "ruff-0.15.18-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01a754cd6a1b630d3f97e33eb452cf7a98040482318e870f8bc52a5a30e62657"}, + {file = "ruff-0.15.18-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ba7a07e03a44dbf10bb086ee06705b173625014ec99f73a7e6836a5e5590a0c"}, + {file = "ruff-0.15.18-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a2c40a41a4cadbcf5897b548ab29dfe248b20c540961c0247d98a3973c70403"}, + {file = "ruff-0.15.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f0480ce690cbb6c4db6e5d08f19fce98e10ba131a8b60c1bcdac42771e3ae2d"}, + {file = "ruff-0.15.18-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2330215f1f393fa8733f55edce04fcf94c36a2c460fcde31f78cc84e4951e9b1"}, + {file = "ruff-0.15.18-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6aa6a3d979e48ae617578183674bf264fbe7d0114a796a26bd678d67963c7ff"}, + {file = "ruff-0.15.18-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a81beadbbff2c9c245561ae3f77b16709d87f35eec650d0501679239d3449b22"}, + {file = "ruff-0.15.18-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2186d9e940ae332ab293623a75b5f4fe49565f449954d50a72a046683aa6b809"}, + {file = "ruff-0.15.18-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5c2abf140438032bc77b2284a6c9944ecd8a19e5f1c7b52b1b8e4a0a80d19a7a"}, + {file = "ruff-0.15.18-py3-none-win32.whl", hash = "sha256:02299e6e9fa5b297a3f6d5d10d7bcd655c925b028bb8b9d4588214549c6b9ec4"}, + {file = "ruff-0.15.18-py3-none-win_amd64.whl", hash = "sha256:dac80dc8d26b2257dbefabed62f5d255c3937b4ccb122da1fc634794fa3578b3"}, + {file = "ruff-0.15.18-py3-none-win_arm64.whl", hash = "sha256:b2c9257fcbd4a3e5b977a1904e6facca016bafe2edc17df24db67cfaee03b4e4"}, + {file = "ruff-0.15.18.tar.gz", hash = "sha256:2698a964c70e8bf402dcb99c8810472d270d141e7aa8c4e13599fd52033a2f33"}, ] [[package]] @@ -830,99 +908,35 @@ files = [ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] -[[package]] -name = "six" -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.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" -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.9.0" -requires_python = ">=3.8.1" +version = "5.3.2" +requires_python = ">=3.10" summary = "Pytest Snapshot Test Utility" groups = ["dev"] dependencies = [ - "pytest<9.0.0,>=7.0.0", + "pytest>=8.0.0", ] files = [ - {file = "syrupy-4.9.0-py3-none-any.whl", hash = "sha256:3028d60188df9b39079678501be7d72fe64d32e2bb53d78df87f5a84bde94d76"}, - {file = "syrupy-4.9.0.tar.gz", hash = "sha256:7bd822492a6fa9f6f98832c2ca24f513b3e69a5690f4b2166bdb3b3941eef767"}, -] - -[[package]] -name = "tomli" -version = "2.2.1" -requires_python = ">=3.8" -summary = "A lil' TOML parser" -groups = ["dev"] -marker = "python_version < \"3.11\"" -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"}, + {file = "syrupy-5.3.2-py3-none-any.whl", hash = "sha256:a4cb08f07342655816c16b6b3aef5f6af8859ef5fbad6a57d0e2c20c0f8be54c"}, + {file = "syrupy-5.3.2.tar.gz", hash = "sha256:d4c9d4badb165a5694dcd869812717d899a8aa0b6f14f842437cb57c4ce09b3a"}, ] [[package]] name = "typer" -version = "0.15.2" -requires_python = ">=3.7" +version = "0.27.0" +requires_python = ">=3.10" summary = "Typer, build great CLIs. Easy to code. Based on Python type hints." groups = ["default"] dependencies = [ - "click>=8.0.0", - "rich>=10.11.0", + "annotated-doc>=0.0.2", + "colorama; platform_system == \"Windows\"", + "rich>=13.8.0", "shellingham>=1.3.0", - "typing-extensions>=3.7.4.3", ] files = [ - {file = "typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc"}, - {file = "typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5"}, + {file = "typer-0.27.0-py3-none-any.whl", hash = "sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1"}, + {file = "typer-0.27.0.tar.gz", hash = "sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5"}, ] [[package]] @@ -935,35 +949,38 @@ files = [ {file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"}, ] -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20241206" -requires_python = ">=3.8" -summary = "Typing stubs for python-dateutil" -groups = ["dev"] -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" -requires_python = ">=3.8" +version = "6.0.12.20260518" +requires_python = ">=3.10" summary = "Typing stubs for PyYAML" groups = ["dev"] 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"}, + {file = "types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash = "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd"}, + {file = "types_pyyaml-6.0.12.20260518.tar.gz", hash = "sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466"}, ] [[package]] name = "typing-extensions" -version = "4.12.2" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.15.0" +requires_python = ">=3.9" +summary = "Backported and Experimental Type Hints for Python 3.9+" 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"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +requires_python = ">=3.9" +summary = "Runtime typing introspection tools" +groups = ["default"] +dependencies = [ + "typing-extensions>=4.12.0", +] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, ] diff --git a/pdm.minimal.lock b/pdm.minimal.lock index 343630bbc..2c4cbc063 100644 --- a/pdm.minimal.lock +++ b/pdm.minimal.lock @@ -5,10 +5,10 @@ groups = ["default", "dev"] strategy = ["direct_minimal_versions", "inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:18a45e099de16a3f298e7c5bf873869d842e647755df674a9d2c53c4ce9b0d71" +content_hash = "sha256:5e4ec1b62724a3abfac73ca1f934560c05c04dd62f46592757671ed4fc3c1d3b" [[metadata.targets]] -requires_python = "~=3.9" +requires_python = "~=3.11" [[package]] name = "annotated-types" @@ -26,19 +26,18 @@ files = [ [[package]] name = "anyio" -version = "3.7.1" -requires_python = ">=3.7" -summary = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.13.0" +requires_python = ">=3.10" +summary = "High-level concurrency and networking framework on top of asyncio or Trio" groups = ["default"] dependencies = [ - "exceptiongroup; python_version < \"3.11\"", + "exceptiongroup>=1.0.2; python_version < \"3.11\"", "idna>=2.8", - "sniffio>=1.1", - "typing-extensions; python_version < \"3.8\"", + "typing-extensions>=4.5; python_version < \"3.13\"", ] files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, + {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, + {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, ] [[package]] @@ -54,104 +53,27 @@ files = [ [[package]] name = "certifi" -version = "2025.1.31" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." -groups = ["default"] -files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.1" +version = "2026.5.20" requires_python = ">=3.7" -summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +summary = "Python package for providing Mozilla's CA Bundle." groups = ["default"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897"}, + {file = "certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"}, ] [[package]] name = "click" -version = "8.1.8" -requires_python = ">=3.7" +version = "8.4.1" +requires_python = ">=3.10" summary = "Composable command line interface toolkit" groups = ["default"] dependencies = [ "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", ] files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, + {file = "click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2"}, + {file = "click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96"}, ] [[package]] @@ -168,165 +90,184 @@ files = [ [[package]] name = "coverage" -version = "7.6.12" -requires_python = ">=3.9" +version = "7.14.1" +requires_python = ">=3.10" summary = "Code coverage measurement for Python" groups = ["dev"] files = [ - {file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"}, - {file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"}, - {file = "coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe"}, - {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674"}, - {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb"}, - {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c"}, - {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c"}, - {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e"}, - {file = "coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425"}, - {file = "coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa"}, - {file = "coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015"}, - {file = "coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45"}, - {file = "coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702"}, - {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0"}, - {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f"}, - {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f"}, - {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d"}, - {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba"}, - {file = "coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f"}, - {file = "coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558"}, - {file = "coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad"}, - {file = "coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3"}, - {file = "coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574"}, - {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985"}, - {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750"}, - {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea"}, - {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3"}, - {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a"}, - {file = "coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95"}, - {file = "coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288"}, - {file = "coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1"}, - {file = "coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd"}, - {file = "coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9"}, - {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e"}, - {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4"}, - {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6"}, - {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3"}, - {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc"}, - {file = "coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3"}, - {file = "coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef"}, - {file = "coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e"}, - {file = "coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703"}, - {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0"}, - {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924"}, - {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b"}, - {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d"}, - {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827"}, - {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9"}, - {file = "coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3"}, - {file = "coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f"}, - {file = "coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d"}, - {file = "coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929"}, - {file = "coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87"}, - {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c"}, - {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2"}, - {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd"}, - {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"}, - {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86"}, - {file = "coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31"}, - {file = "coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57"}, - {file = "coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf"}, - {file = "coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953"}, - {file = "coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2"}, -] - -[[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"}, + {file = "coverage-7.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f"}, + {file = "coverage-7.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4"}, + {file = "coverage-7.14.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1"}, + {file = "coverage-7.14.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f"}, + {file = "coverage-7.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129"}, + {file = "coverage-7.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860"}, + {file = "coverage-7.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c"}, + {file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7"}, + {file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec"}, + {file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef"}, + {file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df"}, + {file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9"}, + {file = "coverage-7.14.1-cp311-cp311-win32.whl", hash = "sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548"}, + {file = "coverage-7.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e"}, + {file = "coverage-7.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3"}, + {file = "coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c"}, + {file = "coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c"}, + {file = "coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b"}, + {file = "coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6"}, + {file = "coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37"}, + {file = "coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad"}, + {file = "coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84"}, + {file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54"}, + {file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7"}, + {file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9"}, + {file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02"}, + {file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a"}, + {file = "coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1"}, + {file = "coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e"}, + {file = "coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a"}, + {file = "coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793"}, + {file = "coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d"}, + {file = "coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247"}, + {file = "coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d"}, + {file = "coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b"}, + {file = "coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be"}, + {file = "coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43"}, + {file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901"}, + {file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff"}, + {file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4"}, + {file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d"}, + {file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33"}, + {file = "coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c"}, + {file = "coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416"}, + {file = "coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42"}, + {file = "coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d"}, + {file = "coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5"}, + {file = "coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52"}, + {file = "coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a"}, + {file = "coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a"}, + {file = "coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2"}, + {file = "coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e"}, + {file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d"}, + {file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb"}, + {file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d"}, + {file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69"}, + {file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54"}, + {file = "coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1"}, + {file = "coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce"}, + {file = "coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1"}, + {file = "coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee"}, + {file = "coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500"}, + {file = "coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906"}, + {file = "coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42"}, + {file = "coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8"}, + {file = "coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851"}, + {file = "coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034"}, + {file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c"}, + {file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36"}, + {file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5"}, + {file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4"}, + {file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d"}, + {file = "coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee"}, + {file = "coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7"}, + {file = "coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343"}, + {file = "coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1"}, + {file = "coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b"}, + {file = "coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474"}, + {file = "coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86"}, + {file = "coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e"}, + {file = "coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65"}, + {file = "coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e"}, + {file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8"}, + {file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07"}, + {file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de"}, + {file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890"}, + {file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd"}, + {file = "coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e"}, + {file = "coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c"}, + {file = "coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af"}, + {file = "coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2"}, + {file = "coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be"}, ] [[package]] name = "execnet" -version = "2.1.1" +version = "2.1.2" requires_python = ">=3.8" summary = "execnet: rapid multi-Python deployment" groups = ["dev"] files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, ] [[package]] name = "h11" -version = "0.12.0" -requires_python = ">=3.6" +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.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, + {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 = "0.13.7" -requires_python = ">=3.6" +version = "0.16.3" +requires_python = ">=3.7" summary = "A minimal low-level HTTP client." groups = ["default"] dependencies = [ - "anyio==3.*", - "h11<0.13,>=0.11", + "anyio<5.0,>=3.0", + "certifi", + "h11<0.15,>=0.13", "sniffio==1.*", ] files = [ - {file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"}, - {file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"}, + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, ] [[package]] name = "httpx" -version = "0.20.0" -requires_python = ">=3.6" +version = "0.23.1" +requires_python = ">=3.7" summary = "The next generation HTTP client." groups = ["default"] dependencies = [ - "async-generator; python_version < \"3.7\"", "certifi", - "charset-normalizer", - "httpcore<0.14.0,>=0.13.3", + "httpcore<0.17.0,>=0.15.0", "rfc3986[idna2008]<2,>=1.3", "sniffio", ] files = [ - {file = "httpx-0.20.0-py3-none-any.whl", hash = "sha256:33af5aad9bdc82ef1fc89219c1e36f5693bf9cd0ebe330884df563445682c0f8"}, - {file = "httpx-0.20.0.tar.gz", hash = "sha256:09606d630f070d07f9ff28104fbcea429ea0014c1e89ac90b4d8de8286c40e7b"}, + {file = "httpx-0.23.1-py3-none-any.whl", hash = "sha256:0b9b1f0ee18b9978d637b0776bfd7f54e2ca278e063e3586d8f01cda89e042a8"}, + {file = "httpx-0.23.1.tar.gz", hash = "sha256:202ae15319be24efe9a8bd4ed4360e68fde7b38bcc2ce87088d416f026667d19"}, ] [[package]] name = "idna" -version = "3.10" -requires_python = ">=3.6" +version = "3.17" +requires_python = ">=3.9" 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"}, + {file = "idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c"}, + {file = "idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f"}, ] [[package]] name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" +version = "2.3.0" +requires_python = ">=3.10" 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"}, + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] [[package]] @@ -343,74 +284,105 @@ files = [ {file = "Jinja2-3.0.0.tar.gz", hash = "sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"}, ] +[[package]] +name = "markdown-it-py" +version = "4.2.0" +requires_python = ">=3.10" +summary = "Python port of markdown-it. Markdown parsing, done right!" +groups = ["default"] +dependencies = [ + "mdurl~=0.1", +] +files = [ + {file = "markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a"}, + {file = "markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49"}, +] + [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" requires_python = ">=3.9" summary = "Safely add untrusted strings to HTML/XML markup." groups = ["default"] 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"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[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]] @@ -425,11 +397,6 @@ dependencies = [ "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, - {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, - {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, - {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, - {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, @@ -445,46 +412,41 @@ files = [ {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, - {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, - {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, - {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, - {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, - {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, ] [[package]] name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" +version = "1.1.0" +requires_python = ">=3.8" 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"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] name = "packaging" -version = "24.2" +version = "26.2" requires_python = ">=3.8" summary = "Core utilities for Python packages" groups = ["dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, ] [[package]] name = "pluggy" -version = "1.5.0" -requires_python = ">=3.8" +version = "1.6.0" +requires_python = ">=3.9" 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"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [[package]] @@ -524,19 +486,6 @@ dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.27.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2ac6b919f7fed71b17fe0b4603c092a4c9b5bae414817c9c81d3c22d1e1bcc"}, - {file = "pydantic_core-2.27.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e015833384ca3e1a0565a79f5d953b0629d9138021c27ad37c92a9fa1af7623c"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db72e40628967f6dc572020d04b5f800d71264e0531c6da35097e73bdf38b003"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df45c4073bed486ea2f18757057953afed8dd77add7276ff01bccb79982cf46c"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:836a4bfe0cc6d36dc9a9cc1a7b391265bf6ce9d1eb1eac62ac5139f5d8d9a6fa"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bf1340ae507f6da6360b24179c2083857c8ca7644aab65807023cf35404ea8d"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ab325fc86fbc077284c8d7f996d904d30e97904a87d6fb303dce6b3de7ebba9"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1da0c98a85a6c6ed702d5556db3b09c91f9b0b78de37b7593e2de8d03238807a"}, - {file = "pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7b0202ebf2268954090209a84f9897345719e46a57c5f2c9b7b250ca0a9d3e63"}, - {file = "pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:35380671c3c921fe8adf31ad349dc6f7588b7e928dbe44e1093789734f607399"}, - {file = "pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b4c19525c3538fbc0bbda6229f9682fb8199ce9ac37395880e6952798e00373"}, - {file = "pydantic_core-2.27.0-cp310-none-win32.whl", hash = "sha256:333c840a1303d1474f491e7be0b718226c730a39ead0f7dab2c7e6a2f3855555"}, - {file = "pydantic_core-2.27.0-cp310-none-win_amd64.whl", hash = "sha256:99b2863c1365f43f74199c980a3d40f18a218fbe683dd64e470199db426c4d6a"}, {file = "pydantic_core-2.27.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4523c4009c3f39d948e01962223c9f5538602e7087a628479b723c939fab262d"}, {file = "pydantic_core-2.27.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84af1cf7bfdcbc6fcf5a5f70cc9896205e0350306e4dd73d54b6a18894f79386"}, {file = "pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e65466b31be1070b4a5b7dbfbd14b247884cb8e8b79c64fb0f36b472912dbaea"}, @@ -579,40 +528,20 @@ files = [ {file = "pydantic_core-2.27.0-cp313-none-win32.whl", hash = "sha256:678f66462058dd978702db17eb6a3633d634f7aa0deaea61e0a674152766d3fc"}, {file = "pydantic_core-2.27.0-cp313-none-win_amd64.whl", hash = "sha256:d28ca7066d6cdd347a50d8b725dc10d9a1d6a1cce09836cf071ea6a2d4908be0"}, {file = "pydantic_core-2.27.0-cp313-none-win_arm64.whl", hash = "sha256:6f4a53af9e81d757756508b57cae1cf28293f0f31b9fa2bfcb416cc7fb230f9d"}, - {file = "pydantic_core-2.27.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:4148dc9184ab79e356dc00a4199dc0ee8647973332cb385fc29a7cced49b9f9c"}, - {file = "pydantic_core-2.27.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5fc72fbfebbf42c0856a824b8b0dc2b5cd2e4a896050281a21cfa6fed8879cb1"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:185ef205256cd8b38431205698531026979db89a79587725c1e55c59101d64e9"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:395e3e1148fa7809016231f8065f30bb0dc285a97b4dc4360cd86e17bab58af7"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33d14369739c5d07e2e7102cdb0081a1fa46ed03215e07f097b34e020b83b1ae"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7820bb0d65e3ce1e3e70b6708c2f66143f55912fa02f4b618d0f08b61575f12"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43b61989068de9ce62296cde02beffabcadb65672207fc51e7af76dca75e6636"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15e350efb67b855cd014c218716feea4986a149ed1f42a539edd271ee074a196"}, - {file = "pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:433689845288f9a1ee5714444e65957be26d30915f7745091ede4a83cfb2d7bb"}, - {file = "pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:3fd8bc2690e7c39eecdf9071b6a889ce7b22b72073863940edc2a0a23750ca90"}, - {file = "pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:884f1806609c2c66564082540cffc96868c5571c7c3cf3a783f63f2fb49bd3cd"}, - {file = "pydantic_core-2.27.0-cp39-none-win32.whl", hash = "sha256:bf37b72834e7239cf84d4a0b2c050e7f9e48bced97bad9bdf98d26b8eb72e846"}, - {file = "pydantic_core-2.27.0-cp39-none-win_amd64.whl", hash = "sha256:31a2cae5f059329f9cfe3d8d266d3da1543b60b60130d186d9b6a3c20a346361"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4fb49cfdb53af5041aba909be00cccfb2c0d0a2e09281bf542371c5fd36ad04c"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:49633583eb7dc5cba61aaf7cdb2e9e662323ad394e543ee77af265736bcd3eaa"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:153017e3d6cd3ce979de06d84343ca424bb6092727375eba1968c8b4693c6ecb"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff63a92f6e249514ef35bc795de10745be0226eaea06eb48b4bbeaa0c8850a4a"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5982048129f40b082c2654de10c0f37c67a14f5ff9d37cf35be028ae982f26df"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:91bc66f878557313c2a6bcf396e7befcffe5ab4354cfe4427318968af31143c3"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:68ef5377eb582fa4343c9d0b57a5b094046d447b4c73dd9fbd9ffb216f829e7d"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c5726eec789ee38f2c53b10b1821457b82274f81f4f746bb1e666d8741fcfadb"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0c431e4be5c1a0c6654e0c31c661cd89e0ca956ef65305c3c3fd96f4e72ca39"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8e21d927469d04b39386255bf00d0feedead16f6253dcc85e9e10ddebc334084"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b51f964fcbb02949fc546022e56cdb16cda457af485e9a3e8b78ac2ecf5d77e"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a7fd4de38f7ff99a37e18fa0098c3140286451bc823d1746ba80cec5b433a1"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fda87808429c520a002a85d6e7cdadbf58231d60e96260976c5b8f9a12a8e13"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a150392102c402c538190730fda06f3bce654fc498865579a9f2c1d2b425833"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c9ed88b398ba7e3bad7bd64d66cc01dcde9cfcb7ec629a6fd78a82fa0b559d78"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:9fe94d9d2a2b4edd7a4b22adcd45814b1b59b03feb00e56deb2e89747aec7bfe"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d8b5ee4ae9170e2775d495b81f414cc20268041c42571530513496ba61e94ba3"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d29e235ce13c91902ef3efc3d883a677655b3908b1cbc73dee816e5e1f8f7739"}, {file = "pydantic_core-2.27.0.tar.gz", hash = "sha256:f57783fbaf648205ac50ae7d646f27582fc706be3977e87c3c124e7a92407b10"}, ] +[[package]] +name = "pygments" +version = "2.20.0" +requires_python = ">=3.9" +summary = "Pygments is a syntax highlighting package written in Python." +groups = ["default"] +files = [ + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, +] + [[package]] name = "pytest" version = "8.0.1" @@ -662,8 +591,8 @@ files = [ [[package]] name = "pytest-xdist" -version = "3.6.1" -requires_python = ">=3.8" +version = "3.8.0" +requires_python = ">=3.9" summary = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" groups = ["dev"] dependencies = [ @@ -671,22 +600,8 @@ dependencies = [ "pytest>=7.0.0", ] files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, -] - -[[package]] -name = "python-dateutil" -version = "2.8.1" -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.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, + {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, + {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, ] [[package]] @@ -723,6 +638,21 @@ files = [ {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, ] +[[package]] +name = "rich" +version = "15.0.0" +requires_python = ">=3.9.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", +] +files = [ + {file = "rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb"}, + {file = "rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36"}, +] + [[package]] name = "ruamel-yaml" version = "0.18.6" @@ -739,58 +669,53 @@ files = [ [[package]] name = "ruamel-yaml-clib" -version = "0.2.12" +version = "0.2.15" requires_python = ">=3.9" summary = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" groups = ["default"] marker = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" 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"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c583229f336682b7212a43d2fa32c30e643d3076178fb9f7a6a14dde85a2d8bd"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56ea19c157ed8c74b6be51b5fa1c3aff6e289a041575f0556f66e5fb848bb137"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5fea0932358e18293407feb921d4f4457db837b67ec1837f87074667449f9401"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71831bd61fbdb7aa0399d5c4da06bea37107ab5c79ff884cc07f2450910262"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:617d35dc765715fa86f8c3ccdae1e4229055832c452d4ec20856136acc75053f"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b45498cc81a4724a2d42273d6cfc243c0547ad7c6b87b4f774cb7bcc131c98d"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:def5663361f6771b18646620fca12968aae730132e104688766cf8a3b1d65922"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:014181cdec565c8745b7cbc4de3bf2cc8ced05183d986e6d1200168e5bb59490"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win32.whl", hash = "sha256:d290eda8f6ada19e1771b54e5706b8f9807e6bb08e873900d5ba114ced13e02c"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win_amd64.whl", hash = "sha256:bdc06ad71173b915167702f55d0f3f027fc61abd975bd308a0968c02db4a4c3e"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win32.whl", hash = "sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win32.whl", hash = "sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bfd309b316228acecfa30670c3887dcedf9b7a44ea39e2101e75d2654522acd4"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2812ff359ec1f30129b62372e5f22a52936fac13d5d21e70373dbca5d64bb97c"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7e74ea87307303ba91073b63e67f2c667e93f05a8c63079ee5b7a5c8d0d7b043"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:713cd68af9dfbe0bb588e144a61aad8dcc00ef92a82d2e87183ca662d242f524"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win32.whl", hash = "sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467"}, + {file = "ruamel_yaml_clib-0.2.15.tar.gz", hash = "sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600"}, ] [[package]] @@ -830,17 +755,6 @@ files = [ {file = "shellingham-1.3.2.tar.gz", hash = "sha256:576c1982bea0ba82fb46c36feb951319d7f42214a82634233f58b40d858a751e"}, ] -[[package]] -name = "six" -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.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" @@ -866,60 +780,21 @@ files = [ {file = "syrupy-4.6.1.tar.gz", hash = "sha256:37a835c9ce7857eeef86d62145885e10b3cb9615bc6abeb4ce404b3f18e1bb36"}, ] -[[package]] -name = "tomli" -version = "2.2.1" -requires_python = ">=3.8" -summary = "A lil' TOML parser" -groups = ["dev"] -marker = "python_version < \"3.11\"" -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.6.1" -requires_python = ">=3.6" +version = "0.16.1" +requires_python = ">=3.7" summary = "Typer, build great CLIs. Easy to code. Based on Python type hints." groups = ["default"] dependencies = [ - "click<9.0.0,>=7.1.1", + "click>=8.0.0", + "rich>=10.11.0", + "shellingham>=1.3.0", + "typing-extensions>=3.7.4.3", ] files = [ - {file = "typer-0.6.1-py3-none-any.whl", hash = "sha256:54b19e5df18654070a82f8c2aa1da456a4ac16a2a83e6dcd9f170e291c56338e"}, - {file = "typer-0.6.1.tar.gz", hash = "sha256:2d5720a5e63f73eaf31edaa15f6ab87f35f0690f8ca233017d7d23d743a91d73"}, + {file = "typer-0.16.1-py3-none-any.whl", hash = "sha256:90ee01cb02d9b8395ae21ee3368421faf21fa138cb2a541ed369c08cec5237c9"}, + {file = "typer-0.16.1.tar.gz", hash = "sha256:d358c65a464a7a90f338e3bb7ff0c74ac081449e53884b12ba658cbd72990614"}, ] [[package]] @@ -932,16 +807,6 @@ files = [ {file = "types_certifi-2020.4.0-py3-none-any.whl", hash = "sha256:0ffdbe451d3b02f6d2cfd87bcfb2f086a4ff1fa76a35d51cfc3771e261d7a8fd"}, ] -[[package]] -name = "types-python-dateutil" -version = "2.8.0" -summary = "Typing stubs for python-dateutil" -groups = ["dev"] -files = [ - {file = "types-python-dateutil-2.8.0.tar.gz", hash = "sha256:540c6c53c3a52433d7088254e3afdc3f6c86b5ae452aaa1b796c26d01c9fd73c"}, - {file = "types_python_dateutil-2.8.0-py3-none-any.whl", hash = "sha256:9954d87dc982344bb2aad73a7fe505bdca72f89088ef653c4c40f52649183437"}, -] - [[package]] name = "types-pyyaml" version = "6.0.3" @@ -954,11 +819,11 @@ files = [ [[package]] name = "typing-extensions" -version = "4.12.2" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.15.0" +requires_python = ">=3.9" +summary = "Backported and Experimental Type Hints for Python 3.9+" 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"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] diff --git a/pyproject.toml b/pyproject.toml index 450a8a429..0ab78d698 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,23 +2,21 @@ authors = [ { name = "Dylan Anthony", email = "contact@dylananthony.com" }, ] -license = { text = "MIT" } -requires-python = ">=3.9,<4.0" +license = "MIT" +requires-python = ">=3.11,<4.0" dependencies = [ "jinja2>=3.0.0,<4.0.0", - "typer>0.6,<0.16", + "typer>0.16,<0.28", "colorama>=0.4.3; sys_platform == \"win32\"", "shellingham>=1.3.2,<2.0.0", "pydantic>=2.10,<3.0.0", "attrs>=22.2.0", - "python-dateutil>=2.8.1,<3.0.0", - "httpx>=0.20.0,<0.29.0", - "ruamel.yaml>=0.18.6,<0.19.0", - "ruff>=0.2,<0.12", - "typing-extensions>=4.8.0,<5.0.0", + "httpx>=0.23.1,<0.29.0", + "ruamel.yaml>=0.18.6,<0.20.0", + "ruff>=0.2", ] name = "openapi-python-client" -version = "0.24.1" +version = "0.29.0" description = "Generate modern Python clients from OpenAPI" keywords = [ "OpenAPI", @@ -27,14 +25,12 @@ keywords = [ ] classifiers = [ "Development Status :: 4 - Beta", - "License :: OSI Approved :: MIT License", "Intended Audience :: Developers", "Programming Language :: Python :: 3", - "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", + "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Code Generators", "Typing :: Typed", ] @@ -94,7 +90,6 @@ dev = [ "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", "syrupy>=4", ] @@ -106,7 +101,7 @@ includes = [ ] [tool.pdm.scripts] -lint = "ruff check --fix ." +lint = "ruff check ." format = "ruff format ." mypy = "mypy openapi_python_client" check = { composite = ["lint", "format", "mypy", "test"] } @@ -129,7 +124,7 @@ composite = ["test --cov openapi_python_client tests --cov-report=term-missing"] [tool.pdm.scripts.regen_integration] shell = """ -openapi-python-client generate --overwrite --url https://raw.githubusercontent.com/openapi-generators/openapi-test-server/main/openapi.json --config integration-tests/config.yaml --meta pdm --output-path integration-tests \ +openapi-python-client generate --overwrite --url https://raw.githubusercontent.com/openapi-generators/openapi-test-server/refs/tags/v0.2.1/openapi.yaml --config integration-tests/config.yaml --meta none --output-path integration-tests/integration_tests \ """ [build-system] diff --git a/tests/conftest.py b/tests/conftest.py index 969e57cbd..500ebf911 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,13 @@ from __future__ import annotations +from collections.abc import Callable from pathlib import Path -from typing import Any, Callable +from typing import Any import pytest from mypy.semanal_shared import Protocol -from openapi_python_client import Config, MetaType +from openapi_python_client import Config, MetaType, utils from openapi_python_client import schema as oai from openapi_python_client.config import ConfigFile from openapi_python_client.parser.properties import ( @@ -56,7 +57,6 @@ def model_property_factory() -> ModelFactory: You can pass the same params into this as the ModelProperty constructor to override defaults. """ - from openapi_python_client.parser.properties import Class def _factory(**kwargs): kwargs = _common_kwargs(kwargs) @@ -130,7 +130,6 @@ def enum_property_factory() -> EnumFactory[EnumProperty]: You can pass the same params into this as the EnumProerty constructor to override defaults. """ - from openapi_python_client.parser.properties import Class return _simple_factory( EnumProperty, @@ -149,7 +148,6 @@ def literal_enum_property_factory() -> EnumFactory[LiteralEnumProperty]: You can pass the same params into this as the LiteralEnumProerty constructor to override defaults. """ - from openapi_python_client.parser.properties import Class return _simple_factory( LiteralEnumProperty, @@ -324,5 +322,5 @@ def _common_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]: **kwargs, } if not kwargs.get("python_name"): - kwargs["python_name"] = kwargs["name"] + kwargs["python_name"] = utils.PythonIdentifier(value=kwargs["name"], prefix="") return kwargs diff --git a/tests/test___init__.py b/tests/test___init__.py index 3d9b7a0f0..34ad3188f 100644 --- a/tests/test___init__.py +++ b/tests/test___init__.py @@ -1,3 +1,5 @@ +from unittest.mock import MagicMock + import pytest from openapi_python_client import Config, ErrorLevel, Project @@ -7,10 +9,6 @@ def make_project(config: Config) -> Project: - from unittest.mock import MagicMock - - from openapi_python_client import Project - return Project(openapi=MagicMock(title="My Test API"), config=config) diff --git a/tests/test_cli.py b/tests/test_cli.py index 8679584fd..9712b2a04 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,11 +1,11 @@ from typer.testing import CliRunner +from openapi_python_client.cli import app + runner = CliRunner() def test_version() -> None: - from openapi_python_client.cli import app - result = runner.invoke(app, ["--version", "generate"]) assert result.exit_code == 0 @@ -13,28 +13,22 @@ def test_version() -> None: def test_bad_config() -> None: - from openapi_python_client.cli import app - config_path = "config/path" path = "cool/path" result = runner.invoke(app, ["generate", f"--config={config_path}", f"--path={path}"]) assert result.exit_code == 2 - assert "Unable to parse config" in result.stdout + assert "Unable to parse config" in result.output class TestGenerate: def test_generate_no_params(self) -> None: - from openapi_python_client.cli import app - result = runner.invoke(app, ["generate"]) assert result.exit_code == 1, result.output def test_generate_url_and_path(self) -> None: - from openapi_python_client.cli import app - result = runner.invoke(app, ["generate", "--path=blah", "--url=otherblah"]) assert result.exit_code == 1 @@ -43,8 +37,6 @@ def test_generate_url_and_path(self) -> None: def test_generate_encoding_errors(self) -> None: path = "cool/path" file_encoding = "error-file-encoding" - from openapi_python_client.cli import app - result = runner.invoke(app, ["generate", f"--path={path}", f"--file-encoding={file_encoding}"]) assert result.exit_code == 1 diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index 3c810e344..3bd743804 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -5,8 +5,8 @@ import openapi_python_client.schema as oai 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 +from openapi_python_client.parser.openapi import Endpoint, EndpointCollection, import_string_from_class +from openapi_python_client.parser.properties import Class, IntProperty, Parameters, Schemas from openapi_python_client.schema import DataType MODULE_NAME = "openapi_python_client.parser.openapi" @@ -14,8 +14,6 @@ class TestEndpoint: def make_endpoint(self): - from openapi_python_client.parser.openapi import Endpoint - return Endpoint( path="path", method="method", @@ -26,87 +24,7 @@ def make_endpoint(self): relative_imports={"import_3"}, ) - @pytest.mark.parametrize("response_status_code", ["not_a_number", 499]) - def test__add_responses_status_code_error(self, response_status_code, mocker): - from openapi_python_client.parser.openapi import Endpoint, Schemas - - schemas = Schemas() - response_1_data = mocker.MagicMock() - data = { - response_status_code: response_1_data, - } - endpoint = self.make_endpoint() - parse_error = ParseError(data=mocker.MagicMock()) - 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, responses={}, config=config - ) - - assert response.errors == [ - ParseError( - detail=f"Invalid response status code {response_status_code} (not a valid HTTP status code), " - "response will be omitted from generated client" - ) - ] - response_from_data.assert_not_called() - - def test__add_responses_error(self, mocker): - from openapi_python_client.parser.openapi import Endpoint, Schemas - - schemas = Schemas() - response_1_data = mocker.MagicMock() - response_2_data = mocker.MagicMock() - data = { - "200": response_1_data, - "404": response_2_data, - } - endpoint = self.make_endpoint() - parse_error = ParseError(data=mocker.MagicMock(), detail="some problem") - 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, responses={}, config=config - ) - - response_from_data.assert_has_calls( - [ - 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 == [ - ParseError( - detail="Cannot parse response for status code 200 (some problem), " - "response will be omitted from generated client", - data=parse_error.data, - ), - ParseError( - detail="Cannot parse response for status code 404 (some problem), " - "response will be omitted from generated client", - data=parse_error.data, - ), - ] - def test_add_parameters_handles_no_params(self): - from openapi_python_client.parser.openapi import Endpoint, Schemas - endpoint = self.make_endpoint() schemas = Schemas() parameters = Parameters() @@ -122,8 +40,6 @@ def test_add_parameters_handles_no_params(self): ) == (endpoint, schemas, parameters) def test_add_parameters_parse_error(self, mocker): - from openapi_python_client.parser.openapi import Endpoint - endpoint = self.make_endpoint() initial_schemas = mocker.MagicMock() initial_parameters = mocker.MagicMock() @@ -163,8 +79,6 @@ def test_add_parameters_parse_error(self, mocker): ], ) def test_add_parameters_header_types(self, data_type, allowed, config): - from openapi_python_client.parser.openapi import Endpoint - endpoint = self.make_endpoint() initial_schemas = Schemas() parameters = Parameters() @@ -331,8 +245,6 @@ def test__add_parameters_query_optionality(self, config): assert not param.required def test_add_parameters_duplicate_properties(self, config): - from openapi_python_client.parser.openapi import Endpoint, Schemas - endpoint = self.make_endpoint() param = oai.Parameter.model_construct( name="test", required=True, param_schema=oai.Schema.model_construct(type="string"), param_in="path" @@ -356,8 +268,6 @@ def test_add_parameters_duplicate_properties(self, config): ) def test_add_parameters_duplicate_properties_different_location(self, config): - from openapi_python_client.parser.openapi import Endpoint, Schemas - endpoint = self.make_endpoint() path_param = oai.Parameter.model_construct( name="test", required=True, param_schema=oai.Schema.model_construct(type="string"), param_in="path" @@ -380,8 +290,6 @@ def test_add_parameters_duplicate_properties_different_location(self, config): assert result.query_parameters[0].name == "test" def test_sort_parameters(self, string_property_factory): - from openapi_python_client.parser.openapi import Endpoint - endpoint = self.make_endpoint() endpoint.path = "/multiple-path-parameters/{param4}/{param2}/{param1}/{param3}" @@ -396,8 +304,6 @@ def test_sort_parameters(self, string_property_factory): assert result_names == expected_names def test_sort_parameters_missing_param(self, string_property_factory): - from openapi_python_client.parser.openapi import Endpoint - endpoint = self.make_endpoint() endpoint.path = "/multiple-path-parameters/{param1}/{param2}" param = string_property_factory(name="param1") @@ -410,8 +316,6 @@ def test_sort_parameters_missing_param(self, string_property_factory): assert endpoint.path in result.detail def test_sort_parameters_extra_param(self, string_property_factory): - from openapi_python_client.parser.openapi import Endpoint - endpoint = self.make_endpoint() endpoint.path = "/multiple-path-parameters" param = string_property_factory(name="param1") @@ -424,8 +328,6 @@ def test_sort_parameters_extra_param(self, string_property_factory): assert endpoint.path in result.detail def test_from_data_bad_params(self, mocker, config): - from openapi_python_client.parser.openapi import Endpoint - path = mocker.MagicMock() method = mocker.MagicMock() parse_error = ParseError(data=mocker.MagicMock()) @@ -456,8 +358,6 @@ def test_from_data_bad_params(self, mocker, config): assert result == (parse_error, return_schemas, return_parameters) def test_from_data_bad_responses(self, mocker, config): - from openapi_python_client.parser.openapi import Endpoint - path = mocker.MagicMock() method = mocker.MagicMock() parse_error = ParseError(data=mocker.MagicMock()) @@ -492,8 +392,6 @@ def test_from_data_bad_responses(self, mocker, config): assert result == (parse_error, response_schemas, return_parameters) def test_from_data_standard(self, mocker, config): - from openapi_python_client.parser.openapi import Endpoint - path = mocker.MagicMock() method = mocker.MagicMock() param_schemas = mocker.MagicMock() @@ -550,8 +448,6 @@ def test_from_data_standard(self, mocker, config): ) def test_from_data_no_operation_id(self, mocker, config): - from openapi_python_client.parser.openapi import Endpoint - path = "/path/with/{param}/" method = "get" add_parameters = mocker.patch.object( @@ -570,7 +466,7 @@ def test_from_data_no_operation_id(self, mocker, config): mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=data.description) parameters = mocker.MagicMock() - endpoint, _, return_params = Endpoint.from_data( + Endpoint.from_data( data=data, path=path, method=method, @@ -606,8 +502,6 @@ def test_from_data_no_operation_id(self, mocker, config): ) def test_from_data_no_security(self, mocker, config): - from openapi_python_client.parser.openapi import Endpoint - data = oai.Operation.model_construct( description=mocker.MagicMock(), operationId=mocker.MagicMock(), @@ -710,32 +604,26 @@ def test_from_data_all_bodies_bad(self, config): @pytest.mark.parametrize( "response_types, expected", - (([], "Any"), (["Something"], "Something"), (["First", "Second", "Second"], "Union[First, Second]")), + (([], "Any"), (["Something"], "Something"), (["First", "Second", "Second"], "First | Second")), ) def test_response_type(self, response_types, expected): endpoint = self.make_endpoint() for response_type in response_types: mock_response = MagicMock() mock_response.prop.get_type_string.return_value = response_type - endpoint.responses.append(mock_response) + endpoint.responses.patterns.append(mock_response) assert endpoint.response_type() == expected class TestImportStringFromReference: def test_import_string_from_reference_no_prefix(self, mocker): - from openapi_python_client.parser.openapi import import_string_from_class - from openapi_python_client.parser.properties import Class - class_ = mocker.MagicMock(autospec=Class) result = import_string_from_class(class_) assert result == f"from .{class_.module_name} import {class_.name}" def test_import_string_from_reference_with_prefix(self, mocker): - from openapi_python_client.parser.openapi import import_string_from_class - from openapi_python_client.parser.properties import Class - prefix = mocker.MagicMock(autospec=str) class_ = mocker.MagicMock(autospec=Class) result = import_string_from_class(class_=class_, prefix=prefix) @@ -763,7 +651,7 @@ def test_from_data_overrides_path_item_params_with_operation_params(self, config ) } - collections, schemas, parameters = EndpointCollection.from_data( + collections, _schemas, _parameters = EndpointCollection.from_data( data=data, schemas=Schemas(), parameters=Parameters(), diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 1af6342ad..512f5cdac 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -5,29 +5,35 @@ import openapi_python_client.schema as oai from openapi_python_client.parser.errors import ParameterError, PropertyError from openapi_python_client.parser.properties import ( + Class, + Parameters, ReferencePath, Schemas, + _create_schemas, + _process_model_errors, + _process_models, + _propogate_removal, + build_parameters, + build_schemas, + property_from_data, ) +from openapi_python_client.schema import Parameter, Reference, Schema from openapi_python_client.utils import ClassName, PythonIdentifier MODULE_NAME = "openapi_python_client.parser.properties" class TestFileProperty: - def test_is_base_type(self, file_property_factory): - assert file_property_factory().is_base_type is True - @pytest.mark.parametrize("required", (True, False)) def test_get_imports(self, file_property_factory, required): p = file_property_factory(required=required) expected = { "from io import BytesIO", - "from ...types import File, FileJsonType", + "from ...types import File, FileTypes", } if not required: expected |= { - "from typing import Union", "from ...types import UNSET, Unset", } @@ -35,9 +41,6 @@ def test_get_imports(self, file_property_factory, required): class TestUnionProperty: - def test_is_base_type(self, union_property_factory): - assert union_property_factory().is_base_type is False - def test_get_lazy_import_base_inner(self, union_property_factory): p = union_property_factory() assert p.get_lazy_imports(prefix="..") == set() @@ -50,11 +53,11 @@ def test_get_lazy_import_model_inner(self, union_property_factory, model_propert @pytest.mark.parametrize( "required,no_optional,json,expected", [ - (False, False, False, "Union[Unset, datetime.datetime, str]"), - (False, True, False, "Union[datetime.datetime, str]"), - (True, False, False, "Union[datetime.datetime, str]"), - (True, True, False, "Union[datetime.datetime, str]"), - (False, False, True, "Union[Unset, str]"), + (False, False, False, "datetime.datetime | str | Unset"), + (False, True, False, "datetime.datetime | str"), + (True, False, False, "datetime.datetime | str"), + (True, True, False, "datetime.datetime | str"), + (False, False, True, "str | Unset"), (False, True, True, "str"), (True, False, True, "str"), (True, True, True, "str"), @@ -75,7 +78,7 @@ def test_get_type_string( inner_properties=[date_time_property_factory(), string_property_factory()], ) - assert p.get_base_type_string() == "Union[datetime.datetime, str]" + assert p.get_base_type_string() == "datetime.datetime | str" assert p.get_type_string(no_optional=no_optional, json=json) == expected @@ -84,7 +87,7 @@ def test_get_base_type_string_base_inners( ): p = union_property_factory(inner_properties=[date_time_property_factory(), string_property_factory()]) - assert p.get_base_type_string() == "Union[datetime.datetime, str]" + assert p.get_base_type_string() == "datetime.datetime | str" def test_get_base_type_string_one_base_inner(self, union_property_factory, date_time_property_factory): p = union_property_factory( @@ -98,14 +101,14 @@ def test_get_base_type_string_one_model_inner(self, union_property_factory, mode inner_properties=[model_property_factory()], ) - assert p.get_base_type_string() == "'MyClass'" + assert p.get_base_type_string() == "MyClass" def test_get_base_type_string_model_inners( self, union_property_factory, date_time_property_factory, model_property_factory ): p = union_property_factory(inner_properties=[date_time_property_factory(), model_property_factory()]) - assert p.get_base_type_string() == "Union['MyClass', datetime.datetime]" + assert p.get_base_type_string() == "datetime.datetime | MyClass" def test_get_base_json_type_string(self, union_property_factory, date_time_property_factory): p = union_property_factory( @@ -123,12 +126,9 @@ def test_get_type_imports(self, union_property_factory, date_time_property_facto expected = { "import datetime", "from typing import cast", - "from dateutil.parser import isoparse", - "from typing import cast, Union", } if not required: expected |= { - "from typing import Union", "from ...types import UNSET, Unset", } @@ -137,8 +137,6 @@ def test_get_type_imports(self, union_property_factory, date_time_property_facto class TestPropertyFromData: def test_property_from_data_ref_model(self, model_property_factory, config): - from openapi_python_client.parser.properties import Class, Schemas, property_from_data - name = "new_name" required = False class_name = ClassName("MyModel", "") @@ -163,8 +161,6 @@ def test_property_from_data_ref_model(self, model_property_factory, config): assert schemas == new_schemas def test_property_from_data_ref_not_found(self, mocker): - from openapi_python_client.parser.properties import PropertyError, Schemas, property_from_data - data = oai.Reference.model_construct(ref="a/b/c") parse_reference_path = mocker.patch(f"{MODULE_NAME}.parse_reference_path") schemas = Schemas() @@ -180,8 +176,6 @@ def test_property_from_data_ref_not_found(self, mocker): @pytest.mark.parametrize("references_exist", (True, False)) def test_property_from_data_ref(self, any_property_factory, references_exist, config): - from openapi_python_client.parser.properties import Schemas, property_from_data - name = "new_name" required = False ref_path = "/components/schemas/RefName" @@ -201,8 +195,6 @@ def test_property_from_data_ref(self, any_property_factory, references_exist, co assert schemas.dependencies == {ref_path: {*roots, *references.get(ref_path, set())}} def test_property_from_data_invalid_ref(self, mocker): - from openapi_python_client.parser.properties import PropertyError, Schemas, property_from_data - name = mocker.MagicMock() required = mocker.MagicMock() data = oai.Reference.model_construct(ref=mocker.MagicMock()) @@ -222,8 +214,6 @@ def test_property_from_data_invalid_ref(self, mocker): class TestStringBasedProperty: def test__string_based_property_binary_format(self, file_property_factory, config): - from openapi_python_client.parser.properties import property_from_data - name = "file_prop" required = True data = oai.Schema.model_construct(type="string", schema_format="binary", default="a") @@ -235,32 +225,26 @@ def test__string_based_property_binary_format(self, file_property_factory, confi class TestCreateSchemas: - def test_skips_references_and_keeps_going(self, mocker, config): - from openapi_python_client.parser.properties import Schemas, _create_schemas - from openapi_python_client.schema import Reference, Schema - - components = {"a_ref": Reference.model_construct(), "a_schema": Schema.model_construct()} + def test_dereference_references(self, mocker, config): + components = {"a_ref": Reference(ref="#/components/schemas/a_schema"), "a_schema": Schema.model_construct()} update_schemas_with_data = mocker.patch(f"{MODULE_NAME}.update_schemas_with_data") parse_reference_path = mocker.patch(f"{MODULE_NAME}.parse_reference_path") schemas = Schemas() result = _create_schemas(components=components, schemas=schemas, config=config) - # Should not even try to parse a path for the Reference - parse_reference_path.assert_called_once_with("#/components/schemas/a_schema") - update_schemas_with_data.assert_called_once_with( + + parse_reference_path.assert_has_calls( + [call("#/components/schemas/a_ref"), call("#/components/schemas/a_schema")] + ) + update_schemas_with_data.assert_called_with( ref_path=parse_reference_path.return_value, config=config, data=components["a_schema"], - schemas=Schemas( - errors=[PropertyError(detail="Reference schemas are not supported.", data=components["a_ref"])] - ), + schemas=result, ) assert result == update_schemas_with_data.return_value def test_records_bad_uris_and_keeps_going(self, mocker, config): - from openapi_python_client.parser.properties import Schemas, _create_schemas - from openapi_python_client.schema import Schema - components = {"first": Schema.model_construct(), "second": Schema.model_construct()} update_schemas_with_data = mocker.patch(f"{MODULE_NAME}.update_schemas_with_data") parse_reference_path = mocker.patch( @@ -284,9 +268,6 @@ def test_records_bad_uris_and_keeps_going(self, mocker, config): assert result == update_schemas_with_data.return_value def test_retries_failing_properties_while_making_progress(self, mocker, config): - from openapi_python_client.parser.properties import Schemas, _create_schemas - from openapi_python_client.schema import Schema - components = {"first": Schema.model_construct(), "second": Schema.model_construct()} update_schemas_with_data = mocker.patch( f"{MODULE_NAME}.update_schemas_with_data", side_effect=[PropertyError(), Schemas(), PropertyError()] @@ -308,9 +289,6 @@ def test_retries_failing_properties_while_making_progress(self, mocker, config): class TestProcessModels: def test_detect_recursive_allof_reference_no_retry(self, mocker, model_property_factory, config): - from openapi_python_client.parser.properties import Class, _process_models - from openapi_python_client.schema import Reference - class_name = ClassName("class_name", "") recursive_model = model_property_factory( class_info=Class(name=class_name, module_name=PythonIdentifier("module_name", "")) @@ -341,7 +319,6 @@ def test_detect_recursive_allof_reference_no_retry(self, mocker, model_property_ def test_resolve_reference_to_single_allof_reference(self, config, model_property_factory): # test for https://github.com/openapi-generators/openapi-python-client/issues/1091 - from openapi_python_client.parser.properties import Schemas, build_schemas components = { "Model1": oai.Schema.model_construct( @@ -394,9 +371,6 @@ def test_resolve_reference_to_single_allof_reference(self, config, model_propert class TestPropogateRemoval: def test_propogate_removal_class_name(self): - from openapi_python_client.parser.properties import ReferencePath, _propogate_removal - from openapi_python_client.utils import ClassName - root = ClassName("ClassName", "") ref_path = ReferencePath("/reference") other_class_name = ClassName("OtherClassName", "") @@ -414,9 +388,6 @@ def test_propogate_removal_class_name(self): assert not error.detail def test_propogate_removal_ref_path(self): - from openapi_python_client.parser.properties import ReferencePath, _propogate_removal - from openapi_python_client.utils import ClassName - root = ReferencePath("/root/reference") class_name = ClassName("ClassName", "") ref_path = ReferencePath("/ref/path") @@ -434,9 +405,6 @@ def test_propogate_removal_ref_path(self): assert error.detail == f"\n{root}\n{ref_path}" def test_propogate_removal_ref_path_no_refs(self): - from openapi_python_client.parser.properties import ReferencePath, _propogate_removal - from openapi_python_client.utils import ClassName - root = ReferencePath("/root/reference") class_name = ClassName("ClassName", "") ref_path = ReferencePath("/ref/path") @@ -450,9 +418,6 @@ def test_propogate_removal_ref_path_no_refs(self): assert error.detail == f"\n{root}" def test_propogate_removal_ref_path_already_removed(self): - from openapi_python_client.parser.properties import ReferencePath, _propogate_removal - from openapi_python_client.utils import ClassName - root = ReferencePath("/root/reference") class_name = ClassName("ClassName", "") ref_path = ReferencePath("/ref/path") @@ -471,8 +436,6 @@ def test_propogate_removal_ref_path_already_removed(self): def test_process_model_errors(mocker, model_property_factory): - from openapi_python_client.parser.properties import _process_model_errors - propogate_removal = mocker.patch(f"{MODULE_NAME}._propogate_removal") model_errors = [ (model_property_factory(roots={"root1", "root2"}), PropertyError(detail="existing detail")), @@ -492,9 +455,6 @@ def test_process_model_errors(mocker, model_property_factory): class TestBuildParameters: def test_skips_references_and_keeps_going(self, mocker, config): - from openapi_python_client.parser.properties import Parameters, build_parameters - from openapi_python_client.schema import Parameter, Reference - parameters = { "reference": Reference(ref="#/components/parameters/another_parameter"), "defined": Parameter( @@ -524,9 +484,6 @@ def test_skips_references_and_keeps_going(self, mocker, config): assert result == update_parameters_with_data.return_value def test_records_bad_uris_and_keeps_going(self, mocker, config): - from openapi_python_client.parser.properties import Parameters, build_parameters - from openapi_python_client.schema import Parameter - parameters = {"first": Parameter.model_construct(), "second": Parameter.model_construct()} update_parameters_with_data = mocker.patch(f"{MODULE_NAME}.update_parameters_with_data") parse_reference_path = mocker.patch( @@ -549,9 +506,6 @@ def test_records_bad_uris_and_keeps_going(self, mocker, config): assert result == update_parameters_with_data.return_value def test_retries_failing_parameters_while_making_progress(self, mocker, config): - from openapi_python_client.parser.properties import Parameters, build_parameters - from openapi_python_client.schema import Parameter - parameters = {"first": Parameter.model_construct(), "second": Parameter.model_construct()} update_parameters_with_data = mocker.patch( f"{MODULE_NAME}.update_parameters_with_data", side_effect=[ParameterError(), Parameters(), ParameterError()] @@ -571,9 +525,6 @@ def test_retries_failing_parameters_while_making_progress(self, mocker, config): def test_build_schemas(mocker, config): - from openapi_python_client.parser.properties import Schemas, build_schemas - from openapi_python_client.schema import Reference, Schema - create_schemas = mocker.patch(f"{MODULE_NAME}._create_schemas") process_models = mocker.patch(f"{MODULE_NAME}._process_models") diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index a51fd984b..8d686edd7 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -1,44 +1,41 @@ -from typing import Optional - import pytest from attr import evolve import openapi_python_client.schema as oai from openapi_python_client.parser.errors import PropertyError -from openapi_python_client.parser.properties import Schemas, StringProperty -from openapi_python_client.parser.properties.model_property import ANY_ADDITIONAL_PROPERTY, _process_properties +from openapi_python_client.parser.properties import Class, ModelProperty, Schemas, StringProperty +from openapi_python_client.parser.properties.model_property import ( + ANY_ADDITIONAL_PROPERTY, + _process_properties, + _PropertyData, + process_model, +) MODULE_NAME = "openapi_python_client.parser.properties.model_property" class TestModelProperty: @pytest.mark.parametrize( - "no_optional,required,json,quoted,expected", + "no_optional,required,json,expected", [ - (False, False, False, False, "Union[Unset, MyClass]"), - (False, True, False, False, "MyClass"), - (True, False, False, False, "MyClass"), - (True, True, False, False, "MyClass"), - (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, False, False, "MyClass | Unset"), + (False, True, False, "MyClass"), + (True, False, False, "MyClass"), + (True, True, False, "MyClass"), + (False, True, True, "dict[str, Any]"), ], ) - def test_get_type_string(self, no_optional, required, json, expected, model_property_factory, quoted): + def test_get_type_string(self, no_optional, required, json, expected, model_property_factory): prop = model_property_factory( required=required, ) - assert prop.get_type_string(no_optional=no_optional, json=json, quoted=quoted) == expected + assert prop.get_type_string(no_optional=no_optional, json=json) == expected def test_get_imports(self, model_property_factory): prop = model_property_factory(required=False) assert prop.get_imports(prefix="..") == { - "from typing import Union", "from ..types import UNSET, Unset", "from typing import cast", } @@ -50,19 +47,9 @@ def test_get_lazy_imports(self, model_property_factory): "from ..models.my_module import MyClass", } - def test_is_base_type(self, model_property_factory): - assert model_property_factory().is_base_type is False - - @pytest.mark.parametrize( - "quoted,expected", - [ - (False, "MyClass"), - (True, '"MyClass"'), - ], - ) - def test_get_base_type_string(self, quoted, expected, model_property_factory): + def test_get_base_type_string(self, model_property_factory): m = model_property_factory() - assert m.get_base_type_string(quoted=quoted) == expected + assert m.get_base_type_string() == "MyClass" class TestBuild: @@ -87,8 +74,6 @@ class TestBuild: ], ) def test_additional_schemas(self, additional_properties_schema, expected_additional_properties, config): - from openapi_python_client.parser.properties import ModelProperty, Schemas - data = oai.Schema.model_construct( additionalProperties=additional_properties_schema, ) @@ -107,8 +92,6 @@ def test_additional_schemas(self, additional_properties_schema, expected_additio assert model.additional_properties == expected_additional_properties def test_happy_path(self, model_property_factory, string_property_factory, date_time_property_factory, config): - from openapi_python_client.parser.properties import Class, ModelProperty, Schemas - name = "prop" required = True @@ -155,19 +138,15 @@ def test_happy_path(self, model_property_factory, string_property_factory, date_ optional_properties=[date_time_property_factory(name="opt", required=False)], description=data.description, relative_imports={ - "from dateutil.parser import isoparse", "from typing import cast", "import datetime", "from ..types import UNSET, Unset", - "from typing import Union", }, lazy_imports=set(), additional_properties=ANY_ADDITIONAL_PROPERTY, ) def test_model_name_conflict(self, config): - from openapi_python_client.parser.properties import ModelProperty - data = oai.Schema.model_construct() schemas = Schemas(classes_by_name={"OtherModel": None}) @@ -207,14 +186,12 @@ def test_model_name_conflict(self, config): def test_model_naming( self, name: str, - title: Optional[str], - parent_name: Optional[str], + title: str | None, + parent_name: str | None, use_title_prefixing: bool, expected: str, config, ): - from openapi_python_client.parser.properties import ModelProperty - data = oai.Schema( title=title, properties={}, @@ -233,8 +210,6 @@ def test_model_naming( assert result.class_info.name == expected def test_model_bad_properties(self, config): - from openapi_python_client.parser.properties import ModelProperty - data = oai.Schema( properties={ "bad": oai.Reference.model_construct(ref="#/components/schema/NotExist"), @@ -253,8 +228,6 @@ def test_model_bad_properties(self, config): assert isinstance(result, PropertyError) def test_model_bad_additional_properties(self, config): - from openapi_python_client.parser.properties import ModelProperty - additional_properties = oai.Schema( type="object", properties={ @@ -275,8 +248,6 @@ def test_model_bad_additional_properties(self, config): assert isinstance(result, PropertyError) def test_process_properties_false(self, model_property_factory, config): - from openapi_python_client.parser.properties import Class, ModelProperty - name = "prop" required = True @@ -571,6 +542,42 @@ def test_duplicate_properties(self, model_property_factory, string_property_fact assert result.optional_props == [prop], "There should only be one copy of duplicate properties" + def test_allof_required_override(self, model_property_factory, string_property_factory, config): + """Test that required field can be overridden in allOf schemas""" + # Simulates: + # FooBase: + # type: object + # properties: + # bar: {type: string} + # baz: {type: string} + # FooCreate: + # allOf: + # - $ref: '#/components/schemas/FooBase' + # - type: object + # required: [bar] + bar_prop = string_property_factory(name="bar", required=False) + baz_prop = string_property_factory(name="baz", required=False) + + data = oai.Schema.model_construct( + allOf=[ + oai.Reference.model_construct(ref="#/FooBase"), + oai.Schema.model_construct(type="object", required=["bar"]), + ] + ) + schemas = Schemas( + classes_by_reference={ + "/FooBase": model_property_factory(required_properties=[], optional_properties=[bar_prop, baz_prop]), + } + ) + + result = _process_properties(data=data, schemas=schemas, class_name="FooCreate", config=config, roots={"root"}) + + # bar should now be required, baz should remain optional + assert len(result.required_props) == 1 + assert result.required_props[0].name == "bar" + assert len(result.optional_props) == 1 + assert result.optional_props[0].name == "baz" + @pytest.mark.parametrize("first_required", [True, False]) @pytest.mark.parametrize("second_required", [True, False]) def test_mixed_requirements( @@ -671,9 +678,6 @@ def test_merge_inline_objects(self, model_property_factory, enum_property_factor class TestProcessModel: def test_process_model_error(self, mocker, model_property_factory, config): - from openapi_python_client.parser.properties import Schemas - from openapi_python_client.parser.properties.model_property import process_model - model_prop = model_property_factory() schemas = Schemas() process_property_data = mocker.patch(f"{MODULE_NAME}._process_property_data") @@ -688,9 +692,6 @@ def test_process_model_error(self, mocker, model_property_factory, config): assert model_prop.additional_properties is None def test_process_model(self, mocker, model_property_factory, config): - from openapi_python_client.parser.properties import Schemas - from openapi_python_client.parser.properties.model_property import _PropertyData, process_model - model_prop = model_property_factory() schemas = Schemas() property_data = _PropertyData( @@ -715,8 +716,6 @@ def test_process_model(self, mocker, model_property_factory, config): 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 = {f"from ..models.{class_info.module_name} import {class_info.name}"} diff --git a/tests/test_parser/test_properties/test_protocol.py b/tests/test_parser/test_properties/test_protocol.py index 1d4111750..625556c5b 100644 --- a/tests/test_parser/test_properties/test_protocol.py +++ b/tests/test_parser/test_properties/test_protocol.py @@ -2,41 +2,36 @@ import pytest +from openapi_python_client.parser.properties import AnyProperty from openapi_python_client.parser.properties.protocol import Value -def test_is_base_type(any_property_factory): - assert any_property_factory().is_base_type is True - - @pytest.mark.parametrize( - "required,no_optional,json,quoted,expected", + "required,no_optional,json,expected", [ - (False, False, False, False, "Union[Unset, TestType]"), - (False, True, False, False, "TestType"), - (True, False, False, False, "TestType"), - (True, True, False, False, "TestType"), - (False, False, True, False, "Union[Unset, str]"), - (False, True, True, False, "str"), - (True, False, True, False, "str"), - (True, True, True, False, "str"), + (False, False, False, "TestType | Unset"), + (False, True, False, "TestType"), + (True, False, False, "TestType"), + (True, True, False, "TestType"), + (False, False, True, "str | Unset"), + (False, True, True, "str"), + (True, False, True, "str"), + (True, True, True, "str"), ], ) -def test_get_type_string(any_property_factory, mocker, required, no_optional, json, expected, quoted): - from openapi_python_client.parser.properties import AnyProperty - +def test_get_type_string(any_property_factory, mocker, required, no_optional, json, expected): mocker.patch.object(AnyProperty, "_type_string", "TestType") mocker.patch.object(AnyProperty, "_json_type_string", "str") p = any_property_factory(required=required) - assert p.get_type_string(no_optional=no_optional, json=json, quoted=quoted) == expected + assert p.get_type_string(no_optional=no_optional, json=json) == expected @pytest.mark.parametrize( "default,required,expected", [ - (None, False, "test: Union[Unset, Any] = UNSET"), + (None, False, "test: Any | Unset = UNSET"), (None, True, "test: Any"), - ("Test", False, "test: Union[Unset, Any] = Test"), + ("Test", False, "test: Any | Unset = Test"), ("Test", True, "test: Any = Test"), ], ) @@ -54,34 +49,4 @@ def test_get_imports(any_property_factory): assert p.get_imports(prefix="") == set() p = any_property_factory(name="test", required=False, default=None) - assert p.get_imports(prefix="") == {"from types import UNSET, Unset", "from typing import Union"} - - -@pytest.mark.parametrize( - "quoted,expected", - [ - (False, "TestType"), - (True, "TestType"), - ], -) -def test_get_base_type_string(quoted, expected, any_property_factory, mocker): - from openapi_python_client.parser.properties import AnyProperty - - mocker.patch.object(AnyProperty, "_type_string", "TestType") - p = any_property_factory() - assert p.get_base_type_string(quoted=quoted) is expected - - -@pytest.mark.parametrize( - "quoted,expected", - [ - (False, "str"), - (True, "str"), - ], -) -def test_get_base_json_type_string(quoted, expected, any_property_factory, mocker): - from openapi_python_client.parser.properties import AnyProperty - - mocker.patch.object(AnyProperty, "_json_type_string", "str") - p = any_property_factory() - assert p.get_base_json_type_string(quoted=quoted) is expected + assert p.get_imports(prefix="") == {"from types import UNSET, Unset"} diff --git a/tests/test_parser/test_properties/test_schemas.py b/tests/test_parser/test_properties/test_schemas.py index 5560795cf..7e7af8514 100644 --- a/tests/test_parser/test_properties/test_schemas.py +++ b/tests/test_parser/test_properties/test_schemas.py @@ -1,18 +1,21 @@ import pytest from attr import evolve +from openapi_python_client.config import ClassOverride from openapi_python_client.parser.errors import ParameterError from openapi_python_client.parser.properties import Class, Parameters -from openapi_python_client.parser.properties.schemas import parameter_from_reference -from openapi_python_client.schema import Parameter, Reference +from openapi_python_client.parser.properties.schemas import ( + parameter_from_data, + parameter_from_reference, + update_parameters_with_data, +) +from openapi_python_client.schema import Parameter, ParameterLocation, Reference, Schema from openapi_python_client.utils import ClassName MODULE_NAME = "openapi_python_client.parser.properties.schemas" def test_class_from_string_default_config(config): - from openapi_python_client.parser.properties import Class - class_ = Class.from_string(string="#/components/schemas/PingResponse", config=config) assert class_.name == "PingResponse" @@ -29,9 +32,6 @@ def test_class_from_string_default_config(config): ), ) def test_class_from_string(class_override, module_override, expected_class, expected_module, config): - from openapi_python_client.config import ClassOverride - from openapi_python_client.parser.properties import Class - ref = "#/components/schemas/MyResponse" config = evolve( config, class_overrides={"MyResponse": ClassOverride(class_name=class_override, module_name=module_override)} @@ -44,9 +44,6 @@ def test_class_from_string(class_override, module_override, expected_class, expe class TestParameterFromData: def test_cannot_parse_parameters_by_reference(self, config): - from openapi_python_client.parser.properties import Parameters - from openapi_python_client.parser.properties.schemas import parameter_from_data - ref = Reference.model_construct(ref="#/components/parameters/a_param") parameters = Parameters() param_or_error, new_parameters = parameter_from_data( @@ -56,10 +53,6 @@ def test_cannot_parse_parameters_by_reference(self, config): assert new_parameters == parameters def test_parameters_without_schema_are_ignored(self, config): - from openapi_python_client.parser.properties import Parameters - from openapi_python_client.parser.properties.schemas import parameter_from_data - from openapi_python_client.schema import ParameterLocation - param = Parameter(name="a_schemaless_param", param_in=ParameterLocation.QUERY) parameters = Parameters() param_or_error, new_parameters = parameter_from_data( @@ -69,10 +62,6 @@ def test_parameters_without_schema_are_ignored(self, config): assert new_parameters == parameters def test_registers_new_parameters(self, config): - from openapi_python_client.parser.properties import Parameters - from openapi_python_client.parser.properties.schemas import parameter_from_data - from openapi_python_client.schema import ParameterLocation, Schema - param = Parameter.model_construct( name="a_param", param_in=ParameterLocation.QUERY, param_schema=Schema.model_construct() ) @@ -119,9 +108,6 @@ def test_returns_reference_from_registry(self): class TestUpdateParametersFromData: def test_reports_parameters_with_errors(self, mocker, config): - from openapi_python_client.parser.properties.schemas import update_parameters_with_data - from openapi_python_client.schema import ParameterLocation, Schema - parameters = Parameters() param = Parameter.model_construct( name="a_param", param_in=ParameterLocation.QUERY, param_schema=Schema.model_construct() @@ -141,9 +127,6 @@ def test_reports_parameters_with_errors(self, mocker, config): ) def test_records_references_to_parameters(self, mocker, config): - from openapi_python_client.parser.properties.schemas import update_parameters_with_data - from openapi_python_client.schema import ParameterLocation, Schema - parameters = Parameters() param = Parameter.model_construct( name="a_param", param_in=ParameterLocation.QUERY, param_schema=Schema.model_construct() diff --git a/tests/test_parser/test_properties/test_union.py b/tests/test_parser/test_properties/test_union.py index 84e00067d..902603f2e 100644 --- a/tests/test_parser/test_properties/test_union.py +++ b/tests/test_parser/test_properties/test_union.py @@ -1,6 +1,6 @@ import openapi_python_client.schema as oai from openapi_python_client.parser.errors import ParseError -from openapi_python_client.parser.properties import Schemas, UnionProperty +from openapi_python_client.parser.properties import Schemas, UnionProperty, property_from_data from openapi_python_client.schema import DataType, ParameterLocation @@ -28,3 +28,55 @@ def test_not_required_in_path(config): err = prop.validate_location(ParameterLocation.PATH) assert isinstance(err, ParseError) + + +def test_union_oneOf_descriptive_type_name( + union_property_factory, + date_time_property_factory, + string_property_factory, + boolean_property_factory, + date_property_factory, + int_property_factory, + float_property_factory, + config, +): + nested_schema_variant_A = oai.Schema(type=DataType.STRING, title="A") + nested_schema_variant_B = oai.Schema(type=DataType.INTEGER, title="B") + nested_schema_variant_2 = oai.Schema(type=DataType.NUMBER) + nested_schema_variant_C = oai.Schema(type=DataType.BOOLEAN, title="C") + + name = "union_prop" + required = True + data = oai.Schema( + anyOf=[ + # AnyOf retains the old naming convention + nested_schema_variant_C, + oai.Schema(type=DataType.STRING, schema_format="date"), + ], + oneOf=[ + # OneOf fields that define their own titles will have those titles as their Type names + nested_schema_variant_A, + nested_schema_variant_B, + nested_schema_variant_2, + oai.Schema(type=DataType.STRING, schema_format="date-time"), + ], + ) + expected = union_property_factory( + name=name, + required=required, + inner_properties=[ + boolean_property_factory(name=f"{name}_C"), + date_property_factory(name=f"{name}_type_1"), + string_property_factory(name=f"{name}_A"), + int_property_factory(name=f"{name}_B"), + float_property_factory(name=f"{name}_type_4"), + date_time_property_factory(name=f"{name}_type_5"), + ], + ) + + p, s = property_from_data( + name=name, required=required, data=data, schemas=Schemas(), parent_name="parent", config=config + ) + + assert p == expected + assert s == Schemas() diff --git a/tests/test_parser/test_responses.py b/tests/test_parser/test_responses.py index 24fb94c61..6c0f60e2c 100644 --- a/tests/test_parser/test_responses.py +++ b/tests/test_parser/test_responses.py @@ -3,20 +3,25 @@ import pytest import openapi_python_client.schema as oai +from openapi_python_client.parser import responses from openapi_python_client.parser.errors import ParseError, PropertyError from openapi_python_client.parser.properties import Schemas -from openapi_python_client.parser.responses import JSON_SOURCE, NONE_SOURCE +from openapi_python_client.parser.responses import ( + JSON_SOURCE, + NONE_SOURCE, + HTTPStatusPattern, + Response, + response_from_data, +) MODULE_NAME = "openapi_python_client.parser.responses" def test_response_from_data_no_content(any_property_factory): - from openapi_python_client.parser.responses import Response, response_from_data - data = oai.Response.model_construct(description="") - response, schemas = response_from_data( - status_code=200, + response, _schemas = response_from_data( + status_code=HTTPStatusPattern(pattern="200", code_range=(200, 200)), data=data, schemas=Schemas(), responses={}, @@ -25,7 +30,7 @@ def test_response_from_data_no_content(any_property_factory): ) assert response == Response( - status_code=200, + status_code=HTTPStatusPattern(pattern="200", code_range=(200, 200)), prop=any_property_factory( name="response_200", default=None, @@ -37,14 +42,15 @@ def test_response_from_data_no_content(any_property_factory): ) -def test_response_from_data_unsupported_content_type(): - from openapi_python_client.parser.responses import response_from_data +status_code = HTTPStatusPattern(pattern="200", code_range=(200, 200)) + +def test_response_from_data_unsupported_content_type(): data = oai.Response.model_construct(description="", content={"blah": None}) config = MagicMock() config.content_type_overrides = {} - response, schemas = response_from_data( - status_code=200, + response, _schemas = response_from_data( + status_code=status_code, data=data, schemas=Schemas(), responses={}, @@ -56,16 +62,14 @@ def test_response_from_data_unsupported_content_type(): def test_response_from_data_no_content_schema(any_property_factory): - from openapi_python_client.parser.responses import Response, response_from_data - data = oai.Response.model_construct( description="", content={"application/vnd.api+json; version=2.2": oai.MediaType.model_construct()}, ) config = MagicMock() config.content_type_overrides = {} - response, schemas = response_from_data( - status_code=200, + response, _schemas = response_from_data( + status_code=status_code, data=data, schemas=Schemas(), responses={}, @@ -74,7 +78,7 @@ def test_response_from_data_no_content_schema(any_property_factory): ) assert response == Response( - status_code=200, + status_code=status_code, prop=any_property_factory( name="response_200", default=None, @@ -87,8 +91,6 @@ def test_response_from_data_no_content_schema(any_property_factory): def test_response_from_data_property_error(mocker): - from openapi_python_client.parser import responses - property_from_data = mocker.patch.object(responses, "property_from_data", return_value=(PropertyError(), Schemas())) data = oai.Response.model_construct( description="", @@ -97,8 +99,8 @@ def test_response_from_data_property_error(mocker): config = MagicMock() config.content_type_overrides = {} - response, schemas = responses.response_from_data( - status_code=400, + response, _schemas = responses.response_from_data( + status_code=HTTPStatusPattern(pattern="400", code_range=(400, 400)), data=data, schemas=Schemas(), responses={}, @@ -118,8 +120,6 @@ def test_response_from_data_property_error(mocker): def test_response_from_data_property(mocker, any_property_factory): - from openapi_python_client.parser import responses - prop = any_property_factory() property_from_data = mocker.patch.object(responses, "property_from_data", return_value=(prop, Schemas())) data = oai.Response.model_construct( @@ -128,9 +128,10 @@ def test_response_from_data_property(mocker, any_property_factory): ) config = MagicMock() config.content_type_overrides = {} + status_code = HTTPStatusPattern(pattern="400", code_range=(400, 400)) - response, schemas = responses.response_from_data( - status_code=400, + response, _schemas = responses.response_from_data( + status_code=status_code, data=data, schemas=Schemas(), responses={}, @@ -139,7 +140,7 @@ def test_response_from_data_property(mocker, any_property_factory): ) assert response == responses.Response( - status_code=400, + status_code=status_code, prop=prop, source=JSON_SOURCE, data=data, @@ -155,8 +156,6 @@ 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( @@ -166,8 +165,8 @@ def test_response_from_data_reference(mocker, any_property_factory): config = MagicMock() config.content_type_overrides = {} - response, schemas = responses.response_from_data( - status_code=400, + response, _schemas = responses.response_from_data( + status_code=HTTPStatusPattern(pattern="400", code_range=(400, 400)), data=oai.Reference.model_construct(ref="#/components/responses/ErrorResponse"), schemas=Schemas(), responses={"ErrorResponse": predefined_response_data}, @@ -176,7 +175,7 @@ def test_response_from_data_reference(mocker, any_property_factory): ) assert response == responses.Response( - status_code=400, + status_code=HTTPStatusPattern(pattern="400", code_range=(400, 400)), prop=prop, source=JSON_SOURCE, data=predefined_response_data, @@ -192,8 +191,6 @@ def test_response_from_data_reference(mocker, any_property_factory): ], ) 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( @@ -203,8 +200,8 @@ def test_response_from_data_invalid_reference(ref_string, expected_error_string, config = MagicMock() config.content_type_overrides = {} - response, schemas = responses.response_from_data( - status_code=400, + response, _schemas = responses.response_from_data( + status_code=HTTPStatusPattern(pattern="400", code_range=(400, 400)), data=oai.Reference.model_construct(ref=ref_string), schemas=Schemas(), responses={"ErrorResponse": predefined_response_data}, @@ -217,8 +214,6 @@ def test_response_from_data_invalid_reference(ref_string, expected_error_string, 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( @@ -231,8 +226,8 @@ def test_response_from_data_ref_to_response_that_is_a_ref(mocker, any_property_f config = MagicMock() config.content_type_overrides = {} - response, schemas = responses.response_from_data( - status_code=400, + response, _schemas = responses.response_from_data( + status_code=HTTPStatusPattern(pattern="400", code_range=(400, 400)), data=oai.Reference.model_construct(ref="#/components/responses/ErrorResponse"), schemas=Schemas(), responses={ @@ -244,20 +239,18 @@ def test_response_from_data_ref_to_response_that_is_a_ref(mocker, any_property_f ) assert isinstance(response, ParseError) - assert "Top-level $ref" in response.detail + assert response.detail is not None and "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 - data = oai.Response.model_construct( description="", content={"application/zip": oai.MediaType.model_construct()}, ) config = MagicMock() config.content_type_overrides = {"application/zip": "application/octet-stream"} - response, schemas = response_from_data( - status_code=200, + response, _schemas = response_from_data( + status_code=HTTPStatusPattern(pattern="200", code_range=(200, 200)), data=data, schemas=Schemas(), responses={}, @@ -266,7 +259,7 @@ def test_response_from_data_content_type_overrides(any_property_factory): ) assert response == Response( - status_code=200, + status_code=HTTPStatusPattern(pattern="200", code_range=(200, 200)), prop=any_property_factory( name="response_200", default=None, @@ -276,3 +269,23 @@ def test_response_from_data_content_type_overrides(any_property_factory): source=NONE_SOURCE, data=data, ) + + +@pytest.mark.parametrize( + "pattern1, pattern2, result", + [ + ("400", "401", True), + ("503", "500", False), + ("default", "400", False), + ("400", "default", True), + ("2XX", "3XX", True), + ("3XX", "2XX", False), + ("2XX", "400", False), + ], +) +def test_http_status_pattern_lt(pattern1: str, pattern2: str, result: bool) -> None: + first = HTTPStatusPattern.parse(pattern1) + second = HTTPStatusPattern.parse(pattern2) + assert isinstance(first, HTTPStatusPattern) + assert isinstance(second, HTTPStatusPattern) + assert (first < second) == result diff --git a/tests/test_schema/test_data_type.py b/tests/test_schema/test_data_type.py index 19aa256eb..0a4d9681d 100644 --- a/tests/test_schema/test_data_type.py +++ b/tests/test_schema/test_data_type.py @@ -1,3 +1,4 @@ +import pydantic import pytest import openapi_python_client.schema as oai @@ -5,8 +6,6 @@ class TestDataType: def test_schema_bad_types(self): - import pydantic - with pytest.raises(pydantic.ValidationError): oai.Schema(type="bad_type") diff --git a/tests/test_schema/test_noisy_refs.py b/tests/test_schema/test_noisy_refs.py index 0d1ac1fc2..ff5a7cf1c 100644 --- a/tests/test_schema/test_noisy_refs.py +++ b/tests/test_schema/test_noisy_refs.py @@ -11,7 +11,7 @@ # References: # [1] https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object # [2] https://docs.pydantic.dev/latest/concepts/unions/#smart-mode -from typing import Annotated, TypeVar, Union, get_args, get_origin +from typing import Annotated, TypeVar, get_args, get_origin import pytest from pydantic import TypeAdapter @@ -34,7 +34,7 @@ from openapi_python_client.schema.openapi_schema_pydantic.reference import ReferenceOr except ImportError: T = TypeVar("T") - ReferenceOr = Union[Reference, T] + ReferenceOr = Reference | T def get_example(base_type): diff --git a/tests/test_templates/test_model_template.py b/tests/test_templates/test_model_template.py new file mode 100644 index 000000000..b80491197 --- /dev/null +++ b/tests/test_templates/test_model_template.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from types import SimpleNamespace + + +def _section(content: str, start: str, end: str | None = None) -> str: + section = content.split(start, 1)[1] + if end is not None: + section = section.split(end, 1)[0] + return section + + +def test_model_template_renders_lazy_imports_in_stable_order(env) -> None: + template = env.get_template("model.py.jinja") + + model = SimpleNamespace( + is_multipart_body=True, + relative_imports=set(), + lazy_imports={"from ..models.z import Z", "from ..models.a import A"}, + additional_properties=False, + class_info=SimpleNamespace(name="MyModel", module_name="my_model"), + title="", + description="", + example="", + required_properties=[], + optional_properties=[], + ) + config = SimpleNamespace(docstrings_on_attributes=False) + + content = template.render(model=model, config=config) + + sections = [ + _section(content, "if TYPE_CHECKING:", "T = TypeVar"), + _section(content, "def to_dict(self)", "def to_multipart(self)"), + _section(content, "def to_multipart(self)", "@classmethod"), + _section(content, "def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:", "return my_model"), + ] + for section in sections: + assert section.index("from ..models.a import A") < section.index("from ..models.z import Z") diff --git a/tests/test_templates/test_property_templates/test_date_property/date_property_template.py.jinja b/tests/test_templates/test_property_templates/test_date_property/date_property_template.py.jinja index 3709963fe..071aef509 100644 --- a/tests/test_templates/test_property_templates/test_date_property/date_property_template.py.jinja +++ b/tests/test_templates/test_property_templates/test_date_property/date_property_template.py.jinja @@ -1,7 +1,6 @@ +import datetime from datetime import date -from typing import cast, Union -from dateutil.parser import isoparse {% from "property_templates/date_property.py.jinja" import transform, construct %} some_source = date(2020, 10, 12) {{ transform(property, "some_source", "some_destination") }} diff --git a/tests/test_templates/test_property_templates/test_date_property/required_not_null.py b/tests/test_templates/test_property_templates/test_date_property/required_not_null.py index ad4f380a4..f27bc455f 100644 --- a/tests/test_templates/test_property_templates/test_date_property/required_not_null.py +++ b/tests/test_templates/test_property_templates/test_date_property/required_not_null.py @@ -1,9 +1,8 @@ +import datetime from datetime import date -from typing import cast, Union -from dateutil.parser import isoparse some_source = date(2020, 10, 12) some_destination = some_source.isoformat() -a_prop = isoparse(some_destination).date() +a_prop = datetime.date.fromisoformat(some_destination) diff --git a/tests/test_templates/test_property_templates/test_datetime_property/datetime_property_template.py.jinja b/tests/test_templates/test_property_templates/test_datetime_property/datetime_property_template.py.jinja index 85fa1548d..3b428734d 100644 --- a/tests/test_templates/test_property_templates/test_datetime_property/datetime_property_template.py.jinja +++ b/tests/test_templates/test_property_templates/test_datetime_property/datetime_property_template.py.jinja @@ -1,7 +1,6 @@ +import datetime from datetime import date -from typing import cast, Union -from dateutil.parser import isoparse {% from "property_templates/datetime_property.py.jinja" import transform, construct %} some_source = date(2020, 10, 12) {{ transform(property, "some_source", "some_destination") }} diff --git a/tests/test_templates/test_property_templates/test_datetime_property/required_not_null.py b/tests/test_templates/test_property_templates/test_datetime_property/required_not_null.py index 8253828e3..c55e883d7 100644 --- a/tests/test_templates/test_property_templates/test_datetime_property/required_not_null.py +++ b/tests/test_templates/test_property_templates/test_datetime_property/required_not_null.py @@ -1,9 +1,8 @@ +import datetime from datetime import date -from typing import cast, Union -from dateutil.parser import isoparse some_source = date(2020, 10, 12) some_destination = some_source.isoformat() -a_prop = isoparse(some_destination) +a_prop = datetime.datetime.fromisoformat(some_destination) diff --git a/tests/test_templates/test_property_templates/test_uuid_property/__init__.py b/tests/test_templates/test_property_templates/test_uuid_property/__init__.py new file mode 100644 index 000000000..839bd41fd --- /dev/null +++ b/tests/test_templates/test_property_templates/test_uuid_property/__init__.py @@ -0,0 +1 @@ +"""Tests for UUID property templates.""" diff --git a/tests/test_templates/test_property_templates/test_uuid_property/test_uuid_multipart.py b/tests/test_templates/test_property_templates/test_uuid_property/test_uuid_multipart.py new file mode 100644 index 000000000..fe7cd7f85 --- /dev/null +++ b/tests/test_templates/test_property_templates/test_uuid_property/test_uuid_multipart.py @@ -0,0 +1,70 @@ +"""Tests for UUID property multipart macro functionality.""" + +from pathlib import Path +from typing import Any +from uuid import UUID + +import jinja2 +import pytest + +from openapi_python_client.parser.properties import UuidProperty +from openapi_python_client.utils import PythonIdentifier + + +def uuid_property(required: bool = True, default: Any = None) -> UuidProperty: + """Helper to create a UuidProperty for testing.""" + return UuidProperty( + name="test_uuid", + required=required, + default=default, + python_name=PythonIdentifier(value="test_uuid", prefix=""), + description="A test UUID property", + example="550e8400-e29b-41d4-a716-446655440000", + ) + + +@pytest.fixture +def jinja_env() -> jinja2.Environment: + """Create a Jinja2 environment with the property templates loaded.""" + templates_dir = Path(__file__).parent.parent.parent.parent.parent / "openapi_python_client" / "templates" + env = jinja2.Environment( + loader=jinja2.FileSystemLoader(templates_dir), + trim_blocks=True, + lstrip_blocks=True, + ) + return env + + +def test_multipart_macro_generates_syntactically_correct_code_for_required_uuid(jinja_env: jinja2.Environment) -> None: + """Test that the multipart macro generates syntactically correct Python code for required UUID properties.""" + prop = uuid_property(required=True) + + template = jinja_env.get_template("property_templates/uuid_property.py.jinja") + + # Render the multipart macro + multipart_code = template.module.multipart(prop, "test_uuid", '"test_uuid"') # type: ignore[attr-defined] + + # Verify the generated code is syntactically correct + expected = 'files.append(("test_uuid", (None, str(test_uuid), "text/plain")))' + assert multipart_code.strip() == expected + + # Verify it compiles as valid Python + compile(multipart_code, "", "exec") + + +def test_multipart_macro_generates_syntactically_correct_code_for_optional_uuid(jinja_env: jinja2.Environment) -> None: + """Test that the multipart macro generates syntactically correct Python code for optional UUID properties.""" + prop = uuid_property(required=False) + + template = jinja_env.get_template("property_templates/uuid_property.py.jinja") + + # Render the multipart macro + multipart_code = template.module.multipart(prop, "test_uuid", '"test_uuid"') # type: ignore[attr-defined] + + # Verify the generated code is syntactically correct + expected = 'files.append(("test_uuid", (None, str(test_uuid), "text/plain")))' + assert multipart_code.strip() == expected + + # Verify it compiles as valid Python + compile(multipart_code, "", "exec") + \ No newline at end of file