Saturday, 15 July 2023

fastapi lifespan closing session raises AttributeError: 'SQLAlchemyUserDatabase' object has no attribute 'close'

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?



from fastapi lifespan closing session raises AttributeError: 'SQLAlchemyUserDatabase' object has no attribute 'close'

No comments:

Post a Comment