Files
duty-teller/duty_teller/db/session.py
Nikolay Tatarinov 28973489a5 Refactor project structure and enhance Docker configuration
- Updated `.dockerignore` to exclude test and development artifacts, optimizing the Docker image size.
- Refactored `main.py` to delegate execution to `duty_teller.run.main()`, simplifying the entry point.
- Introduced a new `duty_teller` package to encapsulate core functionality, improving modularity and organization.
- Enhanced `pyproject.toml` to define a script for running the application, streamlining the execution process.
- Updated README documentation to reflect changes in project structure and usage instructions.
- Improved Alembic environment configuration to utilize the new package structure for database migrations.
2026-02-18 13:03:14 +03:00

58 lines
1.7 KiB
Python

"""SQLAlchemy engine and session factory.
Engine and session factory are cached globally per process. Only one DATABASE_URL
is effectively used for the process lifetime. Using a different URL later (e.g. in
tests with in-memory SQLite) would still use the first engine. To use a different
URL in tests, set env (e.g. DATABASE_URL) before the first import of this module, or
clear _engine and _SessionLocal in test fixtures. Prefer session_scope() for all
callers so sessions are always closed and rolled back on error.
"""
from contextlib import contextmanager
from typing import Generator
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
_engine = None
_SessionLocal = None
@contextmanager
def session_scope(database_url: str) -> Generator[Session, None, None]:
"""Context manager: yields a session, rolls back on exception, closes on exit."""
session = get_session(database_url)
try:
yield session
except Exception:
session.rollback()
raise
finally:
session.close()
def get_engine(database_url: str):
global _engine
if _engine is None:
_engine = create_engine(
database_url,
connect_args={"check_same_thread": False}
if "sqlite" in database_url
else {},
echo=False,
)
return _engine
def get_session_factory(database_url: str) -> sessionmaker[Session]:
global _SessionLocal
if _SessionLocal is None:
engine = get_engine(database_url)
_SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
return _SessionLocal
def get_session(database_url: str) -> Session:
return get_session_factory(database_url)()