mirror of https://github.com/tiangolo/fastapi.git
Add debugger middleware functionality
- Adds middleware class to allow intercepting exceptions in endpoints and starting a debugger in those stack frames - Debugger is customizable by passing in a custom callable that can run debugger of choice - Default debugger if not customized is PDB - Adds support to use web-PDB by including pre made callable that can be passed in as a middleware arg
This commit is contained in:
parent
b254688f37
commit
b95824cfab
|
|
@ -0,0 +1,104 @@
|
|||
import typing
|
||||
|
||||
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
from starlette.types import ASGIApp
|
||||
|
||||
|
||||
async def catch_exceptions_middleware(
|
||||
request: Request, call_next: RequestResponseEndpoint
|
||||
) -> Response:
|
||||
import pdb
|
||||
import sys
|
||||
|
||||
try:
|
||||
return await call_next(request)
|
||||
except Exception as e:
|
||||
sys.last_traceback = e.__traceback__
|
||||
pdb.pm()
|
||||
raise
|
||||
|
||||
|
||||
async def webpdb_catch_exceptions_middleware(
|
||||
request: Request, call_next: RequestResponseEndpoint
|
||||
) -> Response:
|
||||
"""
|
||||
Requires web-pdb package.
|
||||
|
||||
```bash
|
||||
pip install web-pdb
|
||||
```
|
||||
"""
|
||||
import web_pdb # type: ignore
|
||||
|
||||
with web_pdb.catch_post_mortem():
|
||||
return await call_next(request)
|
||||
|
||||
|
||||
class DebuggerMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
Middleware to intercept exceptions and start a debugger in that stack frame.
|
||||
|
||||
The default configured debugger is PDB since it comes packaged with Python.
|
||||
|
||||
Additionally web UI can be added by add web-pdb a dep., or a custom callable
|
||||
be passed through the `start_debugger_func` middleware parameter to run
|
||||
a debugger of choice.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
# Add default (PDB) debugger
|
||||
impost os
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.debugger import DebuggerMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
if os.getenv("ENV") == "LOCAL": # Or something along those lines to check and only run in locally
|
||||
app.add_middleware(DebuggerMiddleware)
|
||||
```
|
||||
|
||||
```python
|
||||
# Add web_PDB debugger
|
||||
impost os
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.debugger import DebuggerMiddleware, webpdb_catch_exceptions_middleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
if os.getenv("ENV") == "LOCAL": # Or something along those lines to check and only run in locally
|
||||
app.add_middleware(DebuggerMiddleware, start_debugger_func=webpdb_catch_exceptions_middleware)
|
||||
```
|
||||
|
||||
Additional notes:
|
||||
A good practice would be to add rules to your linter or use a tool like Pre-commit to remove/ check
|
||||
for forgotten breakpoints left in prod. code.
|
||||
|
||||
Also set `PYTHONBREAKPOINT=0` environment in your prod configs to prevent a python debugger running in
|
||||
prod.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
app: ASGIApp,
|
||||
start_debugger_func: typing.Optional[
|
||||
typing.Callable[
|
||||
[Request, RequestResponseEndpoint],
|
||||
typing.Coroutine[typing.Any, typing.Any, Response],
|
||||
]
|
||||
] = None,
|
||||
) -> None:
|
||||
if start_debugger_func:
|
||||
self.start_debug = start_debugger_func
|
||||
else:
|
||||
self.start_debug = catch_exceptions_middleware
|
||||
super().__init__(app, dispatch=None)
|
||||
|
||||
async def dispatch(
|
||||
self, request: Request, call_next: RequestResponseEndpoint
|
||||
) -> Response:
|
||||
return await self.start_debug(request, call_next)
|
||||
Loading…
Reference in New Issue