reagento / dishka

Cute DI framework with agreeable API and everything you need
https://dishka.readthedocs.io
Apache License 2.0
391 stars 40 forks source link

SqlAlchemy AsyncSession resolve failed #145

Closed jonathanblade closed 5 months ago

jonathanblade commented 5 months ago

Hi! I created DbProvider:

AsyncSessionMaker = async_sessionmaker[AsyncSession]

_db_session_ctx: ContextVar[AsyncSession | None] = ContextVar("_db_session_ctx", default=None)

class ContextSessionMaker(AsyncSessionMaker):
    def __call__(self, **local_kw) -> AsyncSession:
        session = _db_session_ctx.get()
        if session is None:
            session = super().__call__(**local_kw)
            _db_session_ctx.set(session)
        return session

class DbProvider(Provider):
    def __init__(self, conn_string: str, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.conn_string = conn_string

    @provide(scope=Scope.APP)
    async def get_engine(self) -> AsyncEngine:
        return create_async_engine(self.conn_string)

    @provide(scope=Scope.APP)
    async def get_sessionmaker(self, engine: AsyncEngine) -> ContextSessionMaker:
        return ContextSessionMaker(engine, class_=AsyncSession, expire_on_commit=False)

    @provide(scope=Scope.REQUEST)
    async def get_db_session(
        self, sessionmaker: ContextSessionMaker
    ) -> AsyncIterable[AsyncSession]:
        async with sessionmaker() as db_session:
            yield db_session

But in tests i got error: dishka.exceptions.NoFactoryError: Cannot find factory for (<class 'sqlalchemy.ext.asyncio.session.AsyncSession'>, component=''). Check scopes in your providers. It is missing or has invalid scope.

@pytest.fixture()
async def container() -> AsyncIterator[dishka.AsyncContainer]:
    db_provider = DbProvider("sqlite+aiosqlite:///:memory:")
    container = dishka.make_async_container(db_provider)
    yield container
    await container.close()

@pytest.fixture()
async def db_session(container: dishka.AsyncContainer) -> AsyncSession:
    return await container.get(AsyncSession)

dishka version: 1.1.1

jonathanblade commented 5 months ago

I realized my mistake. It is wrong scope. Fixture should be like:

@pytest.fixture()
async def db_session(container: dishka.AsyncContainer) -> AsyncSession:
    async with container(scope=Scope.REQUEST) as request_scope_container:
        return await request_scope_container.get(AsyncSession)