Lire et valider settings.toml avec Pydantic dans les apps Python
Ce module gère la lecture de settings.toml, son analyse avec tomllib, la mise en cache des résultats via lru_cache, et la validation des sections avec Pydantic. Il sépare la logique de configuration des paramètres de votre app, réduit les lectures répétées sur disque et garantit la sécurité des types.
from functools import lru_cache
from pathlib import Path
from tomllib import load
from typing import Any, Type, TypeVar
from pydantic import BaseModel, SecretStr
ConfigType = TypeVar("ConfigType", bound=BaseModel)
class BotConfig(BaseModel):
token: SecretStr
@lru_cache
def parse_config_file() -> dict[str, Any]:
config_path = Path(__file__).resolve().parent.parent.joinpath("settings.toml")
if not config_path.exists():
error = "Could not find settings file"
raise ValueError(error)
with open(config_path, "rb") as file:
config_data = load(file)
return config_data
def get_config(model: Type[ConfigType], root_key: str) -> ConfigType:
config_dict = parse_config_file()
if root_key not in config_dict:
error = f"Key {root_key} not found"
raise ValueError(error)
return model.model_validate(config_dict[root_key])
Pourquoi TOML et Pydantic pour la configuration
Les fichiers TOML sont lisibles par les humains et se parsent proprement en dictionnaires Python. Les sections comme [bot] deviennent des dictionnaires imbriqués. Pydantic valide la structure, gère les conversions de types et masque les secrets avec SecretStr.
Exemple TOML :
[bot]
token = "123456:ABCDEF"
L'analyse produit {"bot": {"token": "123456:ABCDEF"}}. La validation crée un objet BotConfig.
Le cache avec @lru_cache évite les relectures : le premier appel analyse, les suivants puisent dans le cache. Rechargez avec parse_config_file.cache_clear().
Gérer les chemins avec pathlib
Path(__file__).resolve().parent.parent.joinpath("settings.toml") construit un chemin absolu vers le fichier dans le répertoire parent de votre projet. exists() vérifie l'existence et les propriétés parent simplifient la validation.
Path.joinpath(): ajoute des segments de chemin de manière multiplateforme.resolve(): résout les liens symboliques en chemins absolus.- Ouverture en
"rb": requise partomllib.load()pour la lecture binaire.
Annotations de types avec TypeVar et Pydantic
ConfigType = TypeVar("ConfigType", bound=BaseModel) permet à get_config de fonctionner avec n'importe quel modèle Pydantic, en retournant une instance du type passé.
Type[ConfigType]: attend une classe de modèle, pas une instance.model_validate(): transforme un dictionnaire en objet validé, levantValidationErroren cas d'échec.Anypour les dictionnaires : TOML mélange les types (str, int, bool, dict).
Étendez pour d'autres sections :
class DatabaseConfig(BaseModel):
host: str
port: int
user: str
config = get_config(DatabaseConfig, "database")
Mise en cache et décorateurs
@lru_cache stocke les résultats par arguments (aucun ici, donc simple). LRU évince les anciennes entrées au-delà de la limite, mais un seul emplacement suffit pour la config.
Avantages :
- Réduit les E/S : lecture disque une seule fois.
- Idempotence : plusieurs appels à
get_configpartagent une analyse. - Sûr en multithreading : le décorateur de la stdlib s'en charge.
Inconvénients : Les modifications sur disque du TOML sont ignorées jusqu'au cache_clear().
Structure du module
- Modèles (
BotConfig) : indépendants des chargeurs. parse_config_file(): analyse le TOML complet.get_config(): extrait et valide une section.
Cette séparation des responsabilités améliore la testabilité : moquez les chemins ou chaînes TOML.
Conseils clés :
- Utilisez
tomllib(Python 3.11+), reteneztomlipour les versions antérieures. - Validez toujours la config avec Pydantic avant usage.
- Mettez en cache l'analyse, mais ajoutez un support de rechargement.
- Stockez les secrets en
SecretStrpour les masquer dans les logs/debug. - Construisez les chemins avec
pathlibpour la compatibilité multiplateforme.
— Editorial Team
Aucun commentaire pour le moment.