ProvenPin Install

pydantic-settings nested BaseSettings with env_nested_delimiter: working versions (tested 2026-10-16)

Group configuration into nested Pydantic settings models and populate them from prefixed environment variables separated by a double underscore, tested fully offline.

passed6 passed · 0 failed2026-10-16 00:01 UTC
Status
passed
Last passed
2026-10-16 00:01 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions2
pydantic-settings@2.16.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38826982604

The code

Exactly the files that passed. Your agent can pull them with get_recipe("pydantic-settings-nested-sources") (install), or see them on GitHub.

requirements.txt
pydantic-settings==2.16.0
pytest==9.1.1
config.py
from pydantic import BaseModel, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict


class HttpRetrySettings(BaseModel):
    attempts: int = 3
    backoff: float = 0.5


class HttpSettings(BaseModel):
    base_url: str = "https://api.example.com"
    timeout: float = 10.0
    retries: HttpRetrySettings = HttpRetrySettings()


class DbSettings(BaseModel):
    url: str = "sqlite:///app.db"
    pool_size: int = 5
    password: SecretStr = SecretStr("dev")


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_prefix="APP_",
        env_nested_delimiter="__",
        case_sensitive=False,
        extra="ignore",
    )

    debug: bool = False
    features: list[str] = []
    http: HttpSettings = HttpSettings()
    db: DbSettings = DbSettings()


class AuthSettings(BaseModel):
    token: str


class ServiceSettings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="SVC_", env_nested_delimiter="__")

    auth: AuthSettings
tests/test_config.py
import os

import pytest
from pydantic import ValidationError

from config import HttpRetrySettings, HttpSettings, ServiceSettings, Settings


@pytest.fixture(autouse=True)
def clean_env(monkeypatch):
    for key in list(os.environ):
        if key.upper().startswith(("APP_", "SVC_")):
            monkeypatch.delenv(key)


def test_defaults_are_nested_instances():
    s = Settings()
    assert isinstance(s.http, HttpSettings)
    assert s.http.retries.attempts == 3
    assert s.db.password.get_secret_value() == "dev"


def test_nested_env_overrides(monkeypatch):
    monkeypatch.setenv("app_db__url", "sqlite:///prod.db")
    monkeypatch.setenv("APP_DB__POOL_SIZE", "20")
    monkeypatch.setenv("APP_DEBUG", "on")
    s = Settings()
    assert s.db.url == "sqlite:///prod.db"
    assert s.db.pool_size == 20
    assert s.debug is True


def test_partial_nested_override_keeps_sibling_defaults(monkeypatch):
    monkeypatch.setenv("APP_HTTP__TIMEOUT", "2.5")
    s = Settings()
    assert s.http.timeout == 2.5
    assert s.http.base_url == "https://api.example.com"
    assert s.http.retries.attempts == 3


def test_deep_nested_delimiter(monkeypatch):
    monkeypatch.setenv("APP_HTTP__RETRIES__ATTEMPTS", "9")
    monkeypatch.setenv("APP_HTTP__RETRIES__BACKOFF", "1.5")
    s = Settings()
    assert isinstance(s.http.retries, HttpRetrySettings)
    assert s.http.retries.attempts == 9
    assert s.http.retries.backoff == 1.5


def test_complex_field_decoded_from_json(monkeypatch):
    monkeypatch.setenv("APP_FEATURES", '["beta", "dark-mode"]')
    s = Settings()
    assert s.features == ["beta", "dark-mode"]


def test_required_nested_model_is_built_from_env(monkeypatch):
    with pytest.raises(ValidationError):
        ServiceSettings()
    monkeypatch.setenv("SVC_AUTH__TOKEN", "s3cr3t")
    assert ServiceSettings().auth.token == "s3cr3t"
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.