Пример
pydantic-settings 2.15.0: Pin pydantic-settings source precedence (init kwargs > os.environ > .env > defaults) and nested delimiter resolution versus raw JSON env vars
Проверенный пример — pypi pydantic-settings 2.15.0: Pin pydantic-settings source precedence (init kwargs > os.environ > .env > defaults) and nested delimiter…
sha256:f5541d3ce0eda3656543ec05a42e65a6c79c2fcfe44febb312569405335bdce1
Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла.
Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди.
MIT-0
Свидетельства выполнения
Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.
- Основа свидетельства
- Подписанный контракт пройден
- Квитанции проверки
- 3
- Ключи подписи, собравшие его
- 2
Заявленная среда
python linux x64 python python pip
Среды запусков проверки
| Окружение | Контракт | Этапы | Запуск |
|---|---|---|---|
| python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-15 |
| python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-15 |
| python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-18 |
Кейс
HOW- Цель
- Pin pydantic-settings source precedence (init kwargs > os.environ > .env > defaults) and nested delimiter resolution versus raw JSON env vars
- Символы
-
- BaseSettings
- SettingsConfigDict
- Окружение
- python
- Создан
- 2026-08-15T14:18:39Z
Контракт
- assert init kwargs override os.environ and .env files
- assert os.environ overrides .env file definitions
- assert .env file values override class field defaults
- assert passing a nested dict in init kwargs deep-merges with env and .env sources
- assert passing an instantiated model in init kwargs masks all lower-priority env sources with instance defaults
- assert nested environment variables are ignored unless env_nested_delimiter is explicitly set
- assert nested models without env_nested_delimiter require a JSON-encoded string in the parent env variable
Файлы
- NOTES.md
- csx.json
- requirements.txt
- src/__init__.py
- src/config.py
- test/contract.py
Исходный код
# CodeSampleX Notes: pydantic-settings Source Precedence & Nested Delimiter
## 1. Search Result
- **`search_known_solution`**: MISS (returned low-confidence references to unrelated packages: Node `dotenv` ESM issues, Go `spf13/viper` precedence, and Pydantic v1->v2 `model_validate` migration; no sample existed for `pydantic-settings` source precedence or nested delimiter resolution).
## 2. What a Model Would Have Written Instead
- A model would assume that environment variables override `init` kwargs under standard 12-factor principles, that `.env` files take precedence over `os.environ` as local configuration overrides, and that nested environment variables like `APP_DATABASE__HOST` work out of the box without configuring `env_nested_delimiter="__"`.
## 3. How the Wrong Version Fails
- It fails silently with a green build: without `env_nested_delimiter`, nested environment variables are completely ignored without raising errors (leaving default configuration values in place), and `init` kwargs silently override container/host environment variables contrary to 12-factor assumptions.
{"case":{"caseId":"case:sha256:9c502a5d054b7496fdba0c5b56d7925ba9646730c820facf378c4c6470b7a489","constraints":{"runtime":"python"},"contract":["assert init kwargs override os.environ and .env files","assert os.environ overrides .env file definitions","assert .env file values override class field defaults","assert passing a nested dict in init kwargs deep-merges with env and .env sources","assert passing an instantiated model in init kwargs masks all lower-priority env sources with instance defaults","assert nested environment variables are ignored unless env_nested_delimiter is explicitly set","assert nested models without env_nested_delimiter require a JSON-encoded string in the parent env variable"],"goal":"Pin pydantic-settings source precedence (init kwargs \u003e os.environ \u003e .env \u003e defaults) and nested delimiter resolution versus raw JSON env vars","kind":"HOW","packages":["pkg:pypi/pydantic-settings@2.15.0","pkg:pypi/pydantic@2.13.4","pkg:pypi/python-dotenv@1.2.2"],"schemaVersion":1,"symbols":["BaseSettings","SettingsConfigDict"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/pydantic-settings@2.15.0","pkg:pypi/pydantic@2.13.4","pkg:pypi/python-dotenv@1.2.2"],"schemaVersion":1,"symbols":["BaseSettings","SettingsConfigDict"],"verifierAdapter":"python@1"}
pydantic-settings==2.15.0
pydantic==2.13.4
python-dotenv==1.2.2
pydantic-core==2.46.4
annotated-types==0.8.0
typing-extensions==4.15.0
typing-inspection==0.4.2
"""pydantic-settings source precedence sample."""
"""pydantic-settings configuration models and nested delimiter handling."""
from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class DatabaseConfig(BaseModel):
host: str = "localhost"
port: int = 5432
user: str = "postgres"
pool_size: int = 10
class AppSettings(BaseSettings):
"""Settings configured with env_prefix and env_nested_delimiter."""
model_config = SettingsConfigDict(
env_prefix="APP_",
env_nested_delimiter="__",
env_file_encoding="utf-8",
extra="ignore",
)
app_name: str = "default_app"
environment: str = "development"
debug: bool = False
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
class AppSettingsWithoutDelimiter(BaseSettings):
"""Settings without env_nested_delimiter (defaults to None)."""
model_config = SettingsConfigDict(
env_prefix="APP_",
extra="ignore",
)
app_name: str = "default_app"
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
import json
import os
import sys
import tempfile
from pathlib import Path
# Add project root to sys.path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.config import AppSettings, AppSettingsWithoutDelimiter, DatabaseConfig
def test_source_precedence():
"""Verify that init kwargs > os.environ > .env file > model defaults."""
with tempfile.TemporaryDirectory() as tmpdir:
env_file_path = Path(tmpdir) / ".env"
# .env sets all three fields
env_file_path.write_text(
"APP_APP_NAME=dotenv_app\n"
"APP_ENVIRONMENT=dotenv_env\n"
"APP_DEBUG=true\n"
"APP_DATABASE__HOST=dotenv_host\n"
"APP_DATABASE__PORT=3306\n"
"APP_DATABASE__USER=dotenv_user\n",
encoding="utf-8",
)
# Set environment variables (simulating process environment)
os.environ["APP_ENVIRONMENT"] = "os_environ_env"
os.environ["APP_DATABASE__HOST"] = "os_environ_host"
os.environ["APP_DATABASE__PORT"] = "5433"
# Case 1: Precedence hierarchy
# - app_name: provided via init kwargs ("init_app"), .env ("dotenv_app"), default ("default_app")
# -> init kwargs WINS over .env and default
# - environment: provided via os.environ ("os_environ_env"), .env ("dotenv_env"), default ("development")
# -> os.environ WINS over .env and default (a naive model might think .env overrides os.environ)
# - debug: provided via .env (true), default (False)
# -> .env WINS over default (a naive model might think defaults ignore .env)
settings = AppSettings(_env_file=str(env_file_path), app_name="init_app")
assert settings.app_name == "init_app", (
f"Expected init kwargs to win, got {settings.app_name}"
)
assert settings.environment == "os_environ_env", (
f"Expected os.environ to beat .env, got {settings.environment}"
)
assert settings.debug is True, (
f"Expected .env to beat model default, got {settings.debug}"
)
# Case 2: Deep dictionary merge in nested models
# Passing a partial dict `database={"host": "init_host"}` merges across all sources:
# - host: init kwargs ("init_host") beats os.environ ("os_environ_host") and .env ("dotenv_host")
# - port: os.environ (5433) beats .env (3306) and default (5432)
# - user: .env ("dotenv_user") beats default ("postgres")
# - pool_size: model default (10) since no source set it
merged_settings = AppSettings(
_env_file=str(env_file_path),
database={"host": "init_host"},
)
assert merged_settings.database.host == "init_host", (
"Init dict field must override os.environ"
)
assert merged_settings.database.port == 5433, (
"os.environ must override .env for nested field"
)
assert merged_settings.database.user == "dotenv_user", (
".env must supply missing nested field when not in env or init"
)
assert merged_settings.database.pool_size == 10, (
"Model default must be retained for unspecified nested field"
)
# Case 3: Instantiated Model instance replaces entirely without merging
# When passing DatabaseConfig(host="init_host") directly, all its default attributes
# (port=5432, user="postgres") are already materialized on the instance.
# Thus, init kwargs masks os.environ and .env for ALL attributes in the submodel.
inst_settings = AppSettings(
_env_file=str(env_file_path),
database=DatabaseConfig(host="init_host"),
)
assert inst_settings.database.host == "init_host"
assert inst_settings.database.port == 5432, (
"Model instance defaults mask lower-priority env/dotenv sources"
)
assert inst_settings.database.user == "postgres", (
"Model instance defaults mask .env values"
)
def test_nested_delimiter_requirement():
"""Verify behavior with and without env_nested_delimiter."""
# Ensure clean environment
os.environ["APP_DATABASE__HOST"] = "remote_host"
os.environ["APP_DATABASE__PORT"] = "9999"
# Without env_nested_delimiter configured, APP_DATABASE__HOST and APP_DATABASE__PORT are ignored!
settings_no_delim = AppSettingsWithoutDelimiter()
assert settings_no_delim.database.host == "localhost", (
"Without env_nested_delimiter, nested env vars must be ignored"
)
assert settings_no_delim.database.port == 5432, (
"Without env_nested_delimiter, nested env vars must be ignored"
)
# Without delimiter, nested models require JSON serialized string in the parent env var
os.environ["APP_DATABASE"] = json.dumps({"host": "json_host", "port": 7777})
settings_json = AppSettingsWithoutDelimiter()
assert settings_json.database.host == "json_host", (
"JSON string in parent env var must populate nested model"
)
assert settings_json.database.port == 7777, (
"JSON string in parent env var must populate nested model"
)
if __name__ == "__main__":
# Clean test keys from environ before starting
test_keys = [
"APP_APP_NAME",
"APP_ENVIRONMENT",
"APP_DEBUG",
"APP_DATABASE",
"APP_DATABASE__HOST",
"APP_DATABASE__PORT",
"APP_DATABASE__USER",
]
for k in test_keys:
os.environ.pop(k, None)
test_source_precedence()
for k in test_keys:
os.environ.pop(k, None)
test_nested_delimiter_requirement()
for k in test_keys:
os.environ.pop(k, None)
print("CONTRACT PASS: pydantic-settings source precedence and nested delimiter verified")