-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support starlette "lifespan" context for application #2943
Comments
@uSpike Hi! Did you look into asgi-lifespan package? |
@juntatalor I did not look in to asgi-lifespan. It seems strange to use something outside of starlette, when starlette already supports the lifespan context: from starlette import Starlette
async def lifespan(app):
async with SomeResource():
yield
app = Starlette(lifespan=lifespan) What I'm looking for in this request is to have the starlette functionality exposed in fastapi. |
See also issue #617 (which is about potential ways to use lifespan with FastAPI's dependency injection system and potential APIs for that) and encode/starlette#799 (which implemented the lifespan context manager). |
I use such solution: from copy import deepcopy
from fastapi import Depends, Request, WebSocket
from merge_arge import merge_args
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.types import Receive, Scope, Send
class AppLifetimeDependencyMiddleware(BaseHTTPMiddleware):
def __init__(self, **kwargs):
kwargs = dict(kwargs)
new_kwargs = {}
for key in ('app', 'dispatch'):
if key in kwargs:
new_kwargs[key] = kwargs.pop(key)
self.__storage = {}
self.__kwargs = kwargs or {}
super().__init__(**new_kwargs)
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
scope['app_lifetime_dependency'] = self.__storage
scope['app_lifetime_dependency_kwargs'] = deepcopy(self.__kwargs)
if scope["type"] != "http":
await self.app(scope, receive, send)
return
request = Request(scope, receive=receive)
response = await self.dispatch_func(request, self.call_next)
await response(scope, receive, send)
async def dispatch(self, request, call_next):
return await call_next(request)
def app_lifetime_dependency(f):
key = id(f)
@merge_args(f)
async def wrapped(*args, request: Request = None, websocket: WebSocket = None, **kwargs):
req = request
if request is None:
req = websocket
if req is None:
return f(*args, **kwargs) # if none of request or websocket, then it's a direct call
if key not in req.scope['app_lifetime_dependency']:
result = await f(*args, **kwargs)
# TODO add check for async_generator and proper handling
req.scope['app_lifetime_dependency'][key] = result
return req.scope['app_lifetime_dependency'][key]
return wrapped
def get_config(request: Request):
return request.scope['app_lifetime_dependency_kwargs']['config']
@app_lifetime_dependency
def get_db_pool(config = Depends(get_config)):
conn = init_db_connection(config)
return conn.pool()
def main():
...
config = load_config_from_file()
app.add_middleware(
AppLifetimeDependencyMiddleware,
config=config
)
|
Thank you @uSpike! And thanks for the PR implementing it. 🚀 🍰 This will be available in FastAPI
|
Not having the @app.lifespan
async def lifespan(app: FastAPI) -> None:
... |
First check
Description
Starlette added support for the lifespan context function in 0.13.5. It's not well documented, but the code does suggest:
For my purposes, it's much more convenient to use a (async)contextmanager for the startup and shutdown events of my application. It would be nice to have the option
The solution you would like
I'd like an easier way, and one that's officially supported, to use the lifespan context function.
or
Describe alternatives you've considered
I can already accomplish this simply by doing:
however this is not officially supported and would likely break if accidentally using
app.on_event
in addition.One could also do nasty stuff with
__aenter__
and__aexit__
:but that seems quite ugly to me.
Environment
The text was updated successfully, but these errors were encountered: