fastapi-router-versioning 0.2.7__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/bump-pre-commit-hooks.yml +3 -3
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/create-draft-release.yml +3 -3
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/labeler.yml +1 -1
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/latest-changes.yml +1 -1
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/pre-commit.yml +3 -3
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/prepare-release.yml +3 -3
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/publish.yml +3 -3
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/test-redistribute.yml +3 -3
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/test.yml +6 -6
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/zizmor.yml +2 -2
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/PKG-INFO +44 -58
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/README.md +43 -57
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/RELEASE_NOTES.md +12 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/mounted_subapps_app.py +2 -4
- fastapi_router_versioning-0.3.0/examples/validation_override_integration_app.py +56 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/fastapi_router_versioning/__init__.py +1 -1
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/fastapi_router_versioning/versioner.py +0 -99
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/pyproject.toml +1 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/tests/test_versioner.py +7 -330
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/uv.lock +131 -117
- fastapi_router_versioning-0.2.7/examples/validation_error_code_app.py +0 -57
- fastapi_router_versioning-0.2.7/examples/validation_error_code_custom_handler_app.py +0 -70
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/dependabot.yml +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/labeler.yml +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/detect-conflicts.yml +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/guard-dependencies.yml +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.gitignore +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.pre-commit-config.yaml +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.python-version +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/LICENSE +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/calver_app.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/download_static_assets.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/multi_router_app.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/openapi_hook_app.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/self_hosted_docs_app.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/semver_app.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/semver_major_only_app.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/webhook_versioning_app.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/fastapi_router_versioning/py.typed +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/scripts/format.sh +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/scripts/lint.sh +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/scripts/prepare_release.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/scripts/test-cov-html.sh +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/scripts/test-cov.sh +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/scripts/test.sh +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/tests/__init__.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/tests/test_calver.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/tests/test_fastapi_integration.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/tests/test_prepare_release.py +0 -0
- {fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/tests/test_semver.py +0 -0
|
@@ -17,16 +17,16 @@ jobs:
|
|
|
17
17
|
env:
|
|
18
18
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
19
19
|
run: echo "$GITHUB_CONTEXT"
|
|
20
|
-
- uses: actions/checkout@
|
|
20
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
21
21
|
with:
|
|
22
22
|
token: ${{ secrets.FASTAPI_ROUTER_VERSIONING_LATEST_CHANGES }}
|
|
23
23
|
persist-credentials: true
|
|
24
24
|
- name: Set up Python
|
|
25
|
-
uses: actions/setup-python@
|
|
25
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
26
26
|
with:
|
|
27
27
|
python-version-file: ".python-version"
|
|
28
28
|
- name: Setup uv
|
|
29
|
-
uses: astral-sh/setup-uv@
|
|
29
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
30
30
|
with:
|
|
31
31
|
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
32
32
|
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
|
@@ -22,16 +22,16 @@ jobs:
|
|
|
22
22
|
env:
|
|
23
23
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
24
24
|
run: echo "$GITHUB_CONTEXT"
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
26
|
with:
|
|
27
27
|
ref: ${{ github.event.repository.default_branch }}
|
|
28
28
|
persist-credentials: true
|
|
29
29
|
- name: Set up Python
|
|
30
|
-
uses: actions/setup-python@
|
|
30
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
31
31
|
with:
|
|
32
32
|
python-version-file: ".python-version"
|
|
33
33
|
- name: Install uv
|
|
34
|
-
uses: astral-sh/setup-uv@
|
|
34
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
35
35
|
with:
|
|
36
36
|
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
37
37
|
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
{fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/labeler.yml
RENAMED
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
timeout-minutes: 5
|
|
21
21
|
steps:
|
|
22
|
-
- uses: actions/labeler@
|
|
22
|
+
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
|
|
23
23
|
if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }}
|
|
24
24
|
- run: echo "Done adding labels"
|
|
25
25
|
# Run this after labeler applied labels
|
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
env:
|
|
29
29
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
30
30
|
run: echo "$GITHUB_CONTEXT"
|
|
31
|
-
- uses: actions/checkout@
|
|
31
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
32
32
|
with:
|
|
33
33
|
ref: ${{ github.event.repository.default_branch }}
|
|
34
34
|
# To allow latest-changes to commit to the main branch
|
{fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/pre-commit.yml
RENAMED
|
@@ -18,19 +18,19 @@ jobs:
|
|
|
18
18
|
run: echo "$GITHUB_CONTEXT"
|
|
19
19
|
|
|
20
20
|
- name: Checkout code
|
|
21
|
-
uses: actions/checkout@
|
|
21
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
22
|
with:
|
|
23
23
|
persist-credentials: false
|
|
24
24
|
# Full history required for --from-ref/--to-ref diff in prek
|
|
25
25
|
fetch-depth: 0
|
|
26
26
|
|
|
27
27
|
- name: Set up Python
|
|
28
|
-
uses: actions/setup-python@
|
|
28
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
29
29
|
with:
|
|
30
30
|
python-version-file: ".python-version"
|
|
31
31
|
|
|
32
32
|
- name: Setup uv
|
|
33
|
-
uses: astral-sh/setup-uv@
|
|
33
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
34
34
|
with:
|
|
35
35
|
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
36
36
|
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
|
@@ -34,16 +34,16 @@ jobs:
|
|
|
34
34
|
env:
|
|
35
35
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
36
36
|
run: echo "$GITHUB_CONTEXT"
|
|
37
|
-
- uses: actions/checkout@
|
|
37
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
38
38
|
with:
|
|
39
39
|
token: ${{ secrets.FASTAPI_ROUTER_VERSIONING_LATEST_CHANGES }} # zizmor: ignore[secrets-outside-env]
|
|
40
40
|
persist-credentials: true
|
|
41
41
|
- name: Set up Python
|
|
42
|
-
uses: actions/setup-python@
|
|
42
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
43
43
|
with:
|
|
44
44
|
python-version-file: ".python-version"
|
|
45
45
|
- name: Install uv
|
|
46
|
-
uses: astral-sh/setup-uv@
|
|
46
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
47
47
|
with:
|
|
48
48
|
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
49
49
|
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
{fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/publish.yml
RENAMED
|
@@ -20,15 +20,15 @@ jobs:
|
|
|
20
20
|
env:
|
|
21
21
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
22
22
|
run: echo "$GITHUB_CONTEXT"
|
|
23
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
24
|
with:
|
|
25
25
|
persist-credentials: false
|
|
26
26
|
- name: Set up Python
|
|
27
|
-
uses: actions/setup-python@
|
|
27
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
28
28
|
with:
|
|
29
29
|
python-version-file: ".python-version"
|
|
30
30
|
- name: Install uv
|
|
31
|
-
uses: astral-sh/setup-uv@
|
|
31
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
32
32
|
with:
|
|
33
33
|
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
34
34
|
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
outputs:
|
|
19
19
|
src: ${{ steps.filter.outputs.src }}
|
|
20
20
|
steps:
|
|
21
|
-
- uses: actions/checkout@
|
|
21
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
22
|
with:
|
|
23
23
|
persist-credentials: false
|
|
24
24
|
- uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
|
|
@@ -46,11 +46,11 @@ jobs:
|
|
|
46
46
|
env:
|
|
47
47
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
48
48
|
run: echo "$GITHUB_CONTEXT"
|
|
49
|
-
- uses: actions/checkout@
|
|
49
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
50
50
|
with:
|
|
51
51
|
persist-credentials: false
|
|
52
52
|
- name: Set up Python
|
|
53
|
-
uses: actions/setup-python@
|
|
53
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
54
54
|
with:
|
|
55
55
|
python-version-file: ".python-version"
|
|
56
56
|
- name: Install build dependencies
|
{fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/test.yml
RENAMED
|
@@ -26,7 +26,7 @@ jobs:
|
|
|
26
26
|
outputs:
|
|
27
27
|
src: ${{ steps.filter.outputs.src }}
|
|
28
28
|
steps:
|
|
29
|
-
- uses: actions/checkout@
|
|
29
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
30
30
|
with:
|
|
31
31
|
persist-credentials: false
|
|
32
32
|
# For pull requests it's not necessary to checkout the code but for the main branch it is
|
|
@@ -72,17 +72,17 @@ jobs:
|
|
|
72
72
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
73
73
|
run: echo "$GITHUB_CONTEXT"
|
|
74
74
|
|
|
75
|
-
- uses: actions/checkout@
|
|
75
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
76
76
|
with:
|
|
77
77
|
persist-credentials: false
|
|
78
78
|
|
|
79
79
|
- name: Set up Python
|
|
80
|
-
uses: actions/setup-python@
|
|
80
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
81
81
|
with:
|
|
82
82
|
python-version: ${{ matrix.python-version }}
|
|
83
83
|
|
|
84
84
|
- name: Setup uv
|
|
85
|
-
uses: astral-sh/setup-uv@
|
|
85
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
86
86
|
with:
|
|
87
87
|
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
88
88
|
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
|
@@ -130,12 +130,12 @@ jobs:
|
|
|
130
130
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
131
131
|
run: echo "$GITHUB_CONTEXT"
|
|
132
132
|
|
|
133
|
-
- uses: actions/checkout@
|
|
133
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
134
134
|
with:
|
|
135
135
|
persist-credentials: false
|
|
136
136
|
|
|
137
137
|
- name: Setup uv
|
|
138
|
-
uses: astral-sh/setup-uv@
|
|
138
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
139
139
|
with:
|
|
140
140
|
# Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
|
|
141
141
|
# See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
|
{fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/.github/workflows/zizmor.yml
RENAMED
|
@@ -18,8 +18,8 @@ jobs:
|
|
|
18
18
|
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
|
|
19
19
|
steps:
|
|
20
20
|
- name: Checkout repository
|
|
21
|
-
uses: actions/checkout@
|
|
21
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
22
|
with:
|
|
23
23
|
persist-credentials: false
|
|
24
24
|
- name: Run zizmor
|
|
25
|
-
uses: zizmorcore/zizmor-action@
|
|
25
|
+
uses: zizmorcore/zizmor-action@6599ee8b7a49aef6a770f63d261d214911a7ce02 # v0.6.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-router-versioning
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Router-based API versioning for FastAPI, with per-version docs and a declarative route lifecycle.
|
|
5
5
|
Project-URL: Homepage, https://github.com/mat81black/fastapi-router-versioning
|
|
6
6
|
Project-URL: Repository, https://github.com/mat81black/fastapi-router-versioning
|
|
@@ -49,7 +49,6 @@ Running multiple API versions side by side usually means duplicating routers, ha
|
|
|
49
49
|
- **Latest alias**: expose the newest version under a fixed `/latest` prefix clients can pin to
|
|
50
50
|
- **Self-hosted docs assets**: point Swagger UI and ReDoc at your own JS/CSS for air-gapped deployments
|
|
51
51
|
- **Reverse proxy and sub-app aware**: doc URLs pick up the ASGI `root_path` at request time
|
|
52
|
-
- **Configurable validation error status**: swap FastAPI's default `422` for any code, applied consistently to runtime responses and every OpenAPI schema, including the app's own root schema
|
|
53
52
|
- **Route composition support**: works across nested routers, WebSockets, `Depends`, and OpenAPI Callbacks
|
|
54
53
|
|
|
55
54
|
---
|
|
@@ -189,8 +188,6 @@ Use one or the other: `deprecated=True` for an unconditional, version-independen
|
|
|
189
188
|
| `redoc_js_url` | `str \| None` | FastAPI CDN | Custom URL for the ReDoc JS bundle |
|
|
190
189
|
| `redoc_favicon_url` | `str \| None` | FastAPI favicon | Custom URL for the ReDoc favicon |
|
|
191
190
|
| `redoc_with_google_fonts` | `bool` | `True` | Set `False` to stop ReDoc from loading Google Fonts |
|
|
192
|
-
| `validation_error_code` | `int` | `422` | Status code returned for request validation errors; also replaces the `422` entry everywhere it appears in the OpenAPI schema, root schema included |
|
|
193
|
-
| `handle_validation_exceptions` | `bool` | `True` | Set `False` to only patch the schema and register your own `RequestValidationError` handler |
|
|
194
191
|
|
|
195
192
|
`.versionize()` returns the list of versions it activated. It can only be called once per
|
|
196
193
|
instance; a second call raises `RuntimeError`, since it mutates the live FastAPI app in a way
|
|
@@ -299,6 +296,41 @@ RouterVersioner(
|
|
|
299
296
|
).versionize()
|
|
300
297
|
```
|
|
301
298
|
|
|
299
|
+
### Custom validation error status code
|
|
300
|
+
|
|
301
|
+
Changing FastAPI's default `422` for request validation errors is not something
|
|
302
|
+
`RouterVersioner` does itself: it's a separate, general-purpose concern, handled by the
|
|
303
|
+
[fastapi-validation-override](https://pypi.org/project/fastapi-validation-override/) package.
|
|
304
|
+
`openapi_hook` is the integration point: it lets you re-apply the same patch to every
|
|
305
|
+
per-version schema that `RouterVersioner` generates, so all of them, root schema included,
|
|
306
|
+
stay consistent.
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
uv add fastapi-validation-override
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from fastapi_validation_override import override_validation_error, patch_422_responses
|
|
314
|
+
|
|
315
|
+
# 1. Registers the runtime handler and patches the app's own root /openapi.json.
|
|
316
|
+
override_validation_error(app, status_code=400)
|
|
317
|
+
|
|
318
|
+
# 2. Re-applies the same patch to each version's own schema.
|
|
319
|
+
def versioning_openapi_hook(schema: dict, version: tuple[int, int]) -> dict:
|
|
320
|
+
return patch_422_responses(schema, "400")
|
|
321
|
+
|
|
322
|
+
RouterVersioner(
|
|
323
|
+
app=app,
|
|
324
|
+
routers=router,
|
|
325
|
+
version_format=VersionFormat.SEMVER,
|
|
326
|
+
openapi_hook=versioning_openapi_hook,
|
|
327
|
+
).versionize()
|
|
328
|
+
# Validation failures now return 400, both at runtime and in every schema
|
|
329
|
+
# (root, and every /vX_Y/openapi.json)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
See [`examples/validation_override_integration_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_override_integration_app.py).
|
|
333
|
+
|
|
302
334
|
### OpenAPI Callbacks and Webhooks
|
|
303
335
|
|
|
304
336
|
Route-level **Callbacks** need no special handling: a `callbacks=[...]` argument on a route
|
|
@@ -382,63 +414,18 @@ RouterVersioner(
|
|
|
382
414
|
[`examples/download_static_assets.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/download_static_assets.py) downloads the required files in one step;
|
|
383
415
|
[`examples/self_hosted_docs_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/self_hosted_docs_app.py) wires them into a full app.
|
|
384
416
|
|
|
385
|
-
### Validation error status code
|
|
386
|
-
|
|
387
|
-
`validation_error_code` changes what FastAPI returns for a failed request body/query/path
|
|
388
|
-
validation, `422` by default, both at runtime and in the schema:
|
|
389
|
-
|
|
390
|
-
```python
|
|
391
|
-
RouterVersioner(
|
|
392
|
-
app=app,
|
|
393
|
-
routers=router,
|
|
394
|
-
version_format=VersionFormat.SEMVER,
|
|
395
|
-
validation_error_code=400,
|
|
396
|
-
).versionize()
|
|
397
|
-
# Validation failures now return 400; every 422 entry in the schema becomes 400
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
Set `handle_validation_exceptions=False` to keep the schema change while writing your own
|
|
401
|
-
response body:
|
|
402
|
-
|
|
403
|
-
```python
|
|
404
|
-
from fastapi import Request
|
|
405
|
-
from fastapi.exceptions import RequestValidationError
|
|
406
|
-
from fastapi.responses import JSONResponse
|
|
407
|
-
|
|
408
|
-
@app.exception_handler(RequestValidationError)
|
|
409
|
-
async def my_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
|
|
410
|
-
return JSONResponse(status_code=400, content={"errors": exc.errors()})
|
|
411
|
-
|
|
412
|
-
RouterVersioner(
|
|
413
|
-
app=app,
|
|
414
|
-
routers=router,
|
|
415
|
-
version_format=VersionFormat.SEMVER,
|
|
416
|
-
validation_error_code=400,
|
|
417
|
-
handle_validation_exceptions=False, # schema updated; the handler above owns the response
|
|
418
|
-
).versionize()
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
This also patches the app's own root `/docs`, `/redoc`, and `/openapi.json`, not just the
|
|
422
|
-
versioned ones, so there's nothing left showing the stale `422`.
|
|
423
|
-
|
|
424
417
|
Sharing one app across several `RouterVersioner` instances only makes sense for one reason:
|
|
425
418
|
mixing `version_format` values, SemVer for one group of routes and CalVer for another, on
|
|
426
419
|
the same app. Splitting modules that share a `version_format` doesn't need a second instance;
|
|
427
420
|
pass them all to one `RouterVersioner` via `routers=[...]` instead (see
|
|
428
421
|
[`multi_router_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/multi_router_app.py)).
|
|
429
|
-
If you do share an app across instances,
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
router, so a mismatch raises `RuntimeError` when the second instance is constructed rather
|
|
434
|
-
than silently keeping whichever handler was registered first.
|
|
435
|
-
- Every instance needs its own `prefix_format`/`latest_prefix`. Two instances that resolve to
|
|
436
|
-
the same prefix would otherwise overwrite each other's docs/openapi routes at the same
|
|
437
|
-
path; this raises `RuntimeError` too.
|
|
422
|
+
If you do share an app across instances, one rule is enforced for you: every instance needs
|
|
423
|
+
its own `prefix_format`/`latest_prefix`. Two instances that resolve to the same prefix would
|
|
424
|
+
otherwise overwrite each other's docs/openapi routes at the same path; this raises
|
|
425
|
+
`RuntimeError`.
|
|
438
426
|
|
|
439
|
-
For modules that genuinely don't need to coordinate at all,
|
|
440
|
-
|
|
441
|
-
section).
|
|
427
|
+
For modules that genuinely don't need to coordinate at all, mount them as separate FastAPI
|
|
428
|
+
sub-applications instead (next section).
|
|
442
429
|
|
|
443
430
|
### Reverse proxy and sub-application mounting
|
|
444
431
|
|
|
@@ -453,7 +440,7 @@ parent.mount("/api", app) # root_path="/api" is injected per request
|
|
|
453
440
|
|
|
454
441
|
A mounted sub-application is a separate `FastAPI()` instance with its own `app.state`, so a
|
|
455
442
|
`RouterVersioner` attached to it is entirely independent from one attached to the parent, or
|
|
456
|
-
to another sub-application, no
|
|
443
|
+
to another sub-application, no prefix coordination needed.
|
|
457
444
|
See [`examples/mounted_subapps_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/mounted_subapps_app.py).
|
|
458
445
|
|
|
459
446
|
### Callback hook
|
|
@@ -486,9 +473,8 @@ RouterVersioner(
|
|
|
486
473
|
| [`multi_router_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/multi_router_app.py) | Several routers versioned together under one instance |
|
|
487
474
|
| [`self_hosted_docs_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/self_hosted_docs_app.py) | Swagger UI and ReDoc served from local static assets |
|
|
488
475
|
| [`openapi_hook_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/openapi_hook_app.py) | Per-version OpenAPI schema edits via `openapi_hook` |
|
|
489
|
-
| [`validation_error_code_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_error_code_app.py) | `400` instead of `422`, handled automatically by `RouterVersioner` |
|
|
490
|
-
| [`validation_error_code_custom_handler_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_error_code_custom_handler_app.py) | Same, but with `handle_validation_exceptions=False` and a custom handler |
|
|
491
476
|
| [`mounted_subapps_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/mounted_subapps_app.py) | Independently versioned modules as separate `app.mount()` sub-applications |
|
|
477
|
+
| [`validation_override_integration_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_override_integration_app.py) | Custom validation error status code via `fastapi-validation-override` and `openapi_hook` |
|
|
492
478
|
|
|
493
479
|
---
|
|
494
480
|
|
|
@@ -18,7 +18,6 @@ Running multiple API versions side by side usually means duplicating routers, ha
|
|
|
18
18
|
- **Latest alias**: expose the newest version under a fixed `/latest` prefix clients can pin to
|
|
19
19
|
- **Self-hosted docs assets**: point Swagger UI and ReDoc at your own JS/CSS for air-gapped deployments
|
|
20
20
|
- **Reverse proxy and sub-app aware**: doc URLs pick up the ASGI `root_path` at request time
|
|
21
|
-
- **Configurable validation error status**: swap FastAPI's default `422` for any code, applied consistently to runtime responses and every OpenAPI schema, including the app's own root schema
|
|
22
21
|
- **Route composition support**: works across nested routers, WebSockets, `Depends`, and OpenAPI Callbacks
|
|
23
22
|
|
|
24
23
|
---
|
|
@@ -158,8 +157,6 @@ Use one or the other: `deprecated=True` for an unconditional, version-independen
|
|
|
158
157
|
| `redoc_js_url` | `str \| None` | FastAPI CDN | Custom URL for the ReDoc JS bundle |
|
|
159
158
|
| `redoc_favicon_url` | `str \| None` | FastAPI favicon | Custom URL for the ReDoc favicon |
|
|
160
159
|
| `redoc_with_google_fonts` | `bool` | `True` | Set `False` to stop ReDoc from loading Google Fonts |
|
|
161
|
-
| `validation_error_code` | `int` | `422` | Status code returned for request validation errors; also replaces the `422` entry everywhere it appears in the OpenAPI schema, root schema included |
|
|
162
|
-
| `handle_validation_exceptions` | `bool` | `True` | Set `False` to only patch the schema and register your own `RequestValidationError` handler |
|
|
163
160
|
|
|
164
161
|
`.versionize()` returns the list of versions it activated. It can only be called once per
|
|
165
162
|
instance; a second call raises `RuntimeError`, since it mutates the live FastAPI app in a way
|
|
@@ -268,6 +265,41 @@ RouterVersioner(
|
|
|
268
265
|
).versionize()
|
|
269
266
|
```
|
|
270
267
|
|
|
268
|
+
### Custom validation error status code
|
|
269
|
+
|
|
270
|
+
Changing FastAPI's default `422` for request validation errors is not something
|
|
271
|
+
`RouterVersioner` does itself: it's a separate, general-purpose concern, handled by the
|
|
272
|
+
[fastapi-validation-override](https://pypi.org/project/fastapi-validation-override/) package.
|
|
273
|
+
`openapi_hook` is the integration point: it lets you re-apply the same patch to every
|
|
274
|
+
per-version schema that `RouterVersioner` generates, so all of them, root schema included,
|
|
275
|
+
stay consistent.
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
uv add fastapi-validation-override
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from fastapi_validation_override import override_validation_error, patch_422_responses
|
|
283
|
+
|
|
284
|
+
# 1. Registers the runtime handler and patches the app's own root /openapi.json.
|
|
285
|
+
override_validation_error(app, status_code=400)
|
|
286
|
+
|
|
287
|
+
# 2. Re-applies the same patch to each version's own schema.
|
|
288
|
+
def versioning_openapi_hook(schema: dict, version: tuple[int, int]) -> dict:
|
|
289
|
+
return patch_422_responses(schema, "400")
|
|
290
|
+
|
|
291
|
+
RouterVersioner(
|
|
292
|
+
app=app,
|
|
293
|
+
routers=router,
|
|
294
|
+
version_format=VersionFormat.SEMVER,
|
|
295
|
+
openapi_hook=versioning_openapi_hook,
|
|
296
|
+
).versionize()
|
|
297
|
+
# Validation failures now return 400, both at runtime and in every schema
|
|
298
|
+
# (root, and every /vX_Y/openapi.json)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
See [`examples/validation_override_integration_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_override_integration_app.py).
|
|
302
|
+
|
|
271
303
|
### OpenAPI Callbacks and Webhooks
|
|
272
304
|
|
|
273
305
|
Route-level **Callbacks** need no special handling: a `callbacks=[...]` argument on a route
|
|
@@ -351,63 +383,18 @@ RouterVersioner(
|
|
|
351
383
|
[`examples/download_static_assets.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/download_static_assets.py) downloads the required files in one step;
|
|
352
384
|
[`examples/self_hosted_docs_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/self_hosted_docs_app.py) wires them into a full app.
|
|
353
385
|
|
|
354
|
-
### Validation error status code
|
|
355
|
-
|
|
356
|
-
`validation_error_code` changes what FastAPI returns for a failed request body/query/path
|
|
357
|
-
validation, `422` by default, both at runtime and in the schema:
|
|
358
|
-
|
|
359
|
-
```python
|
|
360
|
-
RouterVersioner(
|
|
361
|
-
app=app,
|
|
362
|
-
routers=router,
|
|
363
|
-
version_format=VersionFormat.SEMVER,
|
|
364
|
-
validation_error_code=400,
|
|
365
|
-
).versionize()
|
|
366
|
-
# Validation failures now return 400; every 422 entry in the schema becomes 400
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
Set `handle_validation_exceptions=False` to keep the schema change while writing your own
|
|
370
|
-
response body:
|
|
371
|
-
|
|
372
|
-
```python
|
|
373
|
-
from fastapi import Request
|
|
374
|
-
from fastapi.exceptions import RequestValidationError
|
|
375
|
-
from fastapi.responses import JSONResponse
|
|
376
|
-
|
|
377
|
-
@app.exception_handler(RequestValidationError)
|
|
378
|
-
async def my_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
|
|
379
|
-
return JSONResponse(status_code=400, content={"errors": exc.errors()})
|
|
380
|
-
|
|
381
|
-
RouterVersioner(
|
|
382
|
-
app=app,
|
|
383
|
-
routers=router,
|
|
384
|
-
version_format=VersionFormat.SEMVER,
|
|
385
|
-
validation_error_code=400,
|
|
386
|
-
handle_validation_exceptions=False, # schema updated; the handler above owns the response
|
|
387
|
-
).versionize()
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
This also patches the app's own root `/docs`, `/redoc`, and `/openapi.json`, not just the
|
|
391
|
-
versioned ones, so there's nothing left showing the stale `422`.
|
|
392
|
-
|
|
393
386
|
Sharing one app across several `RouterVersioner` instances only makes sense for one reason:
|
|
394
387
|
mixing `version_format` values, SemVer for one group of routes and CalVer for another, on
|
|
395
388
|
the same app. Splitting modules that share a `version_format` doesn't need a second instance;
|
|
396
389
|
pass them all to one `RouterVersioner` via `routers=[...]` instead (see
|
|
397
390
|
[`multi_router_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/multi_router_app.py)).
|
|
398
|
-
If you do share an app across instances,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
router, so a mismatch raises `RuntimeError` when the second instance is constructed rather
|
|
403
|
-
than silently keeping whichever handler was registered first.
|
|
404
|
-
- Every instance needs its own `prefix_format`/`latest_prefix`. Two instances that resolve to
|
|
405
|
-
the same prefix would otherwise overwrite each other's docs/openapi routes at the same
|
|
406
|
-
path; this raises `RuntimeError` too.
|
|
391
|
+
If you do share an app across instances, one rule is enforced for you: every instance needs
|
|
392
|
+
its own `prefix_format`/`latest_prefix`. Two instances that resolve to the same prefix would
|
|
393
|
+
otherwise overwrite each other's docs/openapi routes at the same path; this raises
|
|
394
|
+
`RuntimeError`.
|
|
407
395
|
|
|
408
|
-
For modules that genuinely don't need to coordinate at all,
|
|
409
|
-
|
|
410
|
-
section).
|
|
396
|
+
For modules that genuinely don't need to coordinate at all, mount them as separate FastAPI
|
|
397
|
+
sub-applications instead (next section).
|
|
411
398
|
|
|
412
399
|
### Reverse proxy and sub-application mounting
|
|
413
400
|
|
|
@@ -422,7 +409,7 @@ parent.mount("/api", app) # root_path="/api" is injected per request
|
|
|
422
409
|
|
|
423
410
|
A mounted sub-application is a separate `FastAPI()` instance with its own `app.state`, so a
|
|
424
411
|
`RouterVersioner` attached to it is entirely independent from one attached to the parent, or
|
|
425
|
-
to another sub-application, no
|
|
412
|
+
to another sub-application, no prefix coordination needed.
|
|
426
413
|
See [`examples/mounted_subapps_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/mounted_subapps_app.py).
|
|
427
414
|
|
|
428
415
|
### Callback hook
|
|
@@ -455,9 +442,8 @@ RouterVersioner(
|
|
|
455
442
|
| [`multi_router_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/multi_router_app.py) | Several routers versioned together under one instance |
|
|
456
443
|
| [`self_hosted_docs_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/self_hosted_docs_app.py) | Swagger UI and ReDoc served from local static assets |
|
|
457
444
|
| [`openapi_hook_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/openapi_hook_app.py) | Per-version OpenAPI schema edits via `openapi_hook` |
|
|
458
|
-
| [`validation_error_code_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_error_code_app.py) | `400` instead of `422`, handled automatically by `RouterVersioner` |
|
|
459
|
-
| [`validation_error_code_custom_handler_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_error_code_custom_handler_app.py) | Same, but with `handle_validation_exceptions=False` and a custom handler |
|
|
460
445
|
| [`mounted_subapps_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/mounted_subapps_app.py) | Independently versioned modules as separate `app.mount()` sub-applications |
|
|
446
|
+
| [`validation_override_integration_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/validation_override_integration_app.py) | Custom validation error status code via `fastapi-validation-override` and `openapi_hook` |
|
|
461
447
|
|
|
462
448
|
---
|
|
463
449
|
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Latest Changes
|
|
4
4
|
|
|
5
|
+
## 0.3.0 (2026-07-28)
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
* ♻️ Remove built-in validation error override in favor of fastapi-validation-override. PR [#46](https://github.com/mat81black/fastapi-router-versioning/pull/46) by [@mat81black](https://github.com/mat81black).
|
|
10
|
+
|
|
11
|
+
### Internal
|
|
12
|
+
|
|
13
|
+
* ⬆ Bump fastapi from 0.139.0 to 0.139.2. PR [#49](https://github.com/mat81black/fastapi-router-versioning/pull/49) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
|
14
|
+
* ⬆ Bump the python-packages group with 7 updates. PR [#48](https://github.com/mat81black/fastapi-router-versioning/pull/48) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
|
15
|
+
* ⬆ Bump the github-actions group with 5 updates. PR [#47](https://github.com/mat81black/fastapi-router-versioning/pull/47) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
|
16
|
+
|
|
5
17
|
## 0.2.7 (2026-07-18)
|
|
6
18
|
|
|
7
19
|
### Internal
|
{fastapi_router_versioning-0.2.7 → fastapi_router_versioning-0.3.0}/examples/mounted_subapps_app.py
RENAMED
|
@@ -20,7 +20,7 @@ class CreateItemRequest(BaseModel):
|
|
|
20
20
|
|
|
21
21
|
main_app = FastAPI(title="Main App")
|
|
22
22
|
|
|
23
|
-
# ── Admin sub-app: SemVer
|
|
23
|
+
# ── Admin sub-app: SemVer ──────────────────────────────────────────────────
|
|
24
24
|
|
|
25
25
|
admin_app = FastAPI(title="Admin API")
|
|
26
26
|
admin_router = APIRouter()
|
|
@@ -38,9 +38,7 @@ def create_admin_item(body: CreateItemRequest) -> dict[str, str]:
|
|
|
38
38
|
return {"name": body.name, "quantity": str(body.quantity)}
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
RouterVersioner(
|
|
42
|
-
app=admin_app, routers=admin_router, version_format=VersionFormat.SEMVER, validation_error_code=400
|
|
43
|
-
).versionize()
|
|
41
|
+
RouterVersioner(app=admin_app, routers=admin_router, version_format=VersionFormat.SEMVER).versionize()
|
|
44
42
|
|
|
45
43
|
|
|
46
44
|
# ── Orders sub-app: CalVer ────────────────────────────────────────────────────
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom validation error status via fastapi-validation-override.
|
|
3
|
+
|
|
4
|
+
RouterVersioner does not have a built-in way to change FastAPI's default 422
|
|
5
|
+
status code: that is a separate, general-purpose concern handled by the
|
|
6
|
+
fastapi-validation-override package. This example wires the two together:
|
|
7
|
+
|
|
8
|
+
1. override_validation_error() patches the app once, at runtime and on its own
|
|
9
|
+
root /openapi.json.
|
|
10
|
+
2. openapi_hook re-applies patch_422_responses() to the schema RouterVersioner
|
|
11
|
+
generates for each version, so every versioned /vX_Y/openapi.json reflects
|
|
12
|
+
the same status code too.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from fastapi import APIRouter, FastAPI
|
|
18
|
+
from fastapi_validation_override import override_validation_error, patch_422_responses
|
|
19
|
+
|
|
20
|
+
from fastapi_router_versioning import RouterVersioner, VersionFormat, VersionT, api_version
|
|
21
|
+
|
|
22
|
+
app = FastAPI(title="Validation Override Integration Demo")
|
|
23
|
+
router = APIRouter()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@router.get("/items")
|
|
27
|
+
@api_version((1, 0))
|
|
28
|
+
def get_items_v1(count: int) -> dict[str, str]:
|
|
29
|
+
return {"count": str(count)}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@router.get("/items")
|
|
33
|
+
@api_version((2, 0))
|
|
34
|
+
def get_items_v2(count: int) -> dict[str, str]:
|
|
35
|
+
return {"count": str(count)}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
override_validation_error(app, status_code=400)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def versioning_openapi_hook(schema: dict[str, Any], version: VersionT) -> dict[str, Any]:
|
|
42
|
+
return patch_422_responses(schema, "400")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
versioner = RouterVersioner(
|
|
46
|
+
app=app,
|
|
47
|
+
routers=router,
|
|
48
|
+
version_format=VersionFormat.SEMVER,
|
|
49
|
+
openapi_hook=versioning_openapi_hook,
|
|
50
|
+
)
|
|
51
|
+
versioner.versionize()
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
import uvicorn
|
|
55
|
+
|
|
56
|
+
uvicorn.run(app, host="127.0.0.1", port=8010)
|