@@ -78,79 +78,116 @@ class State(TypedDict):
7878 fastsqla_engine : AsyncEngine
7979
8080
81- @asynccontextmanager
82- async def lifespan (app : FastAPI ) -> AsyncGenerator [State , None ]:
83- """Use `fastsqla.lifespan` to set up SQLAlchemy.
84-
85- In an ASGI application, [lifespan events](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)
86- are used to communicate startup & shutdown events.
81+ def new_lifespan (url : str | None = None , ** kw ):
82+ """Create a new lifespan async context manager.
8783
88- The [`lifespan`](https://fastapi.tiangolo.com/advanced/events/#lifespan) parameter of
89- the `FastAPI` app can be assigned to a context manager, which is opened when the app
90- starts and closed when the app stops.
84+ It expects the exact same parameters as
85+ [`sqlalchemy.ext.asyncio.create_async_engine`][sqlalchemy.ext.asyncio.create_async_engine]
9186
92- In order for `FastSQLA` to setup `SQLAlchemy` before the app is started, set
93- `lifespan` parameter to `fastsqla.lifespan`:
87+ Example:
9488
9589 ```python
9690 from fastapi import FastAPI
97- from fastsqla import lifespan
91+ from fastsqla import new_lifespan
9892
93+ lifespan = new_lifespan(
94+ "sqlite+aiosqlite:///app/db.sqlite"), connect_args={"autocommit": False}
95+ )
9996
10097 app = FastAPI(lifespan=lifespan)
10198 ```
10299
103- If multiple lifespan contexts are required, create an async context manager function
104- to handle them and set it as the app's lifespan:
100+ Args:
101+ url (str): Database url.
102+ kw (dict): Configuration parameters as expected by [`sqlalchemy.ext.asyncio.create_async_engine`][sqlalchemy.ext.asyncio.create_async_engine]
103+ """
105104
106- ```python
107- from collections.abc import AsyncGenerator
108- from contextlib import asynccontextmanager
105+ has_config = url is not None
109106
110- from fastapi import FastAPI
111- from fastsqla import lifespan as fastsqla_lifespan
112- from this_other_library import another_lifespan
107+ @asynccontextmanager
108+ async def lifespan (app : FastAPI ) -> AsyncGenerator [State , None ]:
109+ if has_config :
110+ prefix = ""
111+ sqla_config = {** kw , ** {"url" : url }}
113112
113+ else :
114+ prefix = "sqlalchemy_"
115+ sqla_config = {k .lower (): v for k , v in os .environ .items ()}
114116
115- @asynccontextmanager
116- async def lifespan(app:FastAPI) -> AsyncGenerator[dict, None]:
117- async with AsyncExitStack() as stack:
118- yield {
119- **stack.enter_async_context(lifespan(app)),
120- **stack.enter_async_context(another_lifespan(app)),
121- }
117+ try :
118+ engine = async_engine_from_config (sqla_config , prefix = prefix )
122119
120+ except KeyError as exc :
121+ raise Exception (f"Missing { prefix } { exc .args [0 ]} in environ." ) from exc
123122
124- app = FastAPI(lifespan=lifespan)
125- ```
123+ async with engine . begin () as conn :
124+ await conn . run_sync ( Base . prepare )
126125
127- To learn more about lifespan protocol:
126+ SessionFactory . configure ( bind = engine )
128127
129- * [Lifespan Protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)
130- * [Use Lifespan State instead of `app.state`](https://github.com/Kludex/fastapi-tips?tab=readme-ov-file#6-use-lifespan-state-instead-of-appstate)
131- * [FastAPI lifespan documentation](https://fastapi.tiangolo.com/advanced/events/)
132- """
133- prefix = "sqlalchemy_"
134- sqla_config = {k .lower (): v for k , v in os .environ .items ()}
135- try :
136- engine = async_engine_from_config (sqla_config , prefix = prefix )
128+ await logger .ainfo ("Configured SQLAlchemy." )
129+
130+ yield {"fastsqla_engine" : engine }
131+
132+ SessionFactory .configure (bind = None )
133+ await engine .dispose ()
134+
135+ await logger .ainfo ("Cleared SQLAlchemy config." )
136+
137+ return lifespan
137138
138- except KeyError as exc :
139- raise Exception (f"Missing { prefix } { exc .args [0 ]} in environ." ) from exc
140139
141- async with engine . begin () as conn :
142- await conn . run_sync ( Base . prepare )
140+ lifespan = new_lifespan ()
141+ """Use `fastsqla.lifespan` to set up SQLAlchemy directly from environment variables.
143142
144- SessionFactory .configure (bind = engine )
143+ In an ASGI application, [lifespan events](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)
144+ are used to communicate startup & shutdown events.
145145
146- await logger .ainfo ("Configured SQLAlchemy." )
146+ The [`lifespan`](https://fastapi.tiangolo.com/advanced/events/#lifespan) parameter of
147+ the `FastAPI` app can be assigned to a context manager, which is opened when the app
148+ starts and closed when the app stops.
147149
148- yield {"fastsqla_engine" : engine }
150+ In order for `FastSQLA` to setup `SQLAlchemy` before the app is started, set
151+ `lifespan` parameter to `fastsqla.lifespan`:
149152
150- SessionFactory .configure (bind = None )
151- await engine .dispose ()
153+ ```python
154+ from fastapi import FastAPI
155+ from fastsqla import lifespan
156+
157+
158+ app = FastAPI(lifespan=lifespan)
159+ ```
160+
161+ If multiple lifespan contexts are required, create an async context manager function
162+ to handle them and set it as the app's lifespan:
163+
164+ ```python
165+ from collections.abc import AsyncGenerator
166+ from contextlib import asynccontextmanager
167+
168+ from fastapi import FastAPI
169+ from fastsqla import lifespan as fastsqla_lifespan
170+ from this_other_library import another_lifespan
152171
153- await logger .ainfo ("Cleared SQLAlchemy config." )
172+
173+ @asynccontextmanager
174+ async def lifespan(app:FastAPI) -> AsyncGenerator[dict, None]:
175+ async with AsyncExitStack() as stack:
176+ yield {
177+ **stack.enter_async_context(lifespan(app)),
178+ **stack.enter_async_context(another_lifespan(app)),
179+ }
180+
181+
182+ app = FastAPI(lifespan=lifespan)
183+ ```
184+
185+ To learn more about lifespan protocol:
186+
187+ * [Lifespan Protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)
188+ * [Use Lifespan State instead of `app.state`](https://github.com/Kludex/fastapi-tips?tab=readme-ov-file#6-use-lifespan-state-instead-of-appstate)
189+ * [FastAPI lifespan documentation](https://fastapi.tiangolo.com/advanced/events/)
190+ """
154191
155192
156193@asynccontextmanager
0 commit comments