CodeSampleX

示例

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 resolution…

sha256:f5541d3ce0eda3656543ec05a42e65a6c79c2fcfe44febb312569405335bdce1

本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。 提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。 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

契约

  1. assert init kwargs override os.environ and .env files
  2. assert os.environ overrides .env file definitions
  3. assert .env file values override class field defaults
  4. assert passing a nested dict in init kwargs deep-merges with env and .env sources
  5. assert passing an instantiated model in init kwargs masks all lower-priority env sources with instance defaults
  6. assert nested environment variables are ignored unless env_nested_delimiter is explicitly set
  7. 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

下载源代码构件 (tar.gz)

源代码

NOTES.md
# 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.
csx.json
{"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"}
requirements.txt
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
src/__init__.py
"""pydantic-settings source precedence sample."""
src/config.py
"""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)
test/contract.py
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")

原始种子者

anonymous