Skip to content

Impossible to integrate with context managers? #868

Open
@SergeAvgust

Description

@SergeAvgust

I am trying to integrate the latest version of the library in my project, and i have faced the issue i can't find a solution for - i cannot make the cleanup work for usage mid-function.

Most of the time my usage of containers/providers is highly conventional and matches FastAPI+SQLAlchemy example. So here's some simplified examples. The main container and the database containers are declared this way:

import example_routes

class DBManager:
    """Manages database connection and migrations."""
    _engine: AsyncEngine = None
    _sessionmaker: async_sessionmaker = None
    
    ... # connect and dispose logic

async def get_session():
    async with DBManager._sessionmaker() as session:
        session: AsyncSession
        try:
            yield session
            await session.commit()
        except Exception as e:
            await session.rollback()
            logger.critical(e)
            raise
        finally:
            await session.close()

class DatabaseContainer(containers.DeclarativeContainer):
    session_factory = providers.Resource(
        get_session
    )
        transaction_service = providers.Factory(
        TxService,
        session=session_factory
    )


class MainContainer(containers.DeclarativeContainer):
    wiring_config = containers.WiringConfiguration(
        modules=[
            example_routes
        ]
    )

    config = providers.Configuration()

    db = providers.Container(
        DatabaseContainer,
        config=config.run
    )

Meanwhile the usage mostly works like that:

@inject
async def some_business_logic(
    tx_service: TxService = Closing[Provide[MainContainer.db.transaction_service]],
):
    ...

HOWEVER.

I have a function with a long execution time, which makes several calls to the database and each of these calls needs to be its own session (cause i update database records and need to do that in real-time).
When i need to use the service in some kind of limited span inside of a function, i encounter a bug(?) where it does not trigger cleanup after the session was used. i have tried several approaches and none of them seem to work. Here is what i want to do:

async def long_function():
    async with MainContainer.db.transaction_service as service:
        await service.funcion_a()
    # session is commited and closed during cleanup
    # more logic

    async with MainContainer.db.transaction_service as service:
        await service.function_b()
    # new session is commited and closed during cleanup

I have tried creating service context managed functions, somewhat like below, but they do not trigger cleanup

@asynccontextmanager
@inject
async def get_tx_service_ctx(
    tx_service: TxService = Closing[Provide[MainContainer.db.session_factory]]
) -> AsyncGenerator[TxService, None]:
    # somewhy under contextmanager container provides a future
    serv = await tx_service 
    yield serv
    print(serv)

The print at the end is triggered, but the cleanup of a session doesn't seem to be. What should i do? Can i make some kind of a workaround for this? I can't seem to find an alternative way in the docs nor in the examples

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions