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.
This commit is contained in:
57
duty_teller/db/session.py
Normal file
57
duty_teller/db/session.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""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)()
|
||||
Reference in New Issue
Block a user