fastapi-error-map 0.9.1__tar.gz → 0.9.3__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 (37) hide show
  1. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/PKG-INFO +31 -6
  2. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/README.md +29 -4
  3. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/fastapi_error_map/error_handling.py +5 -2
  4. fastapi_error_map-0.9.3/fastapi_error_map/openapi.py +34 -0
  5. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/fastapi_error_map/routing.py +5 -1
  6. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/fastapi_error_map/rules.py +8 -6
  7. fastapi_error_map-0.9.3/fastapi_error_map/translator_policy.py +22 -0
  8. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/fastapi_error_map/translators.py +1 -1
  9. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/pyproject.toml +2 -2
  10. fastapi_error_map-0.9.3/tests/unit/test_openapi.py +65 -0
  11. fastapi_error_map-0.9.3/tests/unit/translator_stubs.py +40 -0
  12. fastapi_error_map-0.9.1/fastapi_error_map/openapi.py +0 -26
  13. fastapi_error_map-0.9.1/tests/unit/test_openapi.py +0 -48
  14. fastapi_error_map-0.9.1/tests/unit/translator_stubs.py +0 -28
  15. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/.github/workflows/ci.yaml +0 -0
  16. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/.github/workflows/test-compatibility.yaml +0 -0
  17. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/.gitignore +0 -0
  18. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/.pre-commit-config.yaml +0 -0
  19. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/LICENSE +0 -0
  20. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/Makefile +0 -0
  21. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/docs/example-openapi.png +0 -0
  22. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/examples/__init__.py +0 -0
  23. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/examples/errors.py +0 -0
  24. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/examples/main.py +0 -0
  25. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/fastapi_error_map/__init__.py +0 -0
  26. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/fastapi_error_map/py.typed +0 -0
  27. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/noxfile.py +0 -0
  28. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/__init__.py +0 -0
  29. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/integration/__init__.py +0 -0
  30. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/integration/test_example.py +0 -0
  31. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/integration/test_routing.py +0 -0
  32. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/unit/__init__.py +0 -0
  33. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/unit/error_stubs.py +0 -0
  34. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/unit/test_error_handling.py +0 -0
  35. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/unit/test_rules.py +0 -0
  36. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/tests/unit/test_translators.py +0 -0
  37. {fastapi_error_map-0.9.1 → fastapi_error_map-0.9.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-error-map
3
- Version: 0.9.1
3
+ Version: 0.9.3
4
4
  Summary: Elegant per-endpoint error handling in FastAPI with OpenAPI schema generation
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
@@ -21,7 +21,7 @@ Classifier: Topic :: Software Development :: Libraries
21
21
  Classifier: Typing :: Typed
22
22
  Requires-Python: >=3.9
23
23
  Requires-Dist: fastapi>=0.100.0
24
- Requires-Dist: orjson>=3.11.1
24
+ Requires-Dist: orjson>=3.8.4
25
25
  Provides-Extra: dev
26
26
  Requires-Dist: mypy==1.8.0; extra == 'dev'
27
27
  Requires-Dist: pre-commit==4.2.0; extra == 'dev'
@@ -41,8 +41,9 @@ Description-Content-Type: text/markdown
41
41
 
42
42
  ## FastAPI Error Map
43
43
 
44
- [![PyPI version](https://badge.fury.io/py/fastapi-error-map.svg?cacheBust=1)](https://badge.fury.io/py/fastapi-error-map)
44
+ [![PyPI version](https://badge.fury.io/py/fastapi-error-map.svg?cacheBust=2)](https://badge.fury.io/py/fastapi-error-map)
45
45
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/fastapi-error-map?cacheBust=1)
46
+ [![codecov](https://codecov.io/gh/ivan-borovets/fastapi-error-map/branch/master/graph/badge.svg?token=ABTVQLI0RL)](https://codecov.io/gh/ivan-borovets/fastapi-error-map)
46
47
  ![GitHub License](https://img.shields.io/github/license/ivan-borovets/fastapi-error-map?cacheBust=1)
47
48
  ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ivan-borovets/fastapi-error-map/ci.yaml?cacheBust=1)
48
49
 
@@ -122,7 +123,7 @@ there's no single source of truth.
122
123
  `fastapi-error-map` solves this by letting you define error handling rules right in the route declaration.
123
124
  See the [example in the 🚀 Quickstart](#-quickstart).
124
125
 
125
- ### ⚙ How `error_map` Works
126
+ ### ⚙ Using `error_map`
126
127
 
127
128
  Error handling rules are defined directly in the route declaration of an `ErrorAwareRouter`, using the `error_map`
128
129
  parameter.
@@ -151,7 +152,7 @@ In both cases, the default translator is used, which returns JSON like:
151
152
 
152
153
  ```json
153
154
  {
154
- "error": "SomeError"
155
+ "error": "..."
155
156
  }
156
157
  ```
157
158
 
@@ -177,6 +178,13 @@ Parameters of `rule(...)`, * — required:
177
178
  `{ "error": "..." }`)
178
179
  - `on_error` — function to call when an exception occurs (e.g. logging or alerting)
179
180
 
181
+ #### 🧩 Matching semantics
182
+
183
+ `error_map` matches **exact** exception types only (no inheritance).
184
+ If you map `BaseError` and raise `ChildError(BaseError)`, the rule won’t apply.
185
+ This is by design to keep routing explicit.
186
+ If there’s demand, inheritance-based resolving may be added later as an opt-in.
187
+
180
188
  ### 🧰 Custom Translators
181
189
 
182
190
  If you want to change the error response format, define your own `translator` — object that implements `ErrorTranslator`
@@ -212,11 +220,16 @@ class MyTranslator(ErrorTranslator[MyErrorResponse]):
212
220
 
213
221
  ```
214
222
 
223
+ #### ⚠️ Translator robustness
224
+
225
+ Custom translators should not raise exceptions.
226
+ If `from_error(...)` fails at runtime, the exception will propagate to FastAPI’s global handlers.
227
+
215
228
  ### 🔄 Side Effects (`on_error`)
216
229
 
217
230
  The `on_error` parameter in `rule(...)` allows specifying function to run when exception occurs, before response is
218
231
  generated.
219
- It doesn't affect the HTTP response, but it's useful for:
232
+ It doesnt change the response status/body when it succeeds and is useful for:
220
233
 
221
234
  - logging
222
235
  - sending alerts
@@ -240,6 +253,13 @@ error_map = {
240
253
 
241
254
  ```
242
255
 
256
+ #### 🚨 Strict side effects
257
+
258
+ `on_error` is executed before the response is generated.
259
+ If it raises, the exception propagates and the request fails.
260
+ This is intentional to avoid hiding errors in side effects — they should be fixed rather than silently ignored.
261
+ Keep `on_error` robust or wrap it on your side if you prefer soft-fail logging.
262
+
243
263
  ### 🧠 Parameter Precedence
244
264
 
245
265
  Error handling and schema generation in `fastapi-error-map` are fully driven by route-level arguments to `.get()`,
@@ -320,3 +340,8 @@ This behavior is not affected by `warn_on_unmapped`.
320
340
 
321
341
  If the error is declared in `error_map`, `fastapi-error-map` handles the response itself — the global handler will not
322
342
  be triggered.
343
+
344
+ ### 📚 Examples
345
+
346
+ - Minimal usage: [`examples/`](./examples/main.py)
347
+ - More advanced usage: [Clean Architecture example app](https://github.com/ivan-borovets/fastapi-clean-example)
@@ -1,7 +1,8 @@
1
1
  ## FastAPI Error Map
2
2
 
3
- [![PyPI version](https://badge.fury.io/py/fastapi-error-map.svg?cacheBust=1)](https://badge.fury.io/py/fastapi-error-map)
3
+ [![PyPI version](https://badge.fury.io/py/fastapi-error-map.svg?cacheBust=2)](https://badge.fury.io/py/fastapi-error-map)
4
4
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/fastapi-error-map?cacheBust=1)
5
+ [![codecov](https://codecov.io/gh/ivan-borovets/fastapi-error-map/branch/master/graph/badge.svg?token=ABTVQLI0RL)](https://codecov.io/gh/ivan-borovets/fastapi-error-map)
5
6
  ![GitHub License](https://img.shields.io/github/license/ivan-borovets/fastapi-error-map?cacheBust=1)
6
7
  ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ivan-borovets/fastapi-error-map/ci.yaml?cacheBust=1)
7
8
 
@@ -81,7 +82,7 @@ there's no single source of truth.
81
82
  `fastapi-error-map` solves this by letting you define error handling rules right in the route declaration.
82
83
  See the [example in the 🚀 Quickstart](#-quickstart).
83
84
 
84
- ### ⚙ How `error_map` Works
85
+ ### ⚙ Using `error_map`
85
86
 
86
87
  Error handling rules are defined directly in the route declaration of an `ErrorAwareRouter`, using the `error_map`
87
88
  parameter.
@@ -110,7 +111,7 @@ In both cases, the default translator is used, which returns JSON like:
110
111
 
111
112
  ```json
112
113
  {
113
- "error": "SomeError"
114
+ "error": "..."
114
115
  }
115
116
  ```
116
117
 
@@ -136,6 +137,13 @@ Parameters of `rule(...)`, * — required:
136
137
  `{ "error": "..." }`)
137
138
  - `on_error` — function to call when an exception occurs (e.g. logging or alerting)
138
139
 
140
+ #### 🧩 Matching semantics
141
+
142
+ `error_map` matches **exact** exception types only (no inheritance).
143
+ If you map `BaseError` and raise `ChildError(BaseError)`, the rule won’t apply.
144
+ This is by design to keep routing explicit.
145
+ If there’s demand, inheritance-based resolving may be added later as an opt-in.
146
+
139
147
  ### 🧰 Custom Translators
140
148
 
141
149
  If you want to change the error response format, define your own `translator` — object that implements `ErrorTranslator`
@@ -171,11 +179,16 @@ class MyTranslator(ErrorTranslator[MyErrorResponse]):
171
179
 
172
180
  ```
173
181
 
182
+ #### ⚠️ Translator robustness
183
+
184
+ Custom translators should not raise exceptions.
185
+ If `from_error(...)` fails at runtime, the exception will propagate to FastAPI’s global handlers.
186
+
174
187
  ### 🔄 Side Effects (`on_error`)
175
188
 
176
189
  The `on_error` parameter in `rule(...)` allows specifying function to run when exception occurs, before response is
177
190
  generated.
178
- It doesn't affect the HTTP response, but it's useful for:
191
+ It doesnt change the response status/body when it succeeds and is useful for:
179
192
 
180
193
  - logging
181
194
  - sending alerts
@@ -199,6 +212,13 @@ error_map = {
199
212
 
200
213
  ```
201
214
 
215
+ #### 🚨 Strict side effects
216
+
217
+ `on_error` is executed before the response is generated.
218
+ If it raises, the exception propagates and the request fails.
219
+ This is intentional to avoid hiding errors in side effects — they should be fixed rather than silently ignored.
220
+ Keep `on_error` robust or wrap it on your side if you prefer soft-fail logging.
221
+
202
222
  ### 🧠 Parameter Precedence
203
223
 
204
224
  Error handling and schema generation in `fastapi-error-map` are fully driven by route-level arguments to `.get()`,
@@ -279,3 +299,8 @@ This behavior is not affected by `warn_on_unmapped`.
279
299
 
280
300
  If the error is declared in `error_map`, `fastapi-error-map` handles the response itself — the global handler will not
281
301
  be triggered.
302
+
303
+ ### 📚 Examples
304
+
305
+ - Minimal usage: [`examples/`](./examples/main.py)
306
+ - More advanced usage: [Clean Architecture example app](https://github.com/ivan-borovets/fastapi-clean-example)
@@ -18,10 +18,12 @@ def wrap_with_error_handling(
18
18
  default_server_error_translator: ErrorTranslator[Any],
19
19
  default_on_error: Optional[Callable[[Exception], None]],
20
20
  ) -> Callable[..., Any]:
21
+ is_coro = inspect.iscoroutinefunction(func)
22
+
21
23
  @wraps(func)
22
24
  async def wrapped(*args: Any, **kwargs: Any) -> Any:
23
25
  try:
24
- if inspect.iscoroutinefunction(func):
26
+ if is_coro:
25
27
  return await func(*args, **kwargs)
26
28
  return func(*args, **kwargs)
27
29
  except Exception as error:
@@ -57,7 +59,8 @@ def handle_with_error_map(
57
59
  except RuntimeError as exc:
58
60
  if warn_on_unmapped:
59
61
  raise
60
- raise (exc.__cause__ or exc) from None
62
+ original = exc.__cause__ or exc
63
+ raise original.with_traceback(original.__traceback__) from None
61
64
 
62
65
  if rule.on_error is not None:
63
66
  rule.on_error(error)
@@ -0,0 +1,34 @@
1
+ from typing import Any, Union
2
+
3
+ from fastapi_error_map.rules import ErrorMap
4
+ from fastapi_error_map.translator_policy import pick_translator_for_status
5
+ from fastapi_error_map.translators import ErrorTranslator
6
+
7
+
8
+ def build_openapi_responses(
9
+ *,
10
+ error_map: ErrorMap,
11
+ default_client_error_translator: ErrorTranslator[Any],
12
+ default_server_error_translator: ErrorTranslator[Any],
13
+ ) -> dict[Union[int, str], dict[str, Any]]:
14
+ responses: dict[Union[int, str], dict[str, Any]] = {}
15
+
16
+ for value in error_map.values():
17
+ if isinstance(value, int):
18
+ status = value
19
+ translator = None
20
+ else:
21
+ status = value.status
22
+ translator = value.translator
23
+
24
+ if translator is None:
25
+ translator = pick_translator_for_status(
26
+ status=status,
27
+ default_client_error_translator=default_client_error_translator,
28
+ default_server_error_translator=default_server_error_translator,
29
+ )
30
+
31
+ response_model = translator.error_response_model_cls
32
+ responses[status] = {"model": response_model}
33
+
34
+ return responses
@@ -100,7 +100,11 @@ class ErrorAwareRoute(APIRoute):
100
100
  default_server_error_translator=self.default_server_error_translator,
101
101
  )
102
102
  responses = {
103
- **build_openapi_responses(self.error_map),
103
+ **build_openapi_responses(
104
+ error_map=self.error_map,
105
+ default_client_error_translator=self.default_client_error_translator,
106
+ default_server_error_translator=self.default_server_error_translator,
107
+ ),
104
108
  **(responses if responses is not None else {}),
105
109
  }
106
110
  super().__init__(
@@ -1,8 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Any, Callable, Optional, Union
3
3
 
4
- from starlette import status as http_status
5
-
4
+ from fastapi_error_map.translator_policy import pick_translator_for_status
6
5
  from fastapi_error_map.translators import ErrorTranslator
7
6
 
8
7
 
@@ -62,6 +61,7 @@ def resolve_rule_for_error(
62
61
  status_or_rule = error_map[type(error)]
63
62
  except KeyError:
64
63
  raise RuntimeError(f"No rule defined for {type(error).__name__}") from error
64
+
65
65
  if isinstance(status_or_rule, int):
66
66
  status, translator, on_error = status_or_rule, None, default_on_error
67
67
  else:
@@ -70,12 +70,14 @@ def resolve_rule_for_error(
70
70
  status_or_rule.translator,
71
71
  status_or_rule.on_error or default_on_error,
72
72
  )
73
+
73
74
  if translator is None:
74
- translator = (
75
- default_server_error_translator
76
- if status >= http_status.HTTP_500_INTERNAL_SERVER_ERROR
77
- else default_client_error_translator
75
+ translator = pick_translator_for_status(
76
+ status=status,
77
+ default_client_error_translator=default_client_error_translator,
78
+ default_server_error_translator=default_server_error_translator,
78
79
  )
80
+
79
81
  return ResolvedRule(
80
82
  status=status,
81
83
  translator=translator,
@@ -0,0 +1,22 @@
1
+ from typing import Any
2
+
3
+ from starlette import status as http_status
4
+
5
+ from fastapi_error_map.translators import ErrorTranslator
6
+
7
+
8
+ def is_server_error(status: int) -> bool:
9
+ return status >= http_status.HTTP_500_INTERNAL_SERVER_ERROR
10
+
11
+
12
+ def pick_translator_for_status(
13
+ *,
14
+ status: int,
15
+ default_client_error_translator: ErrorTranslator[Any],
16
+ default_server_error_translator: ErrorTranslator[Any],
17
+ ) -> ErrorTranslator[Any]:
18
+ return (
19
+ default_server_error_translator
20
+ if is_server_error(status)
21
+ else default_client_error_translator
22
+ )
@@ -72,4 +72,4 @@ class DefaultServerErrorTranslator(ErrorTranslator[SimpleErrorResponseModel]):
72
72
  return SimpleErrorResponseModel
73
73
 
74
74
  def from_error(self, _err: Exception) -> SimpleErrorResponseModel:
75
- return SimpleErrorResponseModel(error="Internal server error.")
75
+ return SimpleErrorResponseModel(error="Internal server error")
@@ -7,7 +7,7 @@ sources = ["src"]
7
7
 
8
8
  [project]
9
9
  name = "fastapi-error-map"
10
- version = "0.9.1"
10
+ version = "0.9.3"
11
11
  description = "Elegant per-endpoint error handling in FastAPI with OpenAPI schema generation"
12
12
  readme = "README.md"
13
13
  license = { text = "Apache-2.0" }
@@ -31,7 +31,7 @@ classifiers = [
31
31
  ]
32
32
  dependencies = [
33
33
  "fastapi>=0.100.0",
34
- "orjson>=3.11.1",
34
+ "orjson>=3.8.4",
35
35
  ]
36
36
 
37
37
  [project.urls]
@@ -0,0 +1,65 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from fastapi_error_map.openapi import build_openapi_responses
4
+ from fastapi_error_map.rules import rule
5
+ from tests.unit.error_stubs import DatabaseError, ValidationError
6
+ from tests.unit.translator_stubs import (
7
+ ClientErrorStub,
8
+ CustomTranslator,
9
+ DummyClientErrorTranslator,
10
+ DummyServerErrorTranslator,
11
+ ServerErrorStub,
12
+ )
13
+
14
+ if TYPE_CHECKING:
15
+ from fastapi_error_map.rules import ErrorMap
16
+
17
+
18
+ def test_builds_response_from_short_form_rule() -> None:
19
+ error_map: ErrorMap = {
20
+ ValidationError: 400,
21
+ DatabaseError: 503,
22
+ }
23
+
24
+ responses = build_openapi_responses(
25
+ error_map=error_map,
26
+ default_client_error_translator=DummyClientErrorTranslator(),
27
+ default_server_error_translator=DummyServerErrorTranslator(),
28
+ )
29
+
30
+ assert responses == {
31
+ 400: {"model": ClientErrorStub},
32
+ 503: {"model": ServerErrorStub},
33
+ }
34
+
35
+
36
+ def test_builds_response_from_full_form_rule() -> None:
37
+ error_map: ErrorMap = {
38
+ ValidationError: rule(status=400),
39
+ DatabaseError: rule(status=503),
40
+ }
41
+
42
+ responses = build_openapi_responses(
43
+ error_map=error_map,
44
+ default_client_error_translator=DummyClientErrorTranslator(),
45
+ default_server_error_translator=DummyServerErrorTranslator(),
46
+ )
47
+
48
+ assert responses == {
49
+ 400: {"model": ClientErrorStub},
50
+ 503: {"model": ServerErrorStub},
51
+ }
52
+
53
+
54
+ def test_builds_response_with_custom_translator() -> None:
55
+ error_map: ErrorMap = {
56
+ ValidationError: rule(status=400, translator=CustomTranslator()),
57
+ }
58
+
59
+ responses = build_openapi_responses(
60
+ error_map=error_map,
61
+ default_client_error_translator=DummyClientErrorTranslator(),
62
+ default_server_error_translator=DummyServerErrorTranslator(),
63
+ )
64
+
65
+ assert responses == {400: {"model": dict}}
@@ -0,0 +1,40 @@
1
+ from dataclasses import dataclass
2
+
3
+ from fastapi_error_map import ErrorTranslator
4
+
5
+
6
+ @dataclass
7
+ class ClientErrorStub:
8
+ error: str
9
+
10
+
11
+ @dataclass
12
+ class ServerErrorStub:
13
+ error: str
14
+
15
+
16
+ class DummyClientErrorTranslator(ErrorTranslator[ClientErrorStub]):
17
+ @property
18
+ def error_response_model_cls(self) -> type[ClientErrorStub]:
19
+ return ClientErrorStub
20
+
21
+ def from_error(self, err: Exception) -> ClientErrorStub:
22
+ return ClientErrorStub(error=f"client:{err}")
23
+
24
+
25
+ class DummyServerErrorTranslator(ErrorTranslator[ServerErrorStub]):
26
+ @property
27
+ def error_response_model_cls(self) -> type[ServerErrorStub]:
28
+ return ServerErrorStub
29
+
30
+ def from_error(self, _err: Exception) -> ServerErrorStub:
31
+ return ServerErrorStub(error="internal")
32
+
33
+
34
+ class CustomTranslator(ErrorTranslator[dict[str, str]]):
35
+ @property
36
+ def error_response_model_cls(self) -> type[dict[str, str]]:
37
+ return dict
38
+
39
+ def from_error(self, _err: Exception) -> dict[str, str]:
40
+ return {"msg": "explicit"}
@@ -1,26 +0,0 @@
1
- from typing import Any, Union
2
-
3
- from fastapi_error_map.rules import ErrorMap
4
- from fastapi_error_map.translators import SimpleErrorResponseModel
5
-
6
-
7
- def build_openapi_responses(
8
- error_map: ErrorMap,
9
- default_response_model: type[Any] = SimpleErrorResponseModel,
10
- ) -> dict[Union[int, str], dict[str, Any]]:
11
- responses: dict[Union[int, str], dict[str, Any]] = {}
12
-
13
- for value in error_map.values():
14
- if isinstance(value, int):
15
- status_code = value
16
- response_model: type[Any] = default_response_model
17
- else:
18
- status_code = value.status
19
- response_model = (
20
- value.translator.error_response_model_cls
21
- if value.translator is not None
22
- else default_response_model
23
- )
24
- responses[status_code] = {"model": response_model}
25
-
26
- return responses
@@ -1,48 +0,0 @@
1
- from typing import TYPE_CHECKING
2
-
3
- from fastapi_error_map import SimpleErrorResponseModel
4
- from fastapi_error_map.openapi import build_openapi_responses
5
- from fastapi_error_map.rules import rule
6
- from tests.unit.error_stubs import DatabaseError, ValidationError
7
- from tests.unit.translator_stubs import CustomTranslator
8
-
9
- if TYPE_CHECKING:
10
- from fastapi_error_map.rules import ErrorMap
11
-
12
-
13
- def test_builds_response_from_short_form_rule() -> None:
14
- error_map: ErrorMap = {
15
- ValidationError: 400,
16
- DatabaseError: 503,
17
- }
18
-
19
- responses = build_openapi_responses(error_map)
20
-
21
- assert responses == {
22
- 400: {"model": SimpleErrorResponseModel},
23
- 503: {"model": SimpleErrorResponseModel},
24
- }
25
-
26
-
27
- def test_builds_response_from_full_form_rule() -> None:
28
- error_map: ErrorMap = {
29
- ValidationError: rule(status=400),
30
- DatabaseError: rule(status=503),
31
- }
32
-
33
- responses = build_openapi_responses(error_map)
34
-
35
- assert responses == {
36
- 400: {"model": SimpleErrorResponseModel},
37
- 503: {"model": SimpleErrorResponseModel},
38
- }
39
-
40
-
41
- def test_builds_response_with_custom_translator() -> None:
42
- error_map: ErrorMap = {
43
- ValidationError: rule(status=400, translator=CustomTranslator()),
44
- }
45
-
46
- responses = build_openapi_responses(error_map)
47
-
48
- assert responses == {400: {"model": dict}}
@@ -1,28 +0,0 @@
1
- from fastapi_error_map import ErrorTranslator
2
-
3
-
4
- class DummyClientErrorTranslator(ErrorTranslator[dict[str, str]]):
5
- @property
6
- def error_response_model_cls(self) -> type[dict[str, str]]:
7
- return dict
8
-
9
- def from_error(self, err: Exception) -> dict[str, str]:
10
- return {"error": f"client:{err}"}
11
-
12
-
13
- class DummyServerErrorTranslator(ErrorTranslator[dict[str, str]]):
14
- @property
15
- def error_response_model_cls(self) -> type[dict[str, str]]:
16
- return dict
17
-
18
- def from_error(self, _err: Exception) -> dict[str, str]:
19
- return {"error": "internal"}
20
-
21
-
22
- class CustomTranslator(ErrorTranslator[dict[str, str]]):
23
- @property
24
- def error_response_model_cls(self) -> type[dict[str, str]]:
25
- return dict
26
-
27
- def from_error(self, _err: Exception) -> dict[str, str]:
28
- return {"msg": "explicit"}