Environment Configuration
Overview
Pattern de configuration utilisant une dataclass frozen avec valeurs par défaut issues des variables d'environnement. Chargement automatique du fichier .env et validation au démarrage.
File Structure
code
src/cuve-api/ ├── app/ │ ├── config.py # Settings dataclass ├── .env # Variables locales (non versionné) ├── .env.example # Template des variables
Implementation Pattern
Dataclass Settings
python
from dataclasses import dataclass
import os
from dotenv import load_dotenv
# Charge .env si présent (local), sinon pas grave (prod via variables d'env)
load_dotenv()
@dataclass(frozen=True)
class Settings:
# Mode : real = appelle le capteur ESP ; sim = capteur simulé
mode: str = os.getenv("CUVE_MODE", "real").strip().lower()
# URL du capteur (uniquement nécessaire en mode real)
sensor_url: str | None = os.getenv("CUVE_SENSOR_URL")
# Paramètres client HTTP
cache_ttl_seconds: int = int(os.getenv("CUVE_CACHE_TTL_SECONDS", "10"))
http_timeout_seconds: float = float(os.getenv("CUVE_HTTP_TIMEOUT_SECONDS", "2.0"))
# Paramètres DB et collecte
db_path: str = os.getenv("CUVE_DB_PATH", "cuve.sqlite3")
collect_interval_seconds: int = int(os.getenv("CUVE_COLLECT_INTERVAL_SECONDS", "60"))
# Paramètres cuve (géométrie)
tank_total_volume_liters: float = float(os.getenv("CUVE_TANK_TOTAL_LITERS", "10000"))
tank_diameter_cm: float = float(os.getenv("CUVE_TANK_DIAMETER_CM", "184.5"))
tank_length_cm: float = float(os.getenv("CUVE_TANK_LENGTH_CM", "436.4"))
tank_full_air_gap_cm: float = float(os.getenv("CUVE_TANK_FULL_AIR_GAP_CM", "20"))
settings = Settings()
Validation au démarrage
python
if settings.mode not in ("real", "sim"):
raise RuntimeError(f"CUVE_MODE invalide: {settings.mode} (attendu: real ou sim)")
if settings.mode == "real" and not settings.sensor_url:
raise RuntimeError("CUVE_SENSOR_URL est requis quand CUVE_MODE=real")
Exemple .env.example
bash
# Mode: real ou sim CUVE_MODE=sim # URL du capteur ESP8266 (requis si CUVE_MODE=real) # CUVE_SENSOR_URL=http://192.168.1.50/json # Cache et timeout HTTP CUVE_CACHE_TTL_SECONDS=10 CUVE_HTTP_TIMEOUT_SECONDS=2.0 # Base de données CUVE_DB_PATH=cuve.sqlite3 CUVE_COLLECT_INTERVAL_SECONDS=60 # Géométrie de la cuve CUVE_TANK_TOTAL_LITERS=10000 CUVE_TANK_DIAMETER_CM=184.5 CUVE_TANK_LENGTH_CM=436.4 CUVE_TANK_FULL_AIR_GAP_CM=20
Utilisation dans le code
python
from app.config import settings
# Accès direct aux propriétés
if settings.mode == "sim":
cuve = SimCuveClient()
else:
cuve = RealCuveClient(sensor_url=settings.sensor_url)
Rules
Do
- •Utiliser
dataclass(frozen=True)pour l'immutabilité - •Préfixer toutes les variables par le nom du projet (ex:
CUVE_) - •Fournir des valeurs par défaut sensées
- •Valider les valeurs critiques au démarrage avec des messages clairs
- •Créer un
.env.exampledocumenté - •Utiliser
load_dotenv()en début de module
Don't
- •Ne pas versionner le fichier
.env(ajouter à.gitignore) - •Ne pas oublier de convertir les types (
int(),float()) - •Ne pas accéder directement à
os.getenv()ailleurs que dansconfig.py - •Ne pas utiliser de valeurs par défaut qui fonctionnent en production
File Location
- •Configuration :
src/cuve-api/app/config.py - •Template :
src/cuve-api/.env.example