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.
@@ -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", # Protocol (for structural typing)
10
- "BaseErrorDTO", # Base implementation (optional)
11
- "StandardErrorDTO", # Extended implementation (optional)
10
+ "ErrorDTO",
11
+ "LegacyErrorDTO",
12
+ "ErrorDTOLike",
13
+ "ErrorDoc",
14
+ "BaseErrorDTO",
15
+ "StandardErrorDTO",
12
16
  ]
13
- __version__ = "0.6.2"
14
-
17
+ __version__ = "0.8.0"
@@ -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
- def to_example(self) -> Dict[str, Any]:
41
- """Generate example for OpenAPI.
42
-
43
- Returns:
44
- Dict in format: {"key": {"value": {"detail": "message"}}}
45
-
46
- Example:
47
- ```python
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
- examples: Optional[Dict[str, str]] = field(default=None)
91
- """Dictionary of examples: {"key": "message"}."""
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()