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.
Files changed (41) hide show
  1. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.github/workflows/ci.yaml +5 -4
  2. fastapi_error_map-0.9.10/.github/workflows/publish.yaml +59 -0
  3. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.github/workflows/test-compatibility.yaml +5 -3
  4. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.pre-commit-config.yaml +3 -3
  5. fastapi_error_map-0.9.10/Makefile +26 -0
  6. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/PKG-INFO +15 -6
  7. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/README.md +13 -4
  8. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/error_handling.py +11 -5
  9. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/routing.py +103 -1
  10. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/rules.py +8 -3
  11. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/noxfile.py +3 -3
  12. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/pyproject.toml +6 -3
  13. fastapi_error_map-0.9.10/tests/integration/conftest.py +19 -0
  14. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/test_example.py +5 -8
  15. fastapi_error_map-0.9.10/tests/integration/test_exclude_none.py +85 -0
  16. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/test_routing.py +46 -9
  17. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/test_threadpool.py +15 -18
  18. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_error_handling.py +5 -0
  19. fastapi_error_map-0.9.10/tests/unit/test_routing.py +44 -0
  20. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_rules.py +67 -0
  21. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/uv.lock +36 -104
  22. fastapi_error_map-0.9.8/Makefile +0 -23
  23. fastapi_error_map-0.9.8/tests/integration/conftest.py +0 -25
  24. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/.gitignore +0 -0
  25. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/LICENSE +0 -0
  26. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/docs/example-openapi.png +0 -0
  27. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/examples/__init__.py +0 -0
  28. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/examples/errors.py +0 -0
  29. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/examples/main.py +0 -0
  30. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/__init__.py +0 -0
  31. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/openapi.py +0 -0
  32. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/py.typed +0 -0
  33. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/translator_policy.py +0 -0
  34. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/fastapi_error_map/translators.py +0 -0
  35. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/__init__.py +0 -0
  36. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/integration/__init__.py +0 -0
  37. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/__init__.py +0 -0
  38. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/error_stubs.py +0 -0
  39. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_openapi.py +0 -0
  40. {fastapi_error_map-0.9.8 → fastapi_error_map-0.9.10}/tests/unit/test_translators.py +0 -0
  41. {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
- - uses: actions/checkout@v4
10
+ - name: Checkout
11
+ uses: actions/checkout@v6
11
12
 
12
13
  - name: Set up Python
13
- uses: actions/setup-python@v5
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@v6
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 code.lint
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
@@ -23,14 +23,16 @@ jobs:
23
23
  - "3.14"
24
24
 
25
25
  steps:
26
- - uses: actions/checkout@v4
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@v5
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@v6
35
+ uses: astral-sh/setup-uv@v7
34
36
 
35
37
  - name: Run tests
36
38
  run: uvx nox -s compatibility
@@ -1,9 +1,9 @@
1
1
  repos:
2
2
  - repo: local
3
3
  hooks:
4
- - id: make-check
5
- name: source-code-check
6
- entry: make code.check
4
+ - id: code-check
5
+ name: code-check (local)
6
+ entry: uv run make check
7
7
  language: system
8
8
  pass_filenames: false
9
9
  always_run: true
@@ -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.8
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: orjson>=3.11.4
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` matches **exact** exception types only (no inheritance).
173
- If you map `BaseError` and raise `ChildError(BaseError)`, the rule won’t apply.
174
- This is by design to keep routing explicit.
175
- If there’s demand, inheritance-based resolving may be added later as an opt-in.
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` matches **exact** exception types only (no inheritance).
146
- If you map `BaseError` and raise `ChildError(BaseError)`, the rule won’t apply.
147
- This is by design to keep routing explicit.
148
- If there’s demand, inheritance-based resolving may be added later as an opt-in.
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(*args, **kwargs)
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
- ) -> ORJSONResponse:
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 ORJSONResponse(
79
+ return JSONResponse(
77
80
  status_code=rule.status,
78
- content=jsonable_encoder(content),
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
  )