"""App factory and lifespan.""" import logging from contextlib import asynccontextmanager from fastapi import FastAPI from app.api import health as health_router from app.api import settings as settings_router from app.api import slabs as slabs_router from app.config import Settings from app.db.engine import close_db_connection, init_db from app.logging_config import configure_logging @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan: startup and shutdown hooks. Handles database initialization, logging configuration, and cleanup on shutdown. """ configure_logging() logging.info("SlabUploader starting up") await init_db() yield logging.info("SlabUploader shutting down") await close_db_connection() def create_app(app_settings: Settings | None = None) -> FastAPI: """Create a fresh FastAPI app instance. Each call produces an independent app with its own database connection and cleared dependency overrides. This is the factory used by tests. Args: app_settings: Application settings. Uses env vars if None. Returns: Configured FastAPI application. """ if app_settings is None: app_settings = Settings() app_settings.validate() app = FastAPI(title="SlabUploader", version="0.1.0", lifespan=lifespan) # Store settings on app state for dependency injection app.state.settings = app_settings app.include_router(health_router.router) app.include_router(settings_router.router) app.include_router(slabs_router.router) return app