fastapi-errors-plus 0.1.1__tar.gz → 0.4.0__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 (19) hide show
  1. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/LICENSE +1 -0
  2. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/PKG-INFO +164 -16
  3. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/README.md +163 -15
  4. fastapi_errors_plus-0.4.0/fastapi_errors_plus/__init__.py +14 -0
  5. fastapi_errors_plus-0.4.0/fastapi_errors_plus/base.py +122 -0
  6. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/fastapi_errors_plus/errors.py +39 -9
  7. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/fastapi_errors_plus/protocol.py +1 -0
  8. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/fastapi_errors_plus.egg-info/PKG-INFO +164 -16
  9. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/fastapi_errors_plus.egg-info/SOURCES.txt +2 -0
  10. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/pyproject.toml +1 -1
  11. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/tests/test_app.py +68 -2
  12. fastapi_errors_plus-0.4.0/tests/test_base.py +304 -0
  13. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/tests/test_errors.py +138 -6
  14. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/tests/test_integration.py +114 -1
  15. fastapi_errors_plus-0.1.1/fastapi_errors_plus/__init__.py +0 -8
  16. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/fastapi_errors_plus.egg-info/dependency_links.txt +0 -0
  17. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/fastapi_errors_plus.egg-info/requires.txt +0 -0
  18. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/fastapi_errors_plus.egg-info/top_level.txt +0 -0
  19. {fastapi_errors_plus-0.1.1 → fastapi_errors_plus-0.4.0}/setup.cfg +0 -0
@@ -20,3 +20,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
22
 
23
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-errors-plus
3
- Version: 0.1.1
3
+ Version: 0.4.0
4
4
  Summary: Universal library for documenting errors in FastAPI endpoints
5
5
  Author-email: Igor Selivanov <seligoroff@gmail.com>
6
6
  License: MIT
@@ -30,7 +30,7 @@ Dynamic: license-file
30
30
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
31
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
32
32
  [![FastAPI](https://img.shields.io/badge/FastAPI-0.104%2B-009688.svg)](https://fastapi.tiangolo.com/)
33
- [![Tests](https://img.shields.io/badge/tests-41%20passed-success.svg)](https://github.com/seligoroff/fastapi-errors-plus)
33
+ [![Tests](https://img.shields.io/badge/tests-72%20passed-success.svg)](https://github.com/seligoroff/fastapi-errors-plus)
34
34
  [![Coverage](https://img.shields.io/badge/coverage-83%25-green.svg)](https://github.com/seligoroff/fastapi-errors-plus)
35
35
 
36
36
  Universal library for documenting errors in FastAPI endpoints.
@@ -82,9 +82,9 @@ router = APIRouter()
82
82
  },
83
83
  },
84
84
  }},
85
- unauthorized=True, # 401 Unauthorized
86
- forbidden=True, # 403 Forbidden
87
- validation_error=True, # 422 Validation Error
85
+ unauthorized_401=True, # 401 Unauthorized (explicit)
86
+ forbidden_403=True, # 403 Forbidden (explicit)
87
+ # validation_error_422=True - not needed, defaults to True
88
88
  ),
89
89
  )
90
90
  def delete_item(id: int):
@@ -99,17 +99,26 @@ def delete_item(id: int):
99
99
 
100
100
  Use boolean flags for common HTTP status codes:
101
101
 
102
+ **Recommended (explicit status codes):**
103
+ - `unauthorized_401=True` → 401 Unauthorized
104
+ - `forbidden_403=True` → 403 Forbidden
105
+ - `validation_error_422=True` → 422 Unprocessable Entity (defaults to `True`)
106
+ - `internal_server_error_500=True` → 500 Internal Server Error
107
+
108
+ **Legacy (for backward compatibility):**
102
109
  - `unauthorized=True` → 401 Unauthorized
103
110
  - `forbidden=True` → 403 Forbidden
104
- - `validation_error=True` → 422 Unprocessable Entity
111
+ - `validation_error=True` → 422 Unprocessable Entity (defaults to `True`)
105
112
  - `internal_server_error=True` → 500 Internal Server Error
106
113
 
114
+ **Note:** `validation_error` and `validation_error_422` default to `True` because FastAPI automatically validates all parameters (Path, Query, Body), making 422 relevant in 95%+ of endpoints. Set to `False` only for endpoints without parameters.
115
+
107
116
  ```python
108
117
  @router.get(
109
118
  "/protected",
110
119
  responses=Errors(
111
- unauthorized=True,
112
- forbidden=True,
120
+ unauthorized_401=True, # Explicit: 401 is visible
121
+ forbidden_403=True, # Explicit: 403 is visible
113
122
  ),
114
123
  )
115
124
  def get_protected():
@@ -117,6 +126,8 @@ def get_protected():
117
126
  pass
118
127
  ```
119
128
 
129
+ **Why explicit flags?** The new flags with status codes (`_401`, `_403`, etc.) make it immediately clear which HTTP status code corresponds to each flag, improving code readability without needing to remember the mapping.
130
+
120
131
  ### 2. Dict-based Errors
121
132
 
122
133
  Use standard FastAPI `responses` dict format for custom errors:
@@ -171,7 +182,76 @@ def get_resource(id: int):
171
182
  pass
172
183
  ```
173
184
 
174
- ### 4. Mixed Usage
185
+ ### 4. BaseErrorDTO and StandardErrorDTO (Recommended)
186
+
187
+ For convenience, the library provides ready-to-use implementations:
188
+
189
+ #### BaseErrorDTO
190
+
191
+ Simple implementation for errors with a single example:
192
+
193
+ ```python
194
+ from fastapi_errors_plus import Errors, BaseErrorDTO
195
+
196
+ notification_error = BaseErrorDTO(
197
+ status_code=404,
198
+ message="Notification not found",
199
+ )
200
+
201
+ @router.delete(
202
+ "/{id}",
203
+ responses=Errors(notification_error),
204
+ )
205
+ def delete_item(id: int):
206
+ """Delete an item."""
207
+ pass
208
+ ```
209
+
210
+ #### StandardErrorDTO
211
+
212
+ Extended implementation for errors with multiple examples (useful for standard HTTP errors):
213
+
214
+ ```python
215
+ from fastapi_errors_plus import Errors, StandardErrorDTO
216
+
217
+ unauthorized_error = StandardErrorDTO(
218
+ status_code=401,
219
+ message="Unauthorized",
220
+ examples={
221
+ "InvalidToken": "Ошибка декодирования токена.",
222
+ "SessionNotFound": "Сессия пользователя не была найдена.",
223
+ },
224
+ )
225
+
226
+ forbidden_error = StandardErrorDTO(
227
+ status_code=403,
228
+ message="Forbidden",
229
+ examples={
230
+ "AccountNotSelected": "Аккаунт не выбран.",
231
+ "RoleHasNoAccess": "Роль не имеет доступа.",
232
+ },
233
+ )
234
+
235
+ @router.delete(
236
+ "/{id}",
237
+ responses=Errors(
238
+ unauthorized_error,
239
+ forbidden_error,
240
+ # validation_error=True - not needed, defaults to True
241
+ ),
242
+ )
243
+ def delete_item(id: int):
244
+ """Delete an item."""
245
+ pass
246
+ ```
247
+
248
+ **Benefits:**
249
+ - ✅ No need to write ErrorDTO classes from scratch
250
+ - ✅ Correct implementation out of the box
251
+ - ✅ Reusable across all endpoints
252
+ - ✅ Supports inheritance for custom logic
253
+
254
+ ### 5. Mixed Usage
175
255
 
176
256
  Combine flags, dict, and ErrorDTO:
177
257
 
@@ -190,6 +270,7 @@ Combine flags, dict, and ErrorDTO:
190
270
  MyErrorDTO(), # ErrorDTO
191
271
  unauthorized=True, # Flag
192
272
  forbidden=True, # Flag
273
+ # validation_error=True - not needed, defaults to True
193
274
  ),
194
275
  )
195
276
  def create_item_mixed(id: int):
@@ -197,7 +278,7 @@ def create_item_mixed(id: int):
197
278
  pass
198
279
  ```
199
280
 
200
- ### 5. Merging Examples
281
+ ### 6. Merging Examples
201
282
 
202
283
  Multiple errors with the same status code are automatically merged:
203
284
 
@@ -238,6 +319,20 @@ class ErrorDTO(Protocol):
238
319
 
239
320
  Any class implementing this protocol (through structural typing) can be used with `Errors()`.
240
321
 
322
+ ### When to Use Protocol vs BaseErrorDTO
323
+
324
+ **Use Protocol (structural typing)** when:
325
+ - Your project already has error DTOs that implement the protocol
326
+ - You need maximum flexibility and custom implementations
327
+ - You want to keep your existing error infrastructure
328
+
329
+ **Use BaseErrorDTO/StandardErrorDTO** when:
330
+ - Starting a new project or adding error documentation
331
+ - You want a ready-to-use implementation without boilerplate
332
+ - You need multiple examples for standard HTTP errors (401, 403, etc.)
333
+
334
+ Both approaches work together — you can mix them in the same `Errors()` call!
335
+
241
336
  ## Compatibility with Existing Projects
242
337
 
243
338
  If your project already has error DTOs (like `ApiErrorDTO`), they can work with `fastapi-errors-plus` if they implement the `ErrorDTO` protocol:
@@ -280,17 +375,28 @@ Errors(
280
375
  *errors: Union[Dict[int, Dict[str, Any]], ErrorDTO],
281
376
  unauthorized: bool = False,
282
377
  forbidden: bool = False,
283
- validation_error: bool = False,
378
+ validation_error: bool = True, # Defaults to True (FastAPI validates all parameters)
284
379
  internal_server_error: bool = False,
380
+ unauthorized_401: bool = False,
381
+ forbidden_403: bool = False,
382
+ validation_error_422: bool = True, # Defaults to True (FastAPI validates all parameters)
383
+ internal_server_error_500: bool = False,
285
384
  )
286
385
  ```
287
386
 
288
387
  **Parameters:**
289
388
  - `*errors`: Arbitrary errors as dict or ErrorDTO objects
290
- - `unauthorized`: Add 401 Unauthorized error
291
- - `forbidden`: Add 403 Forbidden error
292
- - `validation_error`: Add 422 Unprocessable Entity error
293
- - `internal_server_error`: Add 500 Internal Server Error
389
+ - `unauthorized_401`: Add 401 Unauthorized error (recommended, explicit). Defaults to `False`.
390
+ - `forbidden_403`: Add 403 Forbidden error (recommended, explicit). Defaults to `False`.
391
+ - `validation_error_422`: Add 422 Unprocessable Entity error (recommended, explicit). Defaults to `True` (FastAPI validates all parameters).
392
+ - `internal_server_error_500`: Add 500 Internal Server Error (recommended, explicit). Defaults to `False`.
393
+ - `unauthorized`: Add 401 Unauthorized error (legacy, for backward compatibility). Defaults to `False`.
394
+ - `forbidden`: Add 403 Forbidden error (legacy, for backward compatibility). Defaults to `False`.
395
+ - `validation_error`: Add 422 Unprocessable Entity error (legacy, for backward compatibility). Defaults to `True` (FastAPI validates all parameters).
396
+ - `internal_server_error`: Add 500 Internal Server Error (legacy, for backward compatibility). Defaults to `False`.
397
+
398
+ **Why `validation_error=True` by default?**
399
+ FastAPI automatically validates all parameters (Path, Query, Body), so 422 is relevant in 95%+ of endpoints. For endpoints without parameters, explicitly set `validation_error=False` or `validation_error_422=False`.
294
400
 
295
401
  **Returns:**
296
402
  - Callable object that returns `Dict[int, Dict[str, Any]]` in FastAPI responses format
@@ -313,6 +419,48 @@ Protocol for error objects compatible with the library.
313
419
  **Required methods:**
314
420
  - `to_example() -> Dict[str, Any]` — Generate example for OpenAPI
315
421
 
422
+ ### `BaseErrorDTO`
423
+
424
+ Base implementation of ErrorDTO Protocol for convenience.
425
+
426
+ **Constructor:**
427
+ ```python
428
+ BaseErrorDTO(
429
+ status_code: int,
430
+ message: str,
431
+ )
432
+ ```
433
+
434
+ **Example:**
435
+ ```python
436
+ error = BaseErrorDTO(status_code=404, message="Not found")
437
+ ```
438
+
439
+ ### `StandardErrorDTO`
440
+
441
+ Extended implementation for errors with multiple examples.
442
+
443
+ **Constructor:**
444
+ ```python
445
+ StandardErrorDTO(
446
+ status_code: int,
447
+ message: str,
448
+ examples: Optional[Dict[str, str]] = None,
449
+ )
450
+ ```
451
+
452
+ **Example:**
453
+ ```python
454
+ error = StandardErrorDTO(
455
+ status_code=401,
456
+ message="Unauthorized",
457
+ examples={
458
+ "InvalidToken": "Invalid token",
459
+ "SessionNotFound": "Session not found",
460
+ },
461
+ )
462
+ ```
463
+
316
464
  ## Examples
317
465
 
318
466
  ### Example 1: Standard FastAPI Project
@@ -471,7 +619,7 @@ router = APIRouter()
471
619
  responses=Errors(
472
620
  unauthorized=True, # From authentication dependency
473
621
  forbidden=True, # From authorization dependency
474
- validation_error=True, # From FastAPI validation
622
+ # validation_error=True - not needed, defaults to True (FastAPI validates all parameters)
475
623
  ItemAlreadyExistsError(), # Domain error
476
624
  ),
477
625
  )
@@ -4,7 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
6
6
  [![FastAPI](https://img.shields.io/badge/FastAPI-0.104%2B-009688.svg)](https://fastapi.tiangolo.com/)
7
- [![Tests](https://img.shields.io/badge/tests-41%20passed-success.svg)](https://github.com/seligoroff/fastapi-errors-plus)
7
+ [![Tests](https://img.shields.io/badge/tests-72%20passed-success.svg)](https://github.com/seligoroff/fastapi-errors-plus)
8
8
  [![Coverage](https://img.shields.io/badge/coverage-83%25-green.svg)](https://github.com/seligoroff/fastapi-errors-plus)
9
9
 
10
10
  Universal library for documenting errors in FastAPI endpoints.
@@ -56,9 +56,9 @@ router = APIRouter()
56
56
  },
57
57
  },
58
58
  }},
59
- unauthorized=True, # 401 Unauthorized
60
- forbidden=True, # 403 Forbidden
61
- validation_error=True, # 422 Validation Error
59
+ unauthorized_401=True, # 401 Unauthorized (explicit)
60
+ forbidden_403=True, # 403 Forbidden (explicit)
61
+ # validation_error_422=True - not needed, defaults to True
62
62
  ),
63
63
  )
64
64
  def delete_item(id: int):
@@ -73,17 +73,26 @@ def delete_item(id: int):
73
73
 
74
74
  Use boolean flags for common HTTP status codes:
75
75
 
76
+ **Recommended (explicit status codes):**
77
+ - `unauthorized_401=True` → 401 Unauthorized
78
+ - `forbidden_403=True` → 403 Forbidden
79
+ - `validation_error_422=True` → 422 Unprocessable Entity (defaults to `True`)
80
+ - `internal_server_error_500=True` → 500 Internal Server Error
81
+
82
+ **Legacy (for backward compatibility):**
76
83
  - `unauthorized=True` → 401 Unauthorized
77
84
  - `forbidden=True` → 403 Forbidden
78
- - `validation_error=True` → 422 Unprocessable Entity
85
+ - `validation_error=True` → 422 Unprocessable Entity (defaults to `True`)
79
86
  - `internal_server_error=True` → 500 Internal Server Error
80
87
 
88
+ **Note:** `validation_error` and `validation_error_422` default to `True` because FastAPI automatically validates all parameters (Path, Query, Body), making 422 relevant in 95%+ of endpoints. Set to `False` only for endpoints without parameters.
89
+
81
90
  ```python
82
91
  @router.get(
83
92
  "/protected",
84
93
  responses=Errors(
85
- unauthorized=True,
86
- forbidden=True,
94
+ unauthorized_401=True, # Explicit: 401 is visible
95
+ forbidden_403=True, # Explicit: 403 is visible
87
96
  ),
88
97
  )
89
98
  def get_protected():
@@ -91,6 +100,8 @@ def get_protected():
91
100
  pass
92
101
  ```
93
102
 
103
+ **Why explicit flags?** The new flags with status codes (`_401`, `_403`, etc.) make it immediately clear which HTTP status code corresponds to each flag, improving code readability without needing to remember the mapping.
104
+
94
105
  ### 2. Dict-based Errors
95
106
 
96
107
  Use standard FastAPI `responses` dict format for custom errors:
@@ -145,7 +156,76 @@ def get_resource(id: int):
145
156
  pass
146
157
  ```
147
158
 
148
- ### 4. Mixed Usage
159
+ ### 4. BaseErrorDTO and StandardErrorDTO (Recommended)
160
+
161
+ For convenience, the library provides ready-to-use implementations:
162
+
163
+ #### BaseErrorDTO
164
+
165
+ Simple implementation for errors with a single example:
166
+
167
+ ```python
168
+ from fastapi_errors_plus import Errors, BaseErrorDTO
169
+
170
+ notification_error = BaseErrorDTO(
171
+ status_code=404,
172
+ message="Notification not found",
173
+ )
174
+
175
+ @router.delete(
176
+ "/{id}",
177
+ responses=Errors(notification_error),
178
+ )
179
+ def delete_item(id: int):
180
+ """Delete an item."""
181
+ pass
182
+ ```
183
+
184
+ #### StandardErrorDTO
185
+
186
+ Extended implementation for errors with multiple examples (useful for standard HTTP errors):
187
+
188
+ ```python
189
+ from fastapi_errors_plus import Errors, StandardErrorDTO
190
+
191
+ unauthorized_error = StandardErrorDTO(
192
+ status_code=401,
193
+ message="Unauthorized",
194
+ examples={
195
+ "InvalidToken": "Ошибка декодирования токена.",
196
+ "SessionNotFound": "Сессия пользователя не была найдена.",
197
+ },
198
+ )
199
+
200
+ forbidden_error = StandardErrorDTO(
201
+ status_code=403,
202
+ message="Forbidden",
203
+ examples={
204
+ "AccountNotSelected": "Аккаунт не выбран.",
205
+ "RoleHasNoAccess": "Роль не имеет доступа.",
206
+ },
207
+ )
208
+
209
+ @router.delete(
210
+ "/{id}",
211
+ responses=Errors(
212
+ unauthorized_error,
213
+ forbidden_error,
214
+ # validation_error=True - not needed, defaults to True
215
+ ),
216
+ )
217
+ def delete_item(id: int):
218
+ """Delete an item."""
219
+ pass
220
+ ```
221
+
222
+ **Benefits:**
223
+ - ✅ No need to write ErrorDTO classes from scratch
224
+ - ✅ Correct implementation out of the box
225
+ - ✅ Reusable across all endpoints
226
+ - ✅ Supports inheritance for custom logic
227
+
228
+ ### 5. Mixed Usage
149
229
 
150
230
  Combine flags, dict, and ErrorDTO:
151
231
 
@@ -164,6 +244,7 @@ Combine flags, dict, and ErrorDTO:
164
244
  MyErrorDTO(), # ErrorDTO
165
245
  unauthorized=True, # Flag
166
246
  forbidden=True, # Flag
247
+ # validation_error=True - not needed, defaults to True
167
248
  ),
168
249
  )
169
250
  def create_item_mixed(id: int):
@@ -171,7 +252,7 @@ def create_item_mixed(id: int):
171
252
  pass
172
253
  ```
173
254
 
174
- ### 5. Merging Examples
255
+ ### 6. Merging Examples
175
256
 
176
257
  Multiple errors with the same status code are automatically merged:
177
258
 
@@ -212,6 +293,20 @@ class ErrorDTO(Protocol):
212
293
 
213
294
  Any class implementing this protocol (through structural typing) can be used with `Errors()`.
214
295
 
296
+ ### When to Use Protocol vs BaseErrorDTO
297
+
298
+ **Use Protocol (structural typing)** when:
299
+ - Your project already has error DTOs that implement the protocol
300
+ - You need maximum flexibility and custom implementations
301
+ - You want to keep your existing error infrastructure
302
+
303
+ **Use BaseErrorDTO/StandardErrorDTO** when:
304
+ - Starting a new project or adding error documentation
305
+ - You want a ready-to-use implementation without boilerplate
306
+ - You need multiple examples for standard HTTP errors (401, 403, etc.)
307
+
308
+ Both approaches work together — you can mix them in the same `Errors()` call!
309
+
215
310
  ## Compatibility with Existing Projects
216
311
 
217
312
  If your project already has error DTOs (like `ApiErrorDTO`), they can work with `fastapi-errors-plus` if they implement the `ErrorDTO` protocol:
@@ -254,17 +349,28 @@ Errors(
254
349
  *errors: Union[Dict[int, Dict[str, Any]], ErrorDTO],
255
350
  unauthorized: bool = False,
256
351
  forbidden: bool = False,
257
- validation_error: bool = False,
352
+ validation_error: bool = True, # Defaults to True (FastAPI validates all parameters)
258
353
  internal_server_error: bool = False,
354
+ unauthorized_401: bool = False,
355
+ forbidden_403: bool = False,
356
+ validation_error_422: bool = True, # Defaults to True (FastAPI validates all parameters)
357
+ internal_server_error_500: bool = False,
259
358
  )
260
359
  ```
261
360
 
262
361
  **Parameters:**
263
362
  - `*errors`: Arbitrary errors as dict or ErrorDTO objects
264
- - `unauthorized`: Add 401 Unauthorized error
265
- - `forbidden`: Add 403 Forbidden error
266
- - `validation_error`: Add 422 Unprocessable Entity error
267
- - `internal_server_error`: Add 500 Internal Server Error
363
+ - `unauthorized_401`: Add 401 Unauthorized error (recommended, explicit). Defaults to `False`.
364
+ - `forbidden_403`: Add 403 Forbidden error (recommended, explicit). Defaults to `False`.
365
+ - `validation_error_422`: Add 422 Unprocessable Entity error (recommended, explicit). Defaults to `True` (FastAPI validates all parameters).
366
+ - `internal_server_error_500`: Add 500 Internal Server Error (recommended, explicit). Defaults to `False`.
367
+ - `unauthorized`: Add 401 Unauthorized error (legacy, for backward compatibility). Defaults to `False`.
368
+ - `forbidden`: Add 403 Forbidden error (legacy, for backward compatibility). Defaults to `False`.
369
+ - `validation_error`: Add 422 Unprocessable Entity error (legacy, for backward compatibility). Defaults to `True` (FastAPI validates all parameters).
370
+ - `internal_server_error`: Add 500 Internal Server Error (legacy, for backward compatibility). Defaults to `False`.
371
+
372
+ **Why `validation_error=True` by default?**
373
+ FastAPI automatically validates all parameters (Path, Query, Body), so 422 is relevant in 95%+ of endpoints. For endpoints without parameters, explicitly set `validation_error=False` or `validation_error_422=False`.
268
374
 
269
375
  **Returns:**
270
376
  - Callable object that returns `Dict[int, Dict[str, Any]]` in FastAPI responses format
@@ -287,6 +393,48 @@ Protocol for error objects compatible with the library.
287
393
  **Required methods:**
288
394
  - `to_example() -> Dict[str, Any]` — Generate example for OpenAPI
289
395
 
396
+ ### `BaseErrorDTO`
397
+
398
+ Base implementation of ErrorDTO Protocol for convenience.
399
+
400
+ **Constructor:**
401
+ ```python
402
+ BaseErrorDTO(
403
+ status_code: int,
404
+ message: str,
405
+ )
406
+ ```
407
+
408
+ **Example:**
409
+ ```python
410
+ error = BaseErrorDTO(status_code=404, message="Not found")
411
+ ```
412
+
413
+ ### `StandardErrorDTO`
414
+
415
+ Extended implementation for errors with multiple examples.
416
+
417
+ **Constructor:**
418
+ ```python
419
+ StandardErrorDTO(
420
+ status_code: int,
421
+ message: str,
422
+ examples: Optional[Dict[str, str]] = None,
423
+ )
424
+ ```
425
+
426
+ **Example:**
427
+ ```python
428
+ error = StandardErrorDTO(
429
+ status_code=401,
430
+ message="Unauthorized",
431
+ examples={
432
+ "InvalidToken": "Invalid token",
433
+ "SessionNotFound": "Session not found",
434
+ },
435
+ )
436
+ ```
437
+
290
438
  ## Examples
291
439
 
292
440
  ### Example 1: Standard FastAPI Project
@@ -445,7 +593,7 @@ router = APIRouter()
445
593
  responses=Errors(
446
594
  unauthorized=True, # From authentication dependency
447
595
  forbidden=True, # From authorization dependency
448
- validation_error=True, # From FastAPI validation
596
+ # validation_error=True - not needed, defaults to True (FastAPI validates all parameters)
449
597
  ItemAlreadyExistsError(), # Domain error
450
598
  ),
451
599
  )
@@ -0,0 +1,14 @@
1
+ """Universal library for documenting errors in FastAPI endpoints."""
2
+
3
+ from fastapi_errors_plus.base import BaseErrorDTO, StandardErrorDTO
4
+ from fastapi_errors_plus.errors import Errors
5
+ from fastapi_errors_plus.protocol import ErrorDTO
6
+
7
+ __all__ = [
8
+ "Errors",
9
+ "ErrorDTO", # Protocol (for structural typing)
10
+ "BaseErrorDTO", # Base implementation (optional)
11
+ "StandardErrorDTO", # Extended implementation (optional)
12
+ ]
13
+ __version__ = "0.4.0"
14
+
@@ -0,0 +1,122 @@
1
+ """Base implementations of ErrorDTO protocol for convenience."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, Optional
5
+
6
+
7
+ @dataclass
8
+ class BaseErrorDTO:
9
+ """Base implementation of ErrorDTO Protocol.
10
+
11
+ Projects can use this class directly or create their own implementation
12
+ that implements ErrorDTO Protocol through structural typing.
13
+
14
+ This class provides a simple way to create error DTOs without writing
15
+ boilerplate code in every project.
16
+
17
+ Example:
18
+ ```python
19
+ from fastapi_errors_plus import Errors, BaseErrorDTO
20
+
21
+ notification_error = BaseErrorDTO(
22
+ status_code=404,
23
+ message="Notification not found",
24
+ )
25
+
26
+ @router.delete(
27
+ "/{id}",
28
+ responses=Errors(notification_error),
29
+ )
30
+ def delete_item(id: int):
31
+ pass
32
+ ```
33
+ """
34
+ status_code: int
35
+ """HTTP status code for the error."""
36
+
37
+ message: str
38
+ """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
+ """
55
+ return {
56
+ self.message: {
57
+ "value": {"detail": self.message},
58
+ },
59
+ }
60
+
61
+
62
+ @dataclass
63
+ class StandardErrorDTO(BaseErrorDTO):
64
+ """Extended implementation for errors with multiple examples.
65
+
66
+ Useful for standard HTTP errors (401, 403) that can have different
67
+ causes with different messages.
68
+
69
+ Example:
70
+ ```python
71
+ from fastapi_errors_plus import Errors, StandardErrorDTO
72
+
73
+ unauthorized_error = StandardErrorDTO(
74
+ status_code=401,
75
+ message="Unauthorized",
76
+ examples={
77
+ "InvalidToken": "Ошибка декодирования токена.",
78
+ "SessionNotFound": "Сессия пользователя не была найдена.",
79
+ },
80
+ )
81
+
82
+ @router.delete(
83
+ "/{id}",
84
+ responses=Errors(unauthorized_error),
85
+ )
86
+ def delete_item(id: int):
87
+ pass
88
+ ```
89
+ """
90
+ examples: Optional[Dict[str, str]] = field(default=None)
91
+ """Dictionary of examples: {"key": "message"}."""
92
+
93
+ def __post_init__(self) -> None:
94
+ """Initialize examples with default value if not provided."""
95
+ if self.examples is None:
96
+ 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
+