"""Async SQLAlchemy engine/session setup. Works against Postgres (`postgresql+psycopg://...`) or SQLite (`sqlite+aiosqlite://...`) depending on `DATABASE_URL` - the same models and repositories run against either, which is what makes the no-Docker local dev path possible. """ from __future__ import annotations from collections.abc import AsyncGenerator from functools import lru_cache from sqlalchemy.ext.asyncio import ( AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine, ) from app.core.config import get_settings @lru_cache def get_engine() -> AsyncEngine: settings = get_settings() connect_args = {} if settings.database_url.startswith("sqlite"): connect_args = {"check_same_thread": False} return create_async_engine( settings.database_url, echo=False, pool_pre_ping=True, connect_args=connect_args, ) @lru_cache def get_sessionmaker() -> async_sessionmaker[AsyncSession]: return async_sessionmaker(bind=get_engine(), expire_on_commit=False) async def get_db() -> AsyncGenerator[AsyncSession, None]: session_factory = get_sessionmaker() async with session_factory() as session: try: yield session except Exception: await session.rollback() raise