import sys from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from sqlalchemy.orm import DeclarativeBase from sqlalchemy.pool import NullPool from app.config import settings # pytest-asyncio gives each test function its own event loop by default, but # this engine (and its connection pool) is a module-level singleton created # once and reused across every test. asyncpg connections are bound to the # event loop they were opened on, so pooling them across per-test loops # raises "got Future attached to a different loop". NullPool sidesteps this # by opening a fresh connection per checkout instead of reusing one from a # prior loop. Only applied under pytest; production keeps normal pooling. _engine_kwargs = {"echo": False} if "pytest" in sys.modules: _engine_kwargs["poolclass"] = NullPool engine = create_async_engine(settings.database_url, **_engine_kwargs) async_session_maker = async_sessionmaker(engine, expire_on_commit=False) class Base(DeclarativeBase): pass async def get_db() -> AsyncSession: async with async_session_maker() as session: yield session