fastapi-errors-plus 0.6.2__py3-none-any.whl → 0.8.0__py3-none-any.whl
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_errors_plus/__init__.py +9 -6
- fastapi_errors_plus/base.py +48 -56
- fastapi_errors_plus/error_doc.py +59 -0
- fastapi_errors_plus/errors.py +244 -105
- fastapi_errors_plus/example_utils.py +52 -0
- fastapi_errors_plus/protocol.py +26 -39
- {fastapi_errors_plus-0.6.2.dist-info → fastapi_errors_plus-0.8.0.dist-info}/METADATA +177 -52
- fastapi_errors_plus-0.8.0.dist-info/RECORD +12 -0
- {fastapi_errors_plus-0.6.2.dist-info → fastapi_errors_plus-0.8.0.dist-info}/WHEEL +1 -1
- fastapi_errors_plus-0.6.2.dist-info/RECORD +0 -10
- {fastapi_errors_plus-0.6.2.dist-info → fastapi_errors_plus-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {fastapi_errors_plus-0.6.2.dist-info → fastapi_errors_plus-0.8.0.dist-info}/top_level.txt +0 -0
fastapi_errors_plus/__init__.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
"""Universal library for documenting errors in FastAPI endpoints."""
|
|
2
2
|
|
|
3
3
|
from fastapi_errors_plus.base import BaseErrorDTO, StandardErrorDTO
|
|
4
|
+
from fastapi_errors_plus.error_doc import ErrorDoc
|
|
4
5
|
from fastapi_errors_plus.errors import Errors
|
|
5
|
-
from fastapi_errors_plus.protocol import ErrorDTO
|
|
6
|
+
from fastapi_errors_plus.protocol import ErrorDTO, ErrorDTOLike, LegacyErrorDTO
|
|
6
7
|
|
|
7
8
|
__all__ = [
|
|
8
9
|
"Errors",
|
|
9
|
-
"ErrorDTO",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
10
|
+
"ErrorDTO",
|
|
11
|
+
"LegacyErrorDTO",
|
|
12
|
+
"ErrorDTOLike",
|
|
13
|
+
"ErrorDoc",
|
|
14
|
+
"BaseErrorDTO",
|
|
15
|
+
"StandardErrorDTO",
|
|
12
16
|
]
|
|
13
|
-
__version__ = "0.
|
|
14
|
-
|
|
17
|
+
__version__ = "0.8.0"
|
fastapi_errors_plus/base.py
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
"""Base implementations of ErrorDTO protocol for convenience."""
|
|
2
2
|
|
|
3
|
+
import warnings
|
|
3
4
|
from dataclasses import dataclass, field
|
|
4
5
|
from typing import Any, Dict, Optional
|
|
5
6
|
|
|
7
|
+
from fastapi_errors_plus.example_utils import ExampleSpec, _normalize_example_specs
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
@dataclass
|
|
8
11
|
class BaseErrorDTO:
|
|
9
12
|
"""Base implementation of ErrorDTO Protocol.
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
Projects can use this class directly or create their own implementation
|
|
12
15
|
that implements ErrorDTO Protocol through structural typing.
|
|
13
|
-
|
|
16
|
+
|
|
14
17
|
This class provides a simple way to create error DTOs without writing
|
|
15
18
|
boilerplate code in every project.
|
|
16
|
-
|
|
19
|
+
|
|
17
20
|
Example:
|
|
18
21
|
```python
|
|
19
22
|
from fastapi_errors_plus import Errors, BaseErrorDTO
|
|
20
|
-
|
|
23
|
+
|
|
21
24
|
notification_error = BaseErrorDTO(
|
|
22
25
|
status_code=404,
|
|
23
26
|
message="Notification not found",
|
|
24
27
|
)
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
@router.delete(
|
|
27
30
|
"/{id}",
|
|
28
31
|
responses=Errors(notification_error),
|
|
@@ -31,45 +34,48 @@ class BaseErrorDTO:
|
|
|
31
34
|
pass
|
|
32
35
|
```
|
|
33
36
|
"""
|
|
37
|
+
|
|
34
38
|
status_code: int
|
|
35
39
|
"""HTTP status code for the error."""
|
|
36
|
-
|
|
40
|
+
|
|
37
41
|
message: str
|
|
38
42
|
"""Error message description."""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
"Notification not found": {
|
|
50
|
-
"value": {"detail": "Notification not found"},
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
"""
|
|
43
|
+
|
|
44
|
+
openapi_json_extras: Optional[Dict[str, Any]] = field(default=None)
|
|
45
|
+
"""Optional OpenAPI fragment under ``content['application/json']`` besides examples,
|
|
46
|
+
typically ``{\"schema\": ...}`` or ``encoding``. Do **not** put ``example`` / ``examples``
|
|
47
|
+
here — use :meth:`to_example`. Arbitrary implementations may instead implement
|
|
48
|
+
:meth:`to_openapi_json_media_type_extras`; that return value wins over this attribute."""
|
|
49
|
+
|
|
50
|
+
def to_examples(self) -> Dict[str, Any]:
|
|
51
|
+
"""Generate OpenAPI ``examples`` for this error."""
|
|
55
52
|
return {
|
|
56
53
|
self.message: {
|
|
57
54
|
"value": {"detail": self.message},
|
|
58
55
|
},
|
|
59
56
|
}
|
|
60
57
|
|
|
58
|
+
def to_example(self) -> Dict[str, Any]:
|
|
59
|
+
"""Deprecated alias for :meth:`to_examples`."""
|
|
60
|
+
warnings.warn(
|
|
61
|
+
"to_example() is deprecated; use to_examples() instead.",
|
|
62
|
+
DeprecationWarning,
|
|
63
|
+
stacklevel=2,
|
|
64
|
+
)
|
|
65
|
+
return self.to_examples()
|
|
66
|
+
|
|
61
67
|
|
|
62
68
|
@dataclass
|
|
63
69
|
class StandardErrorDTO(BaseErrorDTO):
|
|
64
70
|
"""Extended implementation for errors with multiple examples.
|
|
65
|
-
|
|
71
|
+
|
|
66
72
|
Useful for standard HTTP errors (401, 403) that can have different
|
|
67
73
|
causes with different messages.
|
|
68
|
-
|
|
74
|
+
|
|
69
75
|
Example:
|
|
70
76
|
```python
|
|
71
77
|
from fastapi_errors_plus import Errors, StandardErrorDTO
|
|
72
|
-
|
|
78
|
+
|
|
73
79
|
unauthorized_error = StandardErrorDTO(
|
|
74
80
|
status_code=401,
|
|
75
81
|
message="Unauthorized",
|
|
@@ -78,7 +84,7 @@ class StandardErrorDTO(BaseErrorDTO):
|
|
|
78
84
|
"SessionNotFound": "Сессия пользователя не была найдена.",
|
|
79
85
|
},
|
|
80
86
|
)
|
|
81
|
-
|
|
87
|
+
|
|
82
88
|
@router.delete(
|
|
83
89
|
"/{id}",
|
|
84
90
|
responses=Errors(unauthorized_error),
|
|
@@ -87,39 +93,25 @@ class StandardErrorDTO(BaseErrorDTO):
|
|
|
87
93
|
pass
|
|
88
94
|
```
|
|
89
95
|
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
|
|
97
|
+
examples: Optional[Dict[str, ExampleSpec]] = field(default=None)
|
|
98
|
+
"""Examples: ``str`` detail text or full OpenAPI example object (with ``summary``)."""
|
|
99
|
+
|
|
93
100
|
def __post_init__(self) -> None:
|
|
94
101
|
"""Initialize examples with default value if not provided."""
|
|
95
102
|
if self.examples is None:
|
|
96
103
|
self.examples = {self.message: self.message}
|
|
97
|
-
|
|
98
|
-
def to_example(self) -> Dict[str, Any]:
|
|
99
|
-
"""Generate examples for OpenAPI with multiple examples.
|
|
100
|
-
|
|
101
|
-
Returns:
|
|
102
|
-
Dict in format: {"key": {"value": {"detail": "message"}}, ...}
|
|
103
|
-
|
|
104
|
-
Example:
|
|
105
|
-
```python
|
|
106
|
-
{
|
|
107
|
-
"InvalidToken": {
|
|
108
|
-
"value": {"detail": "Ошибка декодирования токена."},
|
|
109
|
-
},
|
|
110
|
-
"SessionNotFound": {
|
|
111
|
-
"value": {"detail": "Сессия пользователя не была найдена."},
|
|
112
|
-
},
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
"""
|
|
116
|
-
return {
|
|
117
|
-
key: {
|
|
118
|
-
"value": {"detail": message},
|
|
119
|
-
}
|
|
120
|
-
for key, message in self.examples.items()
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
104
|
|
|
105
|
+
def to_examples(self) -> Dict[str, Any]:
|
|
106
|
+
"""Generate OpenAPI ``examples`` with optional summaries."""
|
|
107
|
+
assert self.examples is not None
|
|
108
|
+
return _normalize_example_specs(self.examples)
|
|
125
109
|
|
|
110
|
+
def to_example(self) -> Dict[str, Any]:
|
|
111
|
+
"""Deprecated alias for :meth:`to_examples`."""
|
|
112
|
+
warnings.warn(
|
|
113
|
+
"to_example() is deprecated; use to_examples() instead.",
|
|
114
|
+
DeprecationWarning,
|
|
115
|
+
stacklevel=2,
|
|
116
|
+
)
|
|
117
|
+
return self.to_examples()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Declarative error documentation for OpenAPI."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
from fastapi_errors_plus.example_utils import ExampleSpec, _normalize_example_specs
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class ErrorDoc:
|
|
12
|
+
"""Declarative ErrorDTO with arbitrary example bodies and optional summaries.
|
|
13
|
+
|
|
14
|
+
``examples`` values:
|
|
15
|
+
|
|
16
|
+
* **str** — shorthand ``{"detail": text}`` (same as bundled DTOs).
|
|
17
|
+
* **dict** with only OpenAPI Example Object keys (``value``, ``summary``, …) —
|
|
18
|
+
used as the Example Object.
|
|
19
|
+
* **any other dict** — treated as the full response **body** (e.g.
|
|
20
|
+
``{"code": "...", "detail": "...", "value": 150}``).
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
ErrorDoc(
|
|
25
|
+
status_code=403,
|
|
26
|
+
message="Insufficient permissions",
|
|
27
|
+
examples={
|
|
28
|
+
"MissingRole": {
|
|
29
|
+
"summary": "User lacks required role",
|
|
30
|
+
"value": {"code": "FORBIDDEN", "detail": "Role admin required"},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
status_code: int
|
|
38
|
+
message: str
|
|
39
|
+
examples: Optional[Dict[str, ExampleSpec]] = None
|
|
40
|
+
body: Optional[Dict[str, Any]] = None
|
|
41
|
+
example_key: Optional[str] = None
|
|
42
|
+
openapi_json_extras: Optional[Dict[str, Any]] = field(default=None)
|
|
43
|
+
|
|
44
|
+
def to_examples(self) -> Dict[str, Any]:
|
|
45
|
+
"""Build OpenAPI ``examples`` map for ``application/json``."""
|
|
46
|
+
if self.examples is not None:
|
|
47
|
+
return _normalize_example_specs(self.examples)
|
|
48
|
+
key = self.example_key or self.message
|
|
49
|
+
value = self.body if self.body is not None else {"detail": self.message}
|
|
50
|
+
return {key: {"value": value}}
|
|
51
|
+
|
|
52
|
+
def to_example(self) -> Dict[str, Any]:
|
|
53
|
+
"""Deprecated alias for :meth:`to_examples`."""
|
|
54
|
+
warnings.warn(
|
|
55
|
+
"to_example() is deprecated; use to_examples() instead.",
|
|
56
|
+
DeprecationWarning,
|
|
57
|
+
stacklevel=2,
|
|
58
|
+
)
|
|
59
|
+
return self.to_examples()
|