Programmatically wrap all routes in APIRouter in custom decorator(s) #9244
-
First Check
Commit to Help
Example Codeimport random
import time
import warnings
import uvicorn
from fastapi import APIRouter, FastAPI
from fastapi.dependencies.utils import get_dependant
from pydantic import BaseModel
app = FastAPI()
router = APIRouter()
class Alert(BaseModel):
message: str
alert_type: str
def deep_stacked_func():
'''
Might be called by other functions, be called outside a route and be declared on a different package.
Don't want to have to receive a request object to have the session that the call belongs.
'''
n = random.randint(1, 10)
if n <= 8:
# do function stuff
alert = Alert(message='/warnings route', alert_type='error')
warnings.warn(Warning(alert))
return 'function stuff ' * n # must be returned, even after warnings.warn call
def alerts_decorator(func):
async def wrapper():
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
response = await func()
warns = []
for warn in caught_warnings:
warn_message = warn.message.args[0]
if isinstance(warn_message, Alert):
warns.append(
{'message': warn_message.message, 'type': warn_message.alert_type})
if warns:
response.update({'alerts': warns})
return response
return wrapper
@router.get("/warnings")
async def warnings_route():
function_stuff = deep_stacked_func()
# do route stuff, with function stuff
time.sleep(random.random())
return {"return": "route stuff"}
@router.get("/no_warnings")
async def no_warnings_route():
time.sleep(random.random())
return {"return": "no warning route stuff"}
for route in router.routes:
route.endpoint = alerts_decorator(route.endpoint)
route.dependendant = get_dependant(
path=route.path_format, call=route.endpoint)
app.include_router(router)
if __name__ == '__main__':
uvicorn.run('app:app', host='0.0.0.0', port=8000, reload=True)DescriptionAfter an issue with I just did not want to go back to my API and wrap every route endpoint function in this new decorator, nor I'd like to have me or anyone else to remember to do so in new routes. I achieved the expected behavior by looping through all routes inside the router and editing the It would be nice to be able to do so in the router instantiation, as with prefix, or some argument on the inclusion of the Operating SystemWindows Operating System DetailsNo response FastAPI Version0.94.0 Python Version3.10.7 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
Hey there! You shouldn't try to use For what you are doing, if you want to capture warnings per-request in a similar way, you would have to create your own utils equivalent to:
|
Beta Was this translation helpful? Give feedback.
Hey there!
You shouldn't try to use
warnings.catch_warnings()in anything that expects to run more than one thing during the same period of time, like a web API handling requests. It is not thread-safe, so it's not concurrent-safe, it's only for smaller or simpler scripts, during testing or so. You can read more in the official docs for warnings, in the last note: https://docs.python.org/3/library/warnings.html#available-context-managersFor what you are doing, if you want to capture warnings per-request in a similar way, you would have to create your own utils equivalent to: