Files
2026-08-08 11:24:44 +03:00

39 lines
3.2 KiB
Markdown

---
name: python-development-settings
description: Application configuration and settings management. Use when defining, loading, validating, or extending application settings.
---
## Rules
- The root settings class must inherit from `pydantic_settings.BaseSettings`. This enables automatic loading from environment variables and `.env` files.
- Every nested settings class (for individual services, adapters, or components) must inherit from `pydantic.BaseModel`, not from `BaseSettings`. Only the root class needs the env-loading behavior.
- The root settings class exposes one field per top-level service or subsystem. Provide sensible defaults as plain values for immutable types (str, int, bool, None) and use `Field(default_factory=...)` only when the default is a mutable object (dict, list, or model instance). Never use mutable literals as class-attribute defaults in Pydantic v2. Provide defaults so the application starts without a config file when reasonable.
- A settings class can accept either a settings object or a string key referencing another entry in the same config. The resolver (or a `@model_validator`) replaces string references with resolved instances after initial parsing. This is how services declare their dependencies declaratively.
- Use `@model_validator(mode="after")` on the root settings class to validate cross-references between subsystems, resolve string keys to actual instances, and enforce consistency across the configuration graph.
- Support loading from YAML config files with custom tags. Register `!include` for file inclusion and `!env` for environment variable interpolation inside YAML. The YAML loader builder must live as a `@staticmethod` inside the root settings class. Example:
```python
@staticmethod
def get_yaml_loader(base_path: pathlib.Path) -> yaml.BaseLoader:
loader = type("Loader", (yaml.SafeLoader,), {})
loader.add_constructor(
"!include",
yaml_include.Constructor(base_dir=str(base_path)),
)
loader.add_constructor("!env", construct_env_tag)
return loader
```
- Support loading from JSON config files using standard `json.load`. JSON does not support custom tags; use string interpolation or env var resolution handled by `BaseSettings` after load.
- Provide a single `load()` classmethod on the root settings class that reads the config file (YAML or JSON), then passes the parsed dict into the `BaseSettings` constructor along with optional `_env_prefix` and `_env_file`. Example:
```python
@classmethod
def load(cls, config_file: pathlib.Path | None = None) -> Self:
data = {}
if config_file is not None:
if config_file.suffix in (".yaml", ".yml"):
data.update(yaml.load(config_file.read_text(), Loader=get_loader()))
elif config_file.suffix == ".json":
data.update(json.loads(config_file.read_text()))
return cls(**data)
```
- Keep each subsystem's settings in its own `settings.py` module inside the subsystem package. Import and compose them into the root settings class. Do not inline subsystem schemas into the root file.
- Use `pydantic.Field(default_factory=...)` for mutable default values (dicts, lists, models). Never use mutable literals as class-attribute defaults in Pydantic v2.