"""Database engine/session wiring. SQLite by default (zero setup), Postgres-ready via DATABASE_URL. The engine/session factory is process-global and (re)built by `configure()`, so tests can point it at a temporary database before creating the app. """ from __future__ import annotations import os from collections.abc import Iterator from sqlalchemy import create_engine from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker DEFAULT_URL = "sqlite:///./ams.db" class Base(DeclarativeBase): pass _engine = None _SessionLocal: sessionmaker | None = None def configure(url: str | None = None) -> None: global _engine, _SessionLocal url = url or os.environ.get("DATABASE_URL", DEFAULT_URL) connect_args = {"check_same_thread": False} if url.startswith("sqlite") else {} _engine = create_engine(url, connect_args=connect_args, future=True) _SessionLocal = sessionmaker(bind=_engine, autoflush=False, expire_on_commit=False, class_=Session) def init_db() -> None: from . import models # noqa: F401 - register mappers before create_all if _engine is None: configure() Base.metadata.create_all(_engine) def get_session() -> Session: if _SessionLocal is None: configure() return _SessionLocal() def get_db() -> Iterator[Session]: """FastAPI dependency yielding a request-scoped session.""" db = get_session() try: yield db finally: db.close()