Centralize config through Settings singleton
UPLOAD_DIR and DB_PATH were read directly from os.environ, bypassing the Settings dataclass. This caused an inconsistency where overriding Settings had no effect on these values. Now all modules import from infra.settings.settings.
This commit is contained in:
parent
f4e11645c7
commit
c50106c185
5 changed files with 86 additions and 9 deletions
|
|
@ -21,6 +21,7 @@ class Settings:
|
|||
|
||||
@classmethod
|
||||
def from_env(cls) -> Settings:
|
||||
"""Build a Settings instance from environment variables."""
|
||||
cors_raw = os.environ.get("CORS_ORIGINS", "http://localhost:3000,http://localhost:5173")
|
||||
return cls(
|
||||
app_version=os.environ.get("APP_VERSION", "dev"),
|
||||
|
|
@ -32,3 +33,7 @@ class Settings:
|
|||
db_path=os.environ.get("DB_PATH", "./data/docling_studio.db"),
|
||||
cors_origins=[o.strip() for o in cors_raw.split(",")],
|
||||
)
|
||||
|
||||
|
||||
# Module-level singleton — import this from other modules.
|
||||
settings = Settings.from_env()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||
|
||||
from api.analyses import router as analyses_router
|
||||
from api.documents import router as documents_router
|
||||
from infra.settings import Settings
|
||||
from infra.settings import settings
|
||||
from persistence.database import init_db
|
||||
from services.analysis_service import AnalysisService
|
||||
|
||||
|
|
@ -29,12 +29,6 @@ logging.basicConfig(
|
|||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Settings & dependency wiring
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
settings = Settings.from_env()
|
||||
|
||||
|
||||
def _build_converter():
|
||||
"""Build the converter adapter based on configuration."""
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ from contextlib import asynccontextmanager
|
|||
|
||||
import aiosqlite
|
||||
|
||||
from infra.settings import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DB_PATH = os.environ.get("DB_PATH", "./data/docling_studio.db")
|
||||
DB_PATH = settings.db_path
|
||||
|
||||
_SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS documents (
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ import uuid
|
|||
from pdf2image import convert_from_bytes, pdfinfo_from_bytes
|
||||
|
||||
from domain.models import Document
|
||||
from infra.settings import settings
|
||||
from persistence import analysis_repo, document_repo
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
UPLOAD_DIR = os.environ.get("UPLOAD_DIR", "./uploads")
|
||||
UPLOAD_DIR = settings.upload_dir
|
||||
MAX_FILE_SIZE = 50 * 1024 * 1024 # 50 MB
|
||||
|
||||
|
||||
|
|
|
|||
75
document-parser/tests/test_settings.py
Normal file
75
document-parser/tests/test_settings.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
"""Tests for Settings — environment variable parsing and defaults."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from infra.settings import Settings
|
||||
|
||||
|
||||
class TestSettingsDefaults:
|
||||
def test_default_values(self):
|
||||
s = Settings()
|
||||
assert s.app_version == "dev"
|
||||
assert s.conversion_engine == "local"
|
||||
assert s.docling_serve_url == "http://localhost:5001"
|
||||
assert s.docling_serve_api_key is None
|
||||
assert s.conversion_timeout == 600
|
||||
assert s.upload_dir == "./uploads"
|
||||
assert s.db_path == "./data/docling_studio.db"
|
||||
assert "http://localhost:3000" in s.cors_origins
|
||||
|
||||
def test_frozen(self):
|
||||
"""Settings should be immutable."""
|
||||
import pytest
|
||||
|
||||
s = Settings()
|
||||
with pytest.raises(AttributeError):
|
||||
s.upload_dir = "/other" # type: ignore[misc]
|
||||
|
||||
|
||||
class TestSettingsFromEnv:
|
||||
def test_reads_env_vars(self, monkeypatch):
|
||||
monkeypatch.setenv("APP_VERSION", "1.2.3")
|
||||
monkeypatch.setenv("CONVERSION_ENGINE", "remote")
|
||||
monkeypatch.setenv("DOCLING_SERVE_URL", "http://serve:9000")
|
||||
monkeypatch.setenv("DOCLING_SERVE_API_KEY", "secret-key")
|
||||
monkeypatch.setenv("CONVERSION_TIMEOUT", "120")
|
||||
monkeypatch.setenv("UPLOAD_DIR", "/data/uploads")
|
||||
monkeypatch.setenv("DB_PATH", "/data/test.db")
|
||||
monkeypatch.setenv("CORS_ORIGINS", "http://a.com, http://b.com")
|
||||
|
||||
s = Settings.from_env()
|
||||
|
||||
assert s.app_version == "1.2.3"
|
||||
assert s.conversion_engine == "remote"
|
||||
assert s.docling_serve_url == "http://serve:9000"
|
||||
assert s.docling_serve_api_key == "secret-key"
|
||||
assert s.conversion_timeout == 120
|
||||
assert s.upload_dir == "/data/uploads"
|
||||
assert s.db_path == "/data/test.db"
|
||||
assert s.cors_origins == ["http://a.com", "http://b.com"]
|
||||
|
||||
def test_defaults_when_env_empty(self, monkeypatch):
|
||||
"""When no env vars set, from_env returns sensible defaults."""
|
||||
for key in (
|
||||
"APP_VERSION",
|
||||
"CONVERSION_ENGINE",
|
||||
"DOCLING_SERVE_URL",
|
||||
"DOCLING_SERVE_API_KEY",
|
||||
"CONVERSION_TIMEOUT",
|
||||
"UPLOAD_DIR",
|
||||
"DB_PATH",
|
||||
"CORS_ORIGINS",
|
||||
):
|
||||
monkeypatch.delenv(key, raising=False)
|
||||
|
||||
s = Settings.from_env()
|
||||
|
||||
assert s.app_version == "dev"
|
||||
assert s.conversion_engine == "local"
|
||||
assert s.conversion_timeout == 600
|
||||
|
||||
def test_cors_origins_split(self, monkeypatch):
|
||||
monkeypatch.setenv("CORS_ORIGINS", "http://a.com,http://b.com,http://c.com")
|
||||
s = Settings.from_env()
|
||||
assert len(s.cors_origins) == 3
|
||||
assert s.cors_origins[2] == "http://c.com"
|
||||
Loading…
Reference in a new issue