Coverage for src / beaverbunch / network / config.py: 100.0%
31 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 00:24 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 00:24 +0000
1from __future__ import annotations
3import os
4from dataclasses import dataclass
6from beaverbunch import __version__
8_DEFAULT_CORS_ORIGINS = (
9 "http://localhost:3000",
10 "http://127.0.0.1:3000",
11)
14@dataclass(frozen=True)
15class RuntimeConfig:
16 app_name: str = "Beaverbunch Lobby API"
17 app_version: str = __version__
18 host: str = "0.0.0.0"
19 port: int = 8000
20 cors_origins: tuple[str, ...] = _DEFAULT_CORS_ORIGINS
21 max_sessions: int = 25
22 log_level: str = "info"
25def load_runtime_config() -> RuntimeConfig:
26 return RuntimeConfig(
27 host=os.getenv("BEAVERBUNCH_HOST", RuntimeConfig.host),
28 port=_read_int_env("BEAVERBUNCH_PORT", RuntimeConfig.port, minimum=1),
29 cors_origins=_read_cors_origins(),
30 max_sessions=_read_int_env("BEAVERBUNCH_MAX_SESSIONS", RuntimeConfig.max_sessions, minimum=1),
31 log_level=os.getenv("BEAVERBUNCH_LOG_LEVEL", RuntimeConfig.log_level).lower(),
32 )
35def _read_int_env(name: str, default: int, *, minimum: int) -> int:
36 raw_value = os.getenv(name)
37 if raw_value is None or not raw_value.strip():
38 return default
40 try:
41 value = int(raw_value)
42 except ValueError as exc: # pragma: no cover - exercised through caller tests
43 raise ValueError(f"Environment variable {name} must be an integer") from exc
45 if value < minimum:
46 raise ValueError(f"Environment variable {name} must be >= {minimum}")
47 return value
50def _read_cors_origins() -> tuple[str, ...]:
51 raw_value = os.getenv("BEAVERBUNCH_CORS_ORIGINS")
52 if raw_value is None:
53 return _DEFAULT_CORS_ORIGINS
55 origins = tuple(origin.strip() for origin in raw_value.split(",") if origin.strip())
56 return origins