Skip to content

Commit 8bba952

Browse files
authored
feat: Add generate_swagger_response method to ExceptionHandler to support generating responses with documentation_uris. (#41)
Rather than passing the same documentation_uri_template to all calls of generate_swagger_response, leverage the internal attributes of the ExceptionHandler. closes #38
1 parent 863178e commit 8bba952

File tree

12 files changed

+203
-85
lines changed

12 files changed

+203
-85
lines changed

docs/error.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,10 @@ called with the type, title, status and any additional extras provided when the
149149
error is raised.
150150

151151
```python
152-
add_exception_handler(
153-
app,
152+
eh = new_exception_handler(
154153
documentation_uri_template="https://link-to/my/errors/{type}",
155154
)
155+
add_exception_handler(app, eh)
156156
```
157157

158158
```json
@@ -166,10 +166,10 @@ Where a full resolvable documentation uri does not exist, the rfc allows for a
166166
[tag uri](https://en.wikipedia.org/wiki/Tag_URI_scheme#Format).
167167

168168
```python
169-
add_exception_handler(
170-
app,
171-
documentation_uri_template="tag:my-domain.com,2024-01-01:{type}",
169+
eh = new_exception_handler(
170+
documentation_uri_template="https://link-to/my/errors/{type}",
172171
)
172+
add_exception_handler(app, eh)
173173
```
174174

175175
```json
@@ -188,11 +188,11 @@ in cases where the Problem doesn't explicitly define a `type_` attribute, the
188188
type will default to `about:blank`.
189189

190190
```python
191-
add_exception_handler(
192-
app,
191+
eh = new_exception_handler(
193192
documentation_uri_template="https://link-to/my/errors/{type}",
194193
strict_rfc9457=True,
195194
)
195+
add_exception_handler(app, eh)
196196
```
197197

198198
```json

docs/handlers.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ A custom handler can then be defined in your application.
2424
import fastapi
2525
from rfc9457 import error_class_to_type
2626
from fastapi_problem.error import Problem
27-
from fastapi_problem.handler import ExceptionHandler, add_exception_handler
27+
from fastapi_problem.handler import ExceptionHandler, add_exception_handler, new_exception_handler
2828
from starlette.requests import Request
2929

3030
from third_party.error import CustomBaseError
@@ -39,12 +39,12 @@ def my_custom_handler(eh: ExceptionHandler, request: Request, exc: CustomBaseErr
3939
)
4040

4141
app = fastapi.FastAPI()
42-
add_exception_handler(
43-
app,
42+
eh = new_exception_handler(
4443
handlers={
4544
CustomBaseError: my_custom_handler,
4645
},
4746
)
47+
add_exception_handler(app, eh)
4848
```
4949

5050
Any instance of CustomBaseError, or any subclasses, that reach the exception
@@ -63,7 +63,7 @@ previously defined, but rather than passing it to handlers, use the
6363
```python
6464
import fastapi
6565
from fastapi_problem.error import Problem
66-
from fastapi_problem.handler import ExceptionHandler, add_exception_handler
66+
from fastapi_problem.handler import ExceptionHandler, add_exception_handler, new_exception_handler
6767
from starlette.exceptions import HTTPException
6868
from starlette.requests import Request
6969

@@ -73,10 +73,10 @@ def my_custom_handler(eh: ExceptionHandler, request: Request, exc: HTTPException
7373

7474

7575
app = fastapi.FastAPI()
76-
add_exception_handler(
77-
app,
76+
eh = new_excep, new_exception_handler(
7877
http_exception_handler=my_custom_handler,
7978
)
79+
add_exception_handler(app, eh)
8080
```
8181

8282
### Optional handling
@@ -90,7 +90,7 @@ will be pass to the next defined handler.
9090
import fastapi
9191
from rfc9457 import error_class_to_type
9292
from fastapi_problem.error import Problem
93-
from fastapi_problem.handler import ExceptionHandler, add_exception_handler
93+
from fastapi_problem.handler import ExceptionHandler, add_exception_handler, new_exception_handler
9494
from starlette.requests import Request
9595

9696
def no_response_handler(eh: ExceptionHandler, request: Request, exc: RuntimeError) -> Problem | None:
@@ -112,13 +112,13 @@ def base_handler(eh: ExceptionHandler, request: Request, exc: Exception) -> Prob
112112
)
113113

114114
app = fastapi.FastAPI()
115-
add_exception_handler(
116-
app,
115+
eh = new_exception_handler(
117116
handlers={
118117
RuntimeError: no_response_handler,
119118
Exception: base_handler,
120119
},
121120
)
121+
add_exception_handler(app, eh)
122122
```
123123

124124
At the time of writing there was (is?) a

docs/hooks.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ for informational purposes such as logging or debugging.
1212
import logging
1313

1414
import fastapi
15-
from fastapi_problem.handler import add_exception_handler
15+
from fastapi_problem.handler import add_exception_handler, new_exception_handler
1616
from starlette.requests import Request
1717

1818
logger = logging.getLogger(__name__)
@@ -24,10 +24,10 @@ def custom_hook(request: Request, exc: Exception) -> None:
2424

2525

2626
app = fastapi.FastAPI()
27-
add_exception_handler(
28-
app,
27+
eh = new_exception_handler(
2928
pre_hooks=[custom_hook],
3029
)
30+
add_exception_handler(app, eh)
3131
```
3232

3333
## Post Hooks
@@ -40,7 +40,7 @@ xml api etc, the raw content can be reprocessed.).
4040

4141
```python
4242
import fastapi
43-
from fastapi_problem.handler import add_exception_handler
43+
from fastapi_problem.handler import add_exception_handler, new_exception_handler
4444
from starlette.requests import Request
4545
from starlette.responses import Response
4646

@@ -55,8 +55,8 @@ def custom_hook(content: dict, request: Request, response: Response) -> Response
5555

5656

5757
app = fastapi.FastAPI()
58-
add_exception_handler(
59-
app,
58+
eh = new_exception_handler(
6059
post_hooks=[custom_hook],
6160
)
61+
add_exception_handler(app, eh)
6262
```

docs/usage.md

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,23 @@ import fastapi_problem.handler
66

77

88
app = fastapi.FastAPI()
9-
fastapi_problem.handler.add_exception_handler(app)
9+
eh = fastapi_problem.handler.new_exception_handler()
10+
fastapi_problem.handler.add_exception_handler(app, eh)
1011
```
1112

1213
A custom logger can be provided using:
1314

1415
```python
15-
add_exception_handler(
16-
app,
16+
new_exception_handler(
1717
logger=...,
1818
)
1919
```
2020

2121
If you require cors headers, you can pass a `fastapi_problem.cors.CorsConfiguration`
22-
instance to `add_exception_handler(cors=...)`.
22+
instance to `new_exception_handler(cors=...)`.
2323

2424
```python
25-
add_exception_handler(
26-
app,
25+
new_exception_handler(
2726
cors=CorsConfiguration(
2827
allow_origins=["*"],
2928
allow_methods=["*"],
@@ -40,18 +39,18 @@ for all unhandled exceptions.
4039

4140
```python
4241
from fastapi_problem.error import StatusProblem
43-
from fastapi_problem.handler import add_exception_handler
42+
from fastapi_problem.handler import add_exception_handler, new_exception_handler
4443

4544
class NotFoundError(StatusProblem):
4645
status = 404
4746
message = "Endpoint not found."
4847

49-
add_exception_handler(
50-
app,
48+
eh = new_exception_handler(
5149
unhandled_wrappers={
5250
"404": NotFoundError,
5351
},
5452
)
53+
add_exception_handler(app, eh)
5554
```
5655

5756
If you wish to hide debug messaging from external users, `StripExtrasPostHook`
@@ -64,10 +63,9 @@ allow extras for specific status codes. Allowing expected fields to reach the
6463
user, while suppressing unexpected server errors etc.
6564

6665
```python
67-
from fastapi_problem.handler import StripExtrasPostHook, add_exception_handler
66+
from fastapi_problem.handler import StripExtrasPostHook, add_exception_handler, new_exception_handler
6867

69-
add_exception_handler(
70-
app,
68+
eh = new_exception_handler(
7169
post_hooks=[
7270
StripExtrasPostHook(
7371
mandatory_fields=["type", "title", "status", "detail", "custom-extra"],
@@ -76,6 +74,7 @@ add_exception_handler(
7674
)
7775
],
7876
)
77+
add_exception_handler(app, eh)
7978
```
8079

8180
## Swagger
@@ -88,10 +87,10 @@ opted out of by passing `generic_swagger_defaults=False` when registering the
8887
exception handlers.
8988

9089
```python
91-
add_exception_handler(
92-
app,
90+
eh = new_exception_handler(
9391
generic_swagger_defaults=False,
9492
)
93+
add_exception_handler(app, eh)
9594
```
9695

9796
To specify specific error responses per endpoint, when registering the route
@@ -108,9 +107,12 @@ class NotFoundError(StatusProblem):
108107
title = "Endpoint not found."
109108
110109
110+
eh = new_exception_handler()
111+
add_exception_handler(app, eh)
112+
111113
@app.post(
112114
"/path",
113-
responses={400: generate_swagger_response(NotFoundError)}},
115+
responses={400: eh.generate_swagger_response(NotFoundError)}},
114116
)
115117
...
116118
```

examples/auth.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
ForbiddenProblem,
1919
UnauthorisedProblem,
2020
)
21-
from fastapi_problem.handler import add_exception_handler
21+
from fastapi_problem.handler import add_exception_handler, new_exception_handler
2222

2323

2424
class AuthorizationRequiredError(UnauthorisedProblem):
@@ -46,9 +46,8 @@ async def check_auth(
4646

4747
app = fastapi.FastAPI()
4848

49-
add_exception_handler(
50-
app,
51-
)
49+
eh = new_exception_handler()
50+
add_exception_handler(app, eh)
5251

5352

5453
@app.get("/authorized")

examples/basic.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import fastapi
1515

1616
from fastapi_problem.error import BadRequestProblem, ServerProblem
17-
from fastapi_problem.handler import add_exception_handler
17+
from fastapi_problem.handler import add_exception_handler, new_exception_handler
1818

1919
logging.getLogger("uvicorn.error").disabled = True
2020

@@ -29,9 +29,8 @@ class KnownServerProblem(ServerProblem):
2929

3030
app = fastapi.FastAPI()
3131

32-
add_exception_handler(
33-
app,
34-
)
32+
eh = new_exception_handler()
33+
add_exception_handler(app, eh)
3534

3635

3736
@app.get("/user-error")

examples/builtin.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,14 @@
2323
import fastapi
2424
import pydantic
2525

26-
from fastapi_problem.handler import add_exception_handler
26+
from fastapi_problem.handler import add_exception_handler, new_exception_handler
2727

2828
logging.getLogger("uvicorn.error").disabled = True
2929

3030
app = fastapi.FastAPI()
3131

32-
add_exception_handler(
33-
app,
34-
)
32+
eh = new_exception_handler()
33+
add_exception_handler(app, eh)
3534

3635

3736
@app.get("/validation-error")

examples/custom.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import fastapi
2121

22-
from fastapi_problem.handler import add_exception_handler
22+
from fastapi_problem.handler import add_exception_handler, new_exception_handler
2323
from fastapi_problem.error import NotFoundProblem, ServerProblem, StatusProblem, UnprocessableProblem
2424

2525
logging.getLogger("uvicorn.error").disabled = True
@@ -44,15 +44,15 @@ class CustomServer(ServerProblem):
4444

4545
app = fastapi.FastAPI()
4646

47-
add_exception_handler(
48-
app,
47+
eh = new_exception_handler(
4948
unhandled_wrappers={
5049
"404": CustomNotFound,
5150
"405": CustomNotAllowed,
5251
"422": CustomValidation,
5352
"default": CustomServer,
5453
},
5554
)
55+
add_exception_handler(app, eh)
5656

5757

5858
@app.get("/validation-error")

examples/override.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import fastapi
1515
from starlette.exceptions import HTTPException
1616

17-
from fastapi_problem.handler import ExceptionHandler, add_exception_handler
17+
from fastapi_problem.handler import ExceptionHandler, add_exception_handler, new_exception_handler
1818
from fastapi_problem.error import NotFoundProblem, Problem, ServerProblem, StatusProblem
1919

2020
logging.getLogger("uvicorn.error").disabled = True
@@ -48,10 +48,10 @@ def http_exception_handler(
4848

4949
app = fastapi.FastAPI()
5050

51-
add_exception_handler(
52-
app,
51+
eh = new_exception_handler(
5352
http_exception_handler=http_exception_handler,
5453
)
54+
add_exception_handler(app, eh)
5555

5656

5757
@app.post("/not-allowed")

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ select = ["ALL"]
116116
ignore = [
117117
"ANN002", # ParamSpec not available in 3.9
118118
"ANN003", # ParamSpec not available in 3.9
119+
"E501", # Handled by ruff format
119120
"FIX", # allow TODO
120121
"D",
121122
]

0 commit comments

Comments
 (0)