fastapi-error-map 0.9.8__tar.gz → 0.9.10__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_error_map-0.9.8 → fastapi_error_map-0.9.10}/.github/workflows/ci.yaml +5 -4
- fastapi_error_map-0.9.10/.github/workflows/publish.yaml +59 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.github/workflows/test-compatibility.yaml +5 -3
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.pre-commit-config.yaml +3 -3
- fastapi_error_map-0.9.10/Makefile +26 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/PKG-INFO +15 -6
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/README.md +13 -4
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/error_handling.py +11 -5
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/routing.py +103 -1
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/rules.py +8 -3
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/noxfile.py +3 -3
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/pyproject.toml +6 -3
- fastapi_error_map-0.9.10/tests/integration/conftest.py +19 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/test_example.py +5 -8
- fastapi_error_map-0.9.10/tests/integration/test_exclude_none.py +85 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/test_routing.py +46 -9
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/test_threadpool.py +15 -18
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_error_handling.py +5 -0
- fastapi_error_map-0.9.10/tests/unit/test_routing.py +44 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_rules.py +67 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/uv.lock +36 -104
- fastapi_error_map-0.9.8/Makefile +0 -23
- fastapi_error_map-0.9.8/tests/integration/conftest.py +0 -25
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.gitignore +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/LICENSE +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/docs/example-openapi.png +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/examples/__init__.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/examples/errors.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/examples/main.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/__init__.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/openapi.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/py.typed +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/translator_policy.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/translators.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/__init__.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/__init__.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/__init__.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/error_stubs.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_openapi.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_translators.py +0 -0
- {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/translator_stubs.py +0 -0
|
@@ -7,21 +7,22 @@ jobs:
|
|
|
7
7
|
runs-on: ubuntu-latest
|
|
8
8
|
|
|
9
9
|
steps:
|
|
10
|
-
-
|
|
10
|
+
- name: Checkout
|
|
11
|
+
uses: actions/checkout@v6
|
|
11
12
|
|
|
12
13
|
- name: Set up Python
|
|
13
|
-
uses: actions/setup-python@
|
|
14
|
+
uses: actions/setup-python@v6
|
|
14
15
|
with:
|
|
15
16
|
python-version: "3.9"
|
|
16
17
|
|
|
17
18
|
- name: Install uv
|
|
18
|
-
uses: astral-sh/setup-uv@
|
|
19
|
+
uses: astral-sh/setup-uv@v7
|
|
19
20
|
|
|
20
21
|
- name: Install dependencies
|
|
21
22
|
run: uv sync --locked --group all
|
|
22
23
|
|
|
23
24
|
- name: Lint code
|
|
24
|
-
run: uv run make
|
|
25
|
+
run: uv run make lint
|
|
25
26
|
|
|
26
27
|
- name: Run tests for Codecov
|
|
27
28
|
run: uv run pytest --cov=fastapi_error_map --cov-branch --cov-report=xml
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Publish to PyPI on GitHub Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [ published ]
|
|
6
|
+
|
|
7
|
+
env:
|
|
8
|
+
UV_PYTHON_DOWNLOADS: 0
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
name: Build distribution
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions: { }
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
persist-credentials: false
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v6
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.13"
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v7
|
|
28
|
+
|
|
29
|
+
- name: Build
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Upload artifact
|
|
33
|
+
uses: actions/upload-artifact@v6
|
|
34
|
+
with:
|
|
35
|
+
name: python-package-distributions
|
|
36
|
+
path: dist/*
|
|
37
|
+
|
|
38
|
+
publish:
|
|
39
|
+
name: Publish to PyPI (OIDC)
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment:
|
|
43
|
+
name: pypi
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
contents: read
|
|
47
|
+
|
|
48
|
+
steps:
|
|
49
|
+
- name: Download artifact
|
|
50
|
+
uses: actions/download-artifact@v7
|
|
51
|
+
with:
|
|
52
|
+
name: python-package-distributions
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
- name: Install uv
|
|
56
|
+
uses: astral-sh/setup-uv@v7
|
|
57
|
+
|
|
58
|
+
- name: Publish
|
|
59
|
+
run: uv publish
|
{fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.github/workflows/test-compatibility.yaml
RENAMED
|
@@ -23,14 +23,16 @@ jobs:
|
|
|
23
23
|
- "3.14"
|
|
24
24
|
|
|
25
25
|
steps:
|
|
26
|
-
-
|
|
26
|
+
- name: Checkout
|
|
27
|
+
uses: actions/checkout@v6
|
|
28
|
+
|
|
27
29
|
- name: Set up ${{ matrix.python-version }} on ${{ matrix.os }}
|
|
28
|
-
uses: actions/setup-python@
|
|
30
|
+
uses: actions/setup-python@v6
|
|
29
31
|
with:
|
|
30
32
|
python-version: ${{ matrix.python-version }}
|
|
31
33
|
|
|
32
34
|
- name: Install uv
|
|
33
|
-
uses: astral-sh/setup-uv@
|
|
35
|
+
uses: astral-sh/setup-uv@v7
|
|
34
36
|
|
|
35
37
|
- name: Run tests
|
|
36
38
|
run: uvx nox -s compatibility
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Make config
|
|
2
|
+
.SILENT:
|
|
3
|
+
MAKEFLAGS += --no-print-directory
|
|
4
|
+
|
|
5
|
+
# Code quality
|
|
6
|
+
.PHONY: lint test check coverage
|
|
7
|
+
lint:
|
|
8
|
+
ruff check --fix
|
|
9
|
+
ruff format
|
|
10
|
+
mypy
|
|
11
|
+
|
|
12
|
+
test:
|
|
13
|
+
pytest -v \
|
|
14
|
+
--cov=fastapi_error_map \
|
|
15
|
+
--cov-report=term-missing
|
|
16
|
+
|
|
17
|
+
check: lint test
|
|
18
|
+
|
|
19
|
+
coverage: check
|
|
20
|
+
coverage html
|
|
21
|
+
|
|
22
|
+
# Project structure visualization
|
|
23
|
+
.PHONY: pycache-del
|
|
24
|
+
pycache-del:
|
|
25
|
+
find . -type d -name '__pycache__' -prune -exec rm -rf {} +; \
|
|
26
|
+
find . -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-error-map
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.10
|
|
4
4
|
Summary: Elegant per-endpoint error handling for FastAPI that keeps OpenAPI in sync
|
|
5
5
|
Project-URL: Homepage, https://github.com/ivan-borovets/fastapi-error-map
|
|
6
6
|
Project-URL: Repository, https://github.com/ivan-borovets/fastapi-error-map
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
22
22
|
Classifier: Typing :: Typed
|
|
23
23
|
Requires-Python: >=3.9
|
|
24
24
|
Requires-Dist: fastapi<1.0,>=0.100
|
|
25
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: packaging>=21.0
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
|
|
28
28
|
## FastAPI Error Map
|
|
@@ -169,10 +169,13 @@ Parameters of `rule(...)`, * — required:
|
|
|
169
169
|
|
|
170
170
|
#### 🧩 Matching semantics
|
|
171
171
|
|
|
172
|
-
`error_map`
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
`error_map` resolves exceptions using Python's Method Resolution Order (MRO).
|
|
173
|
+
The most specific exception type is matched first.
|
|
174
|
+
If no exact match is found, parent classes are checked in MRO order.
|
|
175
|
+
|
|
176
|
+
For example, if you map `BaseError` and raise `ChildError(BaseError)`,
|
|
177
|
+
the rule for `BaseError` will apply — unless a specific rule for
|
|
178
|
+
`ChildError` is defined, in which case it takes precedence.
|
|
176
179
|
|
|
177
180
|
### 🧰 Custom Translators
|
|
178
181
|
|
|
@@ -271,6 +274,7 @@ In addition to `error_map`, you can also pass:
|
|
|
271
274
|
warn_on_unmapped=...,
|
|
272
275
|
default_client_error_translator=...,
|
|
273
276
|
default_server_error_translator=...,
|
|
277
|
+
exclude_none=...,
|
|
274
278
|
)
|
|
275
279
|
```
|
|
276
280
|
|
|
@@ -299,6 +303,11 @@ When an error occurs, `fastapi-error-map` processes it as follows:
|
|
|
299
303
|
- Otherwise, `default_on_error` is used if provided
|
|
300
304
|
- If neither is set, nothing is called
|
|
301
305
|
|
|
306
|
+
4. `exclude_none`:
|
|
307
|
+
- If `True`, fields with value `None` are omitted from the serialized
|
|
308
|
+
error response body
|
|
309
|
+
- If `False` (default), `None` values are included as `null`
|
|
310
|
+
|
|
302
311
|
#### 🧾 OpenAPI: `responses` Takes Priority
|
|
303
312
|
|
|
304
313
|
If you explicitly pass the `responses=...` parameter to `.get(...)` / `.post(...)`, it overrides the schema generation
|
|
@@ -142,10 +142,13 @@ Parameters of `rule(...)`, * — required:
|
|
|
142
142
|
|
|
143
143
|
#### 🧩 Matching semantics
|
|
144
144
|
|
|
145
|
-
`error_map`
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
`error_map` resolves exceptions using Python's Method Resolution Order (MRO).
|
|
146
|
+
The most specific exception type is matched first.
|
|
147
|
+
If no exact match is found, parent classes are checked in MRO order.
|
|
148
|
+
|
|
149
|
+
For example, if you map `BaseError` and raise `ChildError(BaseError)`,
|
|
150
|
+
the rule for `BaseError` will apply — unless a specific rule for
|
|
151
|
+
`ChildError` is defined, in which case it takes precedence.
|
|
149
152
|
|
|
150
153
|
### 🧰 Custom Translators
|
|
151
154
|
|
|
@@ -244,6 +247,7 @@ In addition to `error_map`, you can also pass:
|
|
|
244
247
|
warn_on_unmapped=...,
|
|
245
248
|
default_client_error_translator=...,
|
|
246
249
|
default_server_error_translator=...,
|
|
250
|
+
exclude_none=...,
|
|
247
251
|
)
|
|
248
252
|
```
|
|
249
253
|
|
|
@@ -272,6 +276,11 @@ When an error occurs, `fastapi-error-map` processes it as follows:
|
|
|
272
276
|
- Otherwise, `default_on_error` is used if provided
|
|
273
277
|
- If neither is set, nothing is called
|
|
274
278
|
|
|
279
|
+
4. `exclude_none`:
|
|
280
|
+
- If `True`, fields with value `None` are omitted from the serialized
|
|
281
|
+
error response body
|
|
282
|
+
- If `False` (default), `None` values are included as `null`
|
|
283
|
+
|
|
275
284
|
#### 🧾 OpenAPI: `responses` Takes Priority
|
|
276
285
|
|
|
277
286
|
If you explicitly pass the `responses=...` parameter to `.get(...)` / `.post(...)`, it overrides the schema generation
|
|
@@ -4,8 +4,8 @@ from functools import wraps
|
|
|
4
4
|
from typing import Any, Callable, Optional, Union
|
|
5
5
|
|
|
6
6
|
from fastapi.encoders import jsonable_encoder
|
|
7
|
-
from fastapi.responses import ORJSONResponse
|
|
8
7
|
from starlette.concurrency import run_in_threadpool
|
|
8
|
+
from starlette.responses import JSONResponse
|
|
9
9
|
|
|
10
10
|
from fastapi_error_map.rules import ErrorMap, resolve_rule_for_error
|
|
11
11
|
from fastapi_error_map.translators import ErrorTranslator
|
|
@@ -19,6 +19,7 @@ def wrap_with_error_handling(
|
|
|
19
19
|
default_client_error_translator: ErrorTranslator[Any],
|
|
20
20
|
default_server_error_translator: ErrorTranslator[Any],
|
|
21
21
|
default_on_error: Optional[Callable[[Exception], Union[Awaitable[None], None]]],
|
|
22
|
+
exclude_none: bool = False,
|
|
22
23
|
) -> Callable[..., Any]:
|
|
23
24
|
is_coro = inspect.iscoroutinefunction(func)
|
|
24
25
|
|
|
@@ -27,7 +28,7 @@ def wrap_with_error_handling(
|
|
|
27
28
|
try:
|
|
28
29
|
if is_coro:
|
|
29
30
|
return await func(*args, **kwargs)
|
|
30
|
-
return func
|
|
31
|
+
return await run_in_threadpool(func, *args, **kwargs)
|
|
31
32
|
except Exception as error:
|
|
32
33
|
return await handle_with_error_map(
|
|
33
34
|
error=error,
|
|
@@ -36,6 +37,7 @@ def wrap_with_error_handling(
|
|
|
36
37
|
default_client_error_translator=default_client_error_translator,
|
|
37
38
|
default_server_error_translator=default_server_error_translator,
|
|
38
39
|
default_on_error=default_on_error,
|
|
40
|
+
exclude_none=exclude_none,
|
|
39
41
|
)
|
|
40
42
|
|
|
41
43
|
return wrapped
|
|
@@ -49,7 +51,8 @@ async def handle_with_error_map(
|
|
|
49
51
|
default_client_error_translator: ErrorTranslator[Any],
|
|
50
52
|
default_server_error_translator: ErrorTranslator[Any],
|
|
51
53
|
default_on_error: Optional[Callable[[Exception], Union[Awaitable[None], None]]],
|
|
52
|
-
|
|
54
|
+
exclude_none: bool,
|
|
55
|
+
) -> JSONResponse:
|
|
53
56
|
try:
|
|
54
57
|
rule = resolve_rule_for_error(
|
|
55
58
|
error=error,
|
|
@@ -73,7 +76,10 @@ async def handle_with_error_map(
|
|
|
73
76
|
await result
|
|
74
77
|
|
|
75
78
|
content = rule.translator.from_error(error)
|
|
76
|
-
return
|
|
79
|
+
return JSONResponse(
|
|
77
80
|
status_code=rule.status,
|
|
78
|
-
content=jsonable_encoder(
|
|
81
|
+
content=jsonable_encoder(
|
|
82
|
+
content,
|
|
83
|
+
exclude_none=exclude_none,
|
|
84
|
+
),
|
|
79
85
|
)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from collections.abc import Sequence
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from typing import (
|
|
4
4
|
Any,
|
|
5
5
|
Callable,
|
|
6
6
|
Dict,
|
|
7
|
+
Final,
|
|
7
8
|
List,
|
|
8
9
|
Optional,
|
|
9
10
|
Set,
|
|
@@ -11,6 +12,7 @@ from typing import (
|
|
|
11
12
|
Union,
|
|
12
13
|
)
|
|
13
14
|
|
|
15
|
+
import fastapi
|
|
14
16
|
from fastapi import params
|
|
15
17
|
from fastapi.datastructures import Default, DefaultPlaceholder
|
|
16
18
|
from fastapi.routing import APIRoute, APIRouter
|
|
@@ -19,6 +21,7 @@ from fastapi.utils import (
|
|
|
19
21
|
generate_unique_id,
|
|
20
22
|
get_value_or_default,
|
|
21
23
|
)
|
|
24
|
+
from packaging.version import Version
|
|
22
25
|
from starlette.responses import JSONResponse, Response
|
|
23
26
|
from starlette.routing import (
|
|
24
27
|
BaseRoute,
|
|
@@ -35,6 +38,35 @@ from fastapi_error_map.translators import (
|
|
|
35
38
|
ErrorTranslator,
|
|
36
39
|
)
|
|
37
40
|
|
|
41
|
+
_FASTAPI_VERSION: Final[Version] = Version(fastapi.__version__)
|
|
42
|
+
_HAS_STRICT_CONTENT_TYPE: Final[bool] = _FASTAPI_VERSION >= Version("0.132.0") # noqa: SIM300
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _with_strict_content_type(
|
|
46
|
+
strict_content_type: Union[bool, DefaultPlaceholder],
|
|
47
|
+
kwargs: Mapping[str, Any],
|
|
48
|
+
) -> Dict[str, Any]:
|
|
49
|
+
"""
|
|
50
|
+
Returns a new kwargs dict with 'strict_content_type' added only when supported.
|
|
51
|
+
|
|
52
|
+
FastAPI >= 0.132.0:
|
|
53
|
+
- APIRouter/APIRoute accept `strict_content_type=...`
|
|
54
|
+
|
|
55
|
+
FastAPI < 0.132.0:
|
|
56
|
+
- passing strict_content_type as an explicit bool would crash at runtime
|
|
57
|
+
- Default(True) is the safe sentinel (means "don't pass anything")
|
|
58
|
+
"""
|
|
59
|
+
out = dict(kwargs)
|
|
60
|
+
if _HAS_STRICT_CONTENT_TYPE:
|
|
61
|
+
out["strict_content_type"] = strict_content_type
|
|
62
|
+
return out
|
|
63
|
+
if not isinstance(strict_content_type, DefaultPlaceholder):
|
|
64
|
+
raise TypeError(
|
|
65
|
+
f"'strict_content_type' requires FastAPI >=0.132.0 "
|
|
66
|
+
f"(installed: {fastapi.__version__})"
|
|
67
|
+
)
|
|
68
|
+
return out
|
|
69
|
+
|
|
38
70
|
|
|
39
71
|
class ErrorAwareRoute(APIRoute):
|
|
40
72
|
def __init__(
|
|
@@ -48,6 +80,7 @@ class ErrorAwareRoute(APIRoute):
|
|
|
48
80
|
warn_on_unmapped: bool = True,
|
|
49
81
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
50
82
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
83
|
+
exclude_none: bool = False,
|
|
51
84
|
# --- FastAPI ---
|
|
52
85
|
response_model: Any = Default(None),
|
|
53
86
|
status_code: Optional[int] = None,
|
|
@@ -77,6 +110,8 @@ class ErrorAwareRoute(APIRoute):
|
|
|
77
110
|
generate_unique_id_function: Union[
|
|
78
111
|
Callable[["APIRoute"], str], DefaultPlaceholder
|
|
79
112
|
] = Default(generate_unique_id),
|
|
113
|
+
strict_content_type: Union[bool, DefaultPlaceholder] = Default(True),
|
|
114
|
+
**kwargs: Any,
|
|
80
115
|
) -> None:
|
|
81
116
|
self.error_map = error_map if error_map is not None else {}
|
|
82
117
|
self.default_on_error = default_on_error
|
|
@@ -98,6 +133,7 @@ class ErrorAwareRoute(APIRoute):
|
|
|
98
133
|
default_on_error=self.default_on_error,
|
|
99
134
|
default_client_error_translator=self.default_client_error_translator,
|
|
100
135
|
default_server_error_translator=self.default_server_error_translator,
|
|
136
|
+
exclude_none=exclude_none,
|
|
101
137
|
)
|
|
102
138
|
responses = {
|
|
103
139
|
**build_openapi_responses(
|
|
@@ -107,6 +143,9 @@ class ErrorAwareRoute(APIRoute):
|
|
|
107
143
|
),
|
|
108
144
|
**(responses if responses is not None else {}),
|
|
109
145
|
}
|
|
146
|
+
kwargs_with_strict_content_type = _with_strict_content_type(
|
|
147
|
+
strict_content_type, kwargs
|
|
148
|
+
)
|
|
110
149
|
super().__init__(
|
|
111
150
|
path,
|
|
112
151
|
endpoint,
|
|
@@ -134,6 +173,7 @@ class ErrorAwareRoute(APIRoute):
|
|
|
134
173
|
callbacks=callbacks,
|
|
135
174
|
openapi_extra=openapi_extra,
|
|
136
175
|
generate_unique_id_function=generate_unique_id_function,
|
|
176
|
+
**kwargs_with_strict_content_type,
|
|
137
177
|
)
|
|
138
178
|
|
|
139
179
|
|
|
@@ -373,7 +413,28 @@ class ErrorAwareRouter(APIRouter):
|
|
|
373
413
|
"""
|
|
374
414
|
),
|
|
375
415
|
] = Default(generate_unique_id),
|
|
416
|
+
strict_content_type: Annotated[
|
|
417
|
+
bool,
|
|
418
|
+
Doc(
|
|
419
|
+
"""
|
|
420
|
+
Enable strict checking for request Content-Type headers.
|
|
421
|
+
|
|
422
|
+
When `True` (the default), requests with a body that do not include
|
|
423
|
+
a `Content-Type` header will **not** be parsed as JSON.
|
|
424
|
+
|
|
425
|
+
When `False`, requests without a `Content-Type` header will have
|
|
426
|
+
their body parsed as JSON.
|
|
427
|
+
|
|
428
|
+
Read more about it in the
|
|
429
|
+
[FastAPI docs for Strict Content-Type](https://fastapi.tiangolo.com/advanced/strict-content-type/).
|
|
430
|
+
"""
|
|
431
|
+
),
|
|
432
|
+
] = Default(True),
|
|
433
|
+
**kwargs: Any,
|
|
376
434
|
) -> None:
|
|
435
|
+
kwargs_with_strict_content_type = _with_strict_content_type(
|
|
436
|
+
strict_content_type, kwargs
|
|
437
|
+
)
|
|
377
438
|
super().__init__(
|
|
378
439
|
prefix=prefix,
|
|
379
440
|
tags=tags,
|
|
@@ -392,6 +453,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
392
453
|
deprecated=deprecated,
|
|
393
454
|
include_in_schema=include_in_schema,
|
|
394
455
|
generate_unique_id_function=generate_unique_id_function,
|
|
456
|
+
**kwargs_with_strict_content_type,
|
|
395
457
|
)
|
|
396
458
|
|
|
397
459
|
def add_api_route(
|
|
@@ -405,6 +467,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
405
467
|
warn_on_unmapped: bool = True,
|
|
406
468
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
407
469
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
470
|
+
exclude_none: bool = False,
|
|
408
471
|
# --- FastAPI ---
|
|
409
472
|
response_model: Any = Default(None),
|
|
410
473
|
status_code: Optional[int] = None,
|
|
@@ -434,6 +497,8 @@ class ErrorAwareRouter(APIRouter):
|
|
|
434
497
|
generate_unique_id_function: Union[
|
|
435
498
|
Callable[[APIRoute], str], DefaultPlaceholder
|
|
436
499
|
] = Default(generate_unique_id),
|
|
500
|
+
strict_content_type: Union[bool, DefaultPlaceholder] = Default(True),
|
|
501
|
+
**kwargs: Any,
|
|
437
502
|
) -> None:
|
|
438
503
|
route_class = route_class_override or self.route_class
|
|
439
504
|
responses = responses or {}
|
|
@@ -453,6 +518,16 @@ class ErrorAwareRouter(APIRouter):
|
|
|
453
518
|
current_generate_unique_id = get_value_or_default(
|
|
454
519
|
generate_unique_id_function, self.generate_unique_id_function
|
|
455
520
|
)
|
|
521
|
+
if _HAS_STRICT_CONTENT_TYPE:
|
|
522
|
+
current_strict_content_type = get_value_or_default(
|
|
523
|
+
strict_content_type,
|
|
524
|
+
getattr(self, "strict_content_type", Default(True)),
|
|
525
|
+
)
|
|
526
|
+
else:
|
|
527
|
+
current_strict_content_type = strict_content_type
|
|
528
|
+
kwargs_with_strict_content_type = _with_strict_content_type(
|
|
529
|
+
current_strict_content_type, kwargs
|
|
530
|
+
)
|
|
456
531
|
if issubclass(route_class, ErrorAwareRoute):
|
|
457
532
|
route: APIRoute = route_class(
|
|
458
533
|
self.prefix + path,
|
|
@@ -462,6 +537,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
462
537
|
default_on_error=default_on_error,
|
|
463
538
|
default_client_error_translator=default_client_error_translator,
|
|
464
539
|
default_server_error_translator=default_server_error_translator,
|
|
540
|
+
exclude_none=exclude_none,
|
|
465
541
|
response_model=response_model,
|
|
466
542
|
status_code=status_code,
|
|
467
543
|
tags=current_tags,
|
|
@@ -486,6 +562,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
486
562
|
callbacks=current_callbacks,
|
|
487
563
|
openapi_extra=openapi_extra,
|
|
488
564
|
generate_unique_id_function=current_generate_unique_id,
|
|
565
|
+
**kwargs_with_strict_content_type,
|
|
489
566
|
)
|
|
490
567
|
else:
|
|
491
568
|
route = route_class(
|
|
@@ -515,6 +592,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
515
592
|
callbacks=current_callbacks,
|
|
516
593
|
openapi_extra=openapi_extra,
|
|
517
594
|
generate_unique_id_function=current_generate_unique_id,
|
|
595
|
+
**kwargs_with_strict_content_type,
|
|
518
596
|
)
|
|
519
597
|
self.routes.append(route)
|
|
520
598
|
|
|
@@ -528,6 +606,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
528
606
|
warn_on_unmapped: bool = True,
|
|
529
607
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
530
608
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
609
|
+
exclude_none: bool = False,
|
|
531
610
|
# --- FastAPI ---
|
|
532
611
|
response_model: Any = Default(None),
|
|
533
612
|
status_code: Optional[int] = None,
|
|
@@ -554,6 +633,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
554
633
|
generate_unique_id_function: Callable[[APIRoute], str] = Default(
|
|
555
634
|
generate_unique_id
|
|
556
635
|
),
|
|
636
|
+
**kwargs: Any,
|
|
557
637
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
|
558
638
|
def decorator(func: DecoratedCallable) -> DecoratedCallable:
|
|
559
639
|
self.add_api_route(
|
|
@@ -564,6 +644,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
564
644
|
default_on_error=default_on_error,
|
|
565
645
|
default_client_error_translator=default_client_error_translator,
|
|
566
646
|
default_server_error_translator=default_server_error_translator,
|
|
647
|
+
exclude_none=exclude_none,
|
|
567
648
|
response_model=response_model,
|
|
568
649
|
status_code=status_code,
|
|
569
650
|
tags=tags,
|
|
@@ -587,6 +668,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
587
668
|
callbacks=callbacks,
|
|
588
669
|
openapi_extra=openapi_extra,
|
|
589
670
|
generate_unique_id_function=generate_unique_id_function,
|
|
671
|
+
**kwargs,
|
|
590
672
|
)
|
|
591
673
|
return func
|
|
592
674
|
|
|
@@ -612,6 +694,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
612
694
|
warn_on_unmapped: bool = True,
|
|
613
695
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
614
696
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
697
|
+
exclude_none: bool = False,
|
|
615
698
|
# --- FastAPI ---
|
|
616
699
|
response_model: Annotated[
|
|
617
700
|
Any,
|
|
@@ -931,6 +1014,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
931
1014
|
"""
|
|
932
1015
|
),
|
|
933
1016
|
] = Default(generate_unique_id),
|
|
1017
|
+
**kwargs: Any,
|
|
934
1018
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
|
935
1019
|
"""
|
|
936
1020
|
Add a *path operation* using an HTTP GET operation.
|
|
@@ -957,6 +1041,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
957
1041
|
default_on_error=default_on_error,
|
|
958
1042
|
default_client_error_translator=default_client_error_translator,
|
|
959
1043
|
default_server_error_translator=default_server_error_translator,
|
|
1044
|
+
exclude_none=exclude_none,
|
|
960
1045
|
response_model=response_model,
|
|
961
1046
|
status_code=status_code,
|
|
962
1047
|
tags=tags,
|
|
@@ -980,6 +1065,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
980
1065
|
callbacks=callbacks,
|
|
981
1066
|
openapi_extra=openapi_extra,
|
|
982
1067
|
generate_unique_id_function=generate_unique_id_function,
|
|
1068
|
+
**kwargs,
|
|
983
1069
|
)
|
|
984
1070
|
|
|
985
1071
|
def post(
|
|
@@ -1002,6 +1088,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1002
1088
|
warn_on_unmapped: bool = True,
|
|
1003
1089
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
1004
1090
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
1091
|
+
exclude_none: bool = False,
|
|
1005
1092
|
# --- FastAPI ---
|
|
1006
1093
|
response_model: Annotated[
|
|
1007
1094
|
Any,
|
|
@@ -1321,6 +1408,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1321
1408
|
"""
|
|
1322
1409
|
),
|
|
1323
1410
|
] = Default(generate_unique_id),
|
|
1411
|
+
**kwargs: Any,
|
|
1324
1412
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
|
1325
1413
|
"""
|
|
1326
1414
|
Add a *path operation* using an HTTP POST operation.
|
|
@@ -1352,6 +1440,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1352
1440
|
default_on_error=default_on_error,
|
|
1353
1441
|
default_client_error_translator=default_client_error_translator,
|
|
1354
1442
|
default_server_error_translator=default_server_error_translator,
|
|
1443
|
+
exclude_none=exclude_none,
|
|
1355
1444
|
response_model=response_model,
|
|
1356
1445
|
status_code=status_code,
|
|
1357
1446
|
tags=tags,
|
|
@@ -1375,6 +1464,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1375
1464
|
callbacks=callbacks,
|
|
1376
1465
|
openapi_extra=openapi_extra,
|
|
1377
1466
|
generate_unique_id_function=generate_unique_id_function,
|
|
1467
|
+
**kwargs,
|
|
1378
1468
|
)
|
|
1379
1469
|
|
|
1380
1470
|
def put(
|
|
@@ -1397,6 +1487,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1397
1487
|
warn_on_unmapped: bool = True,
|
|
1398
1488
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
1399
1489
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
1490
|
+
exclude_none: bool = False,
|
|
1400
1491
|
# --- FastAPI ---
|
|
1401
1492
|
response_model: Annotated[
|
|
1402
1493
|
Any,
|
|
@@ -1716,6 +1807,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1716
1807
|
"""
|
|
1717
1808
|
),
|
|
1718
1809
|
] = Default(generate_unique_id),
|
|
1810
|
+
**kwargs: Any,
|
|
1719
1811
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
|
1720
1812
|
"""
|
|
1721
1813
|
Add a *path operation* using an HTTP PUT operation.
|
|
@@ -1747,6 +1839,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1747
1839
|
default_on_error=default_on_error,
|
|
1748
1840
|
default_client_error_translator=default_client_error_translator,
|
|
1749
1841
|
default_server_error_translator=default_server_error_translator,
|
|
1842
|
+
exclude_none=exclude_none,
|
|
1750
1843
|
response_model=response_model,
|
|
1751
1844
|
status_code=status_code,
|
|
1752
1845
|
tags=tags,
|
|
@@ -1770,6 +1863,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1770
1863
|
callbacks=callbacks,
|
|
1771
1864
|
openapi_extra=openapi_extra,
|
|
1772
1865
|
generate_unique_id_function=generate_unique_id_function,
|
|
1866
|
+
**kwargs,
|
|
1773
1867
|
)
|
|
1774
1868
|
|
|
1775
1869
|
def patch(
|
|
@@ -1792,6 +1886,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
1792
1886
|
warn_on_unmapped: bool = True,
|
|
1793
1887
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
1794
1888
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
1889
|
+
exclude_none: bool = False,
|
|
1795
1890
|
# --- FastAPI ---
|
|
1796
1891
|
response_model: Annotated[
|
|
1797
1892
|
Any,
|
|
@@ -2111,6 +2206,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
2111
2206
|
"""
|
|
2112
2207
|
),
|
|
2113
2208
|
] = Default(generate_unique_id),
|
|
2209
|
+
**kwargs: Any,
|
|
2114
2210
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
|
2115
2211
|
"""
|
|
2116
2212
|
Add a *path operation* using an HTTP PATCH operation.
|
|
@@ -2142,6 +2238,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
2142
2238
|
default_on_error=default_on_error,
|
|
2143
2239
|
default_client_error_translator=default_client_error_translator,
|
|
2144
2240
|
default_server_error_translator=default_server_error_translator,
|
|
2241
|
+
exclude_none=exclude_none,
|
|
2145
2242
|
response_model=response_model,
|
|
2146
2243
|
status_code=status_code,
|
|
2147
2244
|
tags=tags,
|
|
@@ -2165,6 +2262,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
2165
2262
|
callbacks=callbacks,
|
|
2166
2263
|
openapi_extra=openapi_extra,
|
|
2167
2264
|
generate_unique_id_function=generate_unique_id_function,
|
|
2265
|
+
**kwargs,
|
|
2168
2266
|
)
|
|
2169
2267
|
|
|
2170
2268
|
def delete(
|
|
@@ -2187,6 +2285,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
2187
2285
|
warn_on_unmapped: bool = True,
|
|
2188
2286
|
default_client_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
2189
2287
|
default_server_error_translator: Optional[ErrorTranslator[Any]] = None,
|
|
2288
|
+
exclude_none: bool = False,
|
|
2190
2289
|
# --- FastAPI ---
|
|
2191
2290
|
response_model: Annotated[
|
|
2192
2291
|
Any,
|
|
@@ -2506,6 +2605,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
2506
2605
|
"""
|
|
2507
2606
|
),
|
|
2508
2607
|
] = Default(generate_unique_id),
|
|
2608
|
+
**kwargs: Any,
|
|
2509
2609
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
|
2510
2610
|
"""
|
|
2511
2611
|
Add a *path operation* using an HTTP DELETE operation.
|
|
@@ -2532,6 +2632,7 @@ class ErrorAwareRouter(APIRouter):
|
|
|
2532
2632
|
default_on_error=default_on_error,
|
|
2533
2633
|
default_client_error_translator=default_client_error_translator,
|
|
2534
2634
|
default_server_error_translator=default_server_error_translator,
|
|
2635
|
+
exclude_none=exclude_none,
|
|
2535
2636
|
response_model=response_model,
|
|
2536
2637
|
status_code=status_code,
|
|
2537
2638
|
tags=tags,
|
|
@@ -2555,4 +2656,5 @@ class ErrorAwareRouter(APIRouter):
|
|
|
2555
2656
|
callbacks=callbacks,
|
|
2556
2657
|
openapi_extra=openapi_extra,
|
|
2557
2658
|
generate_unique_id_function=generate_unique_id_function,
|
|
2659
|
+
**kwargs,
|
|
2558
2660
|
)
|