from collections.abc import Generator from sqlalchemy import create_engine from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, declarative_base, sessionmaker Base = declarative_base() # Engine and session factory are created lazily so that tests can # override DATABASE_URL via environment *before* any import triggers # the real PostgreSQL engine creation (which requires psycopg2). _engine = None _SessionLocal = None def _get_engine() -> Engine: global _engine if _engine is None: from app.config import settings url = settings.DATABASE_URL kwargs: dict = {} if url.startswith("postgresql"): kwargs.update( pool_size=20, max_overflow=10, pool_recycle=3600, pool_pre_ping=True, ) _engine = create_engine(url, **kwargs) return _engine def _get_session_factory() -> sessionmaker: global _SessionLocal if _SessionLocal is None: _SessionLocal = sessionmaker( autocommit=False, autoflush=False, bind=_get_engine() ) return _SessionLocal class _LazySessionLocal: """Proxy so ``SessionLocal()`` keeps working as before but the real sessionmaker is only created on first call.""" def __call__(self, *args: object, **kwargs: object) -> Session: return _get_session_factory()(*args, **kwargs) def __getattr__(self, name: str) -> object: return getattr(_get_session_factory(), name) SessionLocal = _LazySessionLocal() class _EngineProxy: """Thin proxy so ``from app.database import engine`` still works.""" def __getattr__(self, name: str) -> object: return getattr(_get_engine(), name) engine = _EngineProxy() # type: ignore[assignment] def get_db() -> Generator[Session, None, None]: db = SessionLocal() try: yield db finally: db.close()