I am using fastapi (0.95.0), fastapi-users (10.4.2), fastapi-users-db-sqlalchemy (5.0.0) and SQLAlchemy (2.0.10) in my application.
This is a simplified snippet of my code:
engine = create_async_engine(SQLALCHEMY_DATABASE_URL)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session_maker() as session:
yield session
async def get_user_db(session: AsyncSession = Depends(get_async_session)):
yield SQLAlchemyUserDatabase(session, UserModel, OAuthAccount)
@asynccontextmanager
async def lifespan(fapp: FastAPI):
# establish a connection to the database
fapp.state.async_session = await get_user_db().__anext__()
yield
# close the connection to the database
await fapp.state.async_session.close()
await fapp.state.async_session.engine.dispose()
app = FastAPI(lifespan=lifespan)
# Add Routes
# ...
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
When I use Ctrl-C to stop the running uvicorn server, I get the following error trace:
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
^CINFO: Shutting down
INFO: Waiting for application shutdown.
<class 'fastapi_users_db_sqlalchemy.SQLAlchemyUserDatabase'>
ERROR: Traceback (most recent call last):
File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 677, in lifespan
async with self.lifespan_context(app) as maybe_state:
File "/usr/lib/python3.10/contextlib.py", line 206, in __aexit__
await anext(self.gen)
File "/path/to/proj/src/main.py", line 40, in lifespan
await fastapi_app.state.async_session.close()
AttributeError: 'SQLAlchemyUserDatabase' object has no attribute 'close'
ERROR: Application shutdown failed. Exiting.
INFO: Finished server process [37752]
Which is strange, because I am calling close on a variable of type AsyncSession
not SQLAlchemyUserDatabase
, so based on this error message, I change the line statement to reference the session
attribute of the SQLAlchemyUserDatabase
class, and call close()
on the session attribute, as shown below:
await fapp.state.async_session.session.close()
Now, I get this even more cryptic error trace:
INFO: Started server process [33125]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
^CINFO: Shutting down
INFO: Waiting for application shutdown.
ERROR: Traceback (most recent call last):
File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 677, in lifespan
async with self.lifespan_context(app) as maybe_state:
File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 569, in __aexit__
await self._router.shutdown()
File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 664, in shutdown
await handler()
File "/path/to/proj/src/main.py", line 88, in shutdown
await app.state.async_session.session.close()
AttributeError: 'Depends' object has no attribute 'close'
ERROR: Application shutdown failed. Exiting.
fapp.state.async_session.session
should not be of type Depends.
Why is this type error occurring, and how do I resolve it, so that I can gracefully release resources when the server is shutdown?