from typing import Literal, Tuple, Type
from pydantic import BaseModel, PostgresDsn, RedisDsn
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
SettingsConfigDict,
)
from .yaml_source import YamlConfigSettingsSource
class DatabaseSettings(BaseModel):
dsn: PostgresDsn
pool_size: int = 5
echo: bool = False
password: str = ""
class RedisSettings(BaseModel):
dsn: RedisDsn
ttl_seconds: int = 300
class AppSettings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix="APP_",
env_nested_delimiter="__",
env_file=".env",
extra="ignore",
)
environment: Literal["local", "staging", "production"] = "local"
debug: bool = False
database: DatabaseSettings
redis: RedisSettings
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
yaml_config = YamlConfigSettingsSource(settings_cls)
# First source wins: env vars override the YAML defaults.
return (
init_settings,
env_settings,
dotenv_settings,
file_secret_settings,
yaml_config,
)
from functools import lru_cache
from pathlib import Path
from typing import Any, Dict, Tuple, Type
import yaml
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
DEFAULTS_PATH = Path(__file__).parent / "defaults.yaml"
@lru_cache(maxsize=1)
def _load_yaml() -> Dict[str, Any]:
if not DEFAULTS_PATH.exists():
return {}
with DEFAULTS_PATH.open("r", encoding="utf-8") as fh:
data = yaml.safe_load(fh) or {}
if not isinstance(data, dict):
raise ValueError(f"{DEFAULTS_PATH} must contain a mapping at the top level")
return data
class YamlConfigSettingsSource(PydanticBaseSettingsSource):
def __init__(self, settings_cls: Type[BaseSettings]) -> None:
super().__init__(settings_cls)
self._data = _load_yaml()
def get_field_value(self, field: Any, field_name: str) -> Tuple[Any, str, bool]:
return self._data.get(field_name), field_name, False
def __call__(self) -> Dict[str, Any]:
return dict(self._data)
from functools import lru_cache
from .settings import AppSettings
@lru_cache(maxsize=1)
def get_settings() -> AppSettings:
# Validated once per process; raises on missing/invalid config at startup.
return AppSettings()
if __name__ == "__main__":
settings = get_settings()
print(f"env={settings.environment} debug={settings.debug}")
print(f"db pool={settings.database.pool_size} echo={settings.database.echo}")
print(f"redis ttl={settings.redis.ttl_seconds}s")
environment: local
debug: false
database:
dsn: postgresql://app@localhost:5432/app_dev
pool_size: 5
echo: false
# password intentionally omitted; inject via APP_DATABASE__PASSWORD
redis:
dsn: redis://localhost:6379/0
ttl_seconds: 300
This snippet builds a typed configuration system where values come from a YAML defaults file and are then overridden by environment variables, which is the classic 12-factor layering: sane defaults committed to the repo, secrets and per-environment overrides injected at runtime. It uses pydantic-settings, whose BaseSettings machinery makes source precedence explicit and gives every field real type coercion and validation.
In settings.py, the schema is split into nested models (DatabaseSettings, RedisSettings) grouped under a top-level AppSettings. Nesting keeps related keys together and, combined with the env_nested_delimiter="__", lets an env var like APP_DATABASE__POOL_SIZE map cleanly onto settings.database.pool_size. The env_prefix="APP_" namespaces every variable so the app never collides with unrelated environment state. Fields declare precise types (PostgresDsn, int, bool, Literal), so a malformed value fails loudly at startup instead of surfacing as a confusing runtime error later.
The layering itself lives in settings_customise_sources, an override hook pydantic-settings calls to let a project reorder its config sources. Sources are returned highest-precedence-first, so env_settings and dotenv_settings sit ahead of the custom yaml_config source, meaning any environment variable wins over the file. The yaml_config callable is a small PydanticBaseSettingsSource subclass in yaml_source.py that reads and caches the parsed YAML, exposing it as the lowest-priority dictionary.
Because AppSettings() validates on construction, get_settings in config.py wraps it in functools.lru_cache so the file is read and validated exactly once per process, turning settings into a cheap, shareable singleton that is easy to inject. The defaults.yaml tab shows the committed baseline; note it holds no secrets — the database password is expected to arrive via APP_DATABASE__PASSWORD.
The main trade-off is that env vars are stringly-typed, so nested overrides depend entirely on the delimiter convention being consistent; a typo in a variable name silently falls back to the default rather than erroring. Reaching for this pattern makes sense whenever an application must run identically across local, CI, and production with only environment differences.
Related snips
import os
import stat
for root, _dirs, files in os.walk('/etc'):
for name in files:
path = os.path.join(root, name)
Python security audit script for exposed risky filesystem state
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
cost = models.DecimalField(max_digits=10, decimal_places=2)
margin = models.DecimalField(max_digits=5, decimal_places=2, blank=True)
Django model signals vs overriding save
class SignupForm
include ActiveModel::Model
include ActiveModel::Attributes
attribute :account_name, :string
attribute :email, :string
Shallow Controller, Deep Params: Form Object Pattern
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
Django URL namespacing and reverse lookups
<?php
namespace App\Providers;
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;
Laravel service container and dependency injection
Share this code
Here's the card — post it anywhere.