Claude Code for pydantic-settings: Python Settings Management — Claude Skills 360 Blog
Blog / AI / Claude Code for pydantic-settings: Python Settings Management
AI

Claude Code for pydantic-settings: Python Settings Management

Published: March 17, 2028
Read time: 5 min read
By: Claude Skills 360

pydantic-settings reads validated app settings from environment variables and .env files. pip install pydantic-settings. Basic: from pydantic_settings import BaseSettings; class Settings(BaseSettings): db_url: str = "sqlite:///app.db". Settings() reads DB_URL from env. .env file: model_config = SettingsConfigDict(env_file=".env"). Prefix: env_prefix="APP_" — reads APP_DB_URL. Nested: class DBSettings(BaseSettings): host: str; class Settings(BaseSettings): db: DBSettings = DBSettings(). Dotenv: Settings(_env_file=".env.prod") — override. Case: env_ignore_empty=True. Secret: password: SecretStrget_secret_value(). SecretBytes. DB URL: from pydantic import PostgresDsn. Bool coercion: debug: bool — env "true"|"1"|"yes" → True. List: allowed_hosts: list[str] — env "a,b,c"["a","b","c"]. extra="ignore" vs extra="forbid". Cached: @lru_cache; def get_settings() -> Settings: return Settings(). FastAPI: Depends(get_settings). @model_validator(mode="after") for cross-field validation. Priority: _secrets_dir > env vars > .env file > defaults. Multiple files: env_file=[".env", ".env.local"]. Reload: create new instance; settings are immutable. Claude Code generates pydantic-settings config classes, FastAPI dependencies, and multi-environment settings.

CLAUDE.md for pydantic-settings

## pydantic-settings Stack
- Version: pydantic-settings >= 2.2 | pip install pydantic-settings
- Class: class Settings(BaseSettings): db_url: str = "..." — reads DB_URL env
- Config: model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")
- Secrets: password: SecretStr | api_key: SecretStr — .get_secret_value()
- Cache: @lru_cache; get_settings() → don't re-parse env every call
- Nested: class DBSettings(BaseSettings): ... | db: DBSettings = DBSettings()
- Override: Settings(_env_file=".env.prod") or Settings(db_url="override")

pydantic-settings Configuration Pipeline

# app/settings.py — pydantic-settings env config, secrets, and FastAPI integration
from __future__ import annotations

import os
from functools import lru_cache
from typing import Literal

from pydantic import AnyHttpUrl, PostgresDsn, SecretStr, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


# ─────────────────────────────────────────────────────────────────────────────
# Sub-settings models
# ─────────────────────────────────────────────────────────────────────────────

class DatabaseSettings(BaseSettings):
    """Database connection settings — read from DB_* env vars."""
    host:     str   = "localhost"
    port:     int   = 5432
    name:     str   = "appdb"
    user:     str   = "postgres"
    password: SecretStr = SecretStr("postgres")
    pool_min: int   = 2
    pool_max: int   = 10
    echo_sql: bool  = False

    model_config = SettingsConfigDict(env_prefix="DB_")

    @property
    def dsn(self) -> str:
        """Compose the connection string from components."""
        pw = self.password.get_secret_value()
        return f"postgresql://{self.user}:{pw}@{self.host}:{self.port}/{self.name}"

    @property
    def async_dsn(self) -> str:
        pw = self.password.get_secret_value()
        return f"postgresql+asyncpg://{self.user}:{pw}@{self.host}:{self.port}/{self.name}"


class RedisSettings(BaseSettings):
    """Redis connection settings — read from REDIS_* env vars."""
    host:     str = "localhost"
    port:     int = 6379
    db:       int = 0
    password: SecretStr | None = None
    ttl:      int = 3600     # default cache TTL in seconds

    model_config = SettingsConfigDict(env_prefix="REDIS_")

    @property
    def url(self) -> str:
        auth = ""
        if self.password:
            auth = f":{self.password.get_secret_value()}@"
        return f"redis://{auth}{self.host}:{self.port}/{self.db}"


class AuthSettings(BaseSettings):
    """Auth/JWT settings."""
    secret_key:       SecretStr
    algorithm:        str = "HS256"
    access_token_ttl: int = 3600       # seconds
    refresh_token_ttl: int = 86400 * 30

    model_config = SettingsConfigDict(env_prefix="AUTH_")


# ─────────────────────────────────────────────────────────────────────────────
# Main settings class
# ─────────────────────────────────────────────────────────────────────────────

class Settings(BaseSettings):
    """
    Application settings loaded from environment variables and .env files.
    Priority (highest → lowest): env vars > .env.local > .env > defaults.
    """

    # Application
    app_name:    str   = "MyApp"
    environment: Literal["development", "staging", "production"] = "development"
    debug:       bool  = False
    log_level:   str   = "INFO"

    # Server
    host:        str   = "0.0.0.0"
    port:        int   = 8000
    allowed_origins: list[str] = ["http://localhost:3000"]

    # Feature flags
    enable_cache:     bool = True
    enable_analytics: bool = False

    # External API keys
    stripe_api_key:  SecretStr | None = None
    sendgrid_api_key: SecretStr | None = None
    openai_api_key:  SecretStr | None = None

    model_config = SettingsConfigDict(
        env_file=[".env", ".env.local"],    # .env.local overrides .env
        env_file_encoding="utf-8",
        env_nested_delimiter="__",          # DB__HOST → db.host
        env_ignore_empty=True,
        extra="ignore",
    )

    @property
    def is_production(self) -> bool:
        return self.environment == "production"

    @property
    def is_development(self) -> bool:
        return self.environment == "development"

    @field_validator("log_level")
    @classmethod
    def validate_log_level(cls, v: str) -> str:
        valid = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
        upper = v.upper()
        if upper not in valid:
            raise ValueError(f"log_level must be one of {valid}")
        return upper

    @model_validator(mode="after")
    def check_production_settings(self) -> "Settings":
        """Enforce production-only constraints (e.g. debug must be off)."""
        if self.is_production and self.debug:
            raise ValueError("debug must be False in production")
        return self


# ─────────────────────────────────────────────────────────────────────────────
# Environment-specific subclasses
# ─────────────────────────────────────────────────────────────────────────────

class DevelopmentSettings(Settings):
    environment: Literal["development"] = "development"
    debug:       bool = True
    log_level:   str  = "DEBUG"

    model_config = SettingsConfigDict(
        env_file=[".env", ".env.development"],
        env_file_encoding="utf-8",
        extra="ignore",
        env_ignore_empty=True,
    )


class ProductionSettings(Settings):
    environment: Literal["production"] = "production"
    debug:       bool = False
    log_level:   str  = "WARNING"
    enable_analytics: bool = True

    model_config = SettingsConfigDict(
        env_file=[".env"],
        env_file_encoding="utf-8",
        extra="ignore",
    )


def load_settings() -> Settings:
    """
    Load the correct Settings subclass based on the ENVIRONMENT env var.
    Defaults to DevelopmentSettings when ENVIRONMENT is not set.
    """
    env = os.getenv("ENVIRONMENT", "development").lower()
    if env == "production":
        return ProductionSettings()
    return DevelopmentSettings()


# ─────────────────────────────────────────────────────────────────────────────
# Cached singleton for FastAPI / application use
# ─────────────────────────────────────────────────────────────────────────────

@lru_cache(maxsize=1)
def get_settings() -> Settings:
    """
    Return a cached Settings instance.
    @lru_cache ensures env/files are parsed only once per process.
    Invalidate with get_settings.cache_clear() in tests.
    """
    return load_settings()


def get_db_settings() -> DatabaseSettings:
    return DatabaseSettings()


def get_redis_settings() -> RedisSettings:
    return RedisSettings()


# ─────────────────────────────────────────────────────────────────────────────
# FastAPI dependencies
# ─────────────────────────────────────────────────────────────────────────────

def make_fastapi_settings_dependency():
    """
    Return a FastAPI Depends() factory that injects Settings.

    Usage:
        settings_dep = make_fastapi_settings_dependency()

        @app.get("/health")
        async def health(settings: Settings = Depends(settings_dep)):
            return {"env": settings.environment}
    """
    def _dep() -> Settings:
        return get_settings()
    return _dep


# ─────────────────────────────────────────────────────────────────────────────
# Utilities
# ─────────────────────────────────────────────────────────────────────────────

def settings_to_dict(settings: Settings, exclude_secrets: bool = True) -> dict:
    """
    Serialize settings to a dict for logging or debugging.
    exclude_secrets=True: replaces SecretStr values with "***".
    """
    raw = settings.model_dump()
    if exclude_secrets:
        def _redact(d):
            result = {}
            for k, v in d.items():
                from pydantic import SecretStr as _SecretStr
                if hasattr(settings, k) and isinstance(getattr(type(settings), k, None), type) and False:
                    result[k] = "***"
                elif isinstance(v, dict):
                    result[k] = _redact(v)
                else:
                    result[k] = v
            return result
        # Simpler approach: re-serialize with mode="json" which masks secrets
        raw = settings.model_dump(mode="json")
    return raw


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== Settings from environment ===")
    settings = Settings(
        app_name="DemoApp",
        debug=True,
        log_level="DEBUG",
        allowed_origins=["http://localhost:3000", "https://app.example.com"],
        stripe_api_key=SecretStr("sk_test_demo"),
    )

    print(f"  app_name:       {settings.app_name}")
    print(f"  environment:    {settings.environment}")
    print(f"  debug:          {settings.debug}")
    print(f"  log_level:      {settings.log_level}")
    print(f"  allowed_origins:{settings.allowed_origins}")

    stripe = settings.stripe_api_key
    secret_display = stripe.get_secret_value()[:7] + "***" if stripe else None
    print(f"  stripe_api_key: {secret_display!r} (first 7 chars shown)")

    print("\n=== DatabaseSettings ===")
    db_settings = DatabaseSettings(
        host="db.example.com",
        name="production",
        password=SecretStr("s3cr3t"),
    )
    print(f"  dsn: {db_settings.dsn}")

    print("\n=== RedisSettings ===")
    redis_settings = RedisSettings()
    print(f"  url: {redis_settings.url}")

    print("\n=== Environment loading ===")
    os.environ["ENVIRONMENT"] = "production"
    os.environ["DEBUG"]        = "false"
    prod_settings = load_settings()
    print(f"  Loaded: {type(prod_settings).__name__}")
    print(f"  is_production: {prod_settings.is_production}")
    del os.environ["ENVIRONMENT"]

    print("\n=== model_dump (JSON-safe) ===")
    dumped = settings.model_dump(mode="json")
    for k, v in list(dumped.items())[:5]:
        print(f"  {k}: {v!r}")

For the python-dotenv alternative — python-dotenv just loads .env files into os.environ; pydantic-settings then reads from os.environ with type coercion, validation, defaults, and nested model support — they complement each other, and pydantic-settings includes dotenv loading natively so you only need the one library. For the dynaconf alternative — dynaconf supports multiple configuration formats (TOML, YAML, JSON, .env, Redis), environments via ENV_FOR_DYNACONF, and a layering system; pydantic-settings is tighter: settings are a Python class with full type annotations and validators from pydantic, IDE completion for every setting, and first-class FastAPI integration via Depends(); choose dynaconf when you need multi-format configs, pydantic-settings when you want a typed Python-first API. The Claude Skills 360 bundle includes pydantic-settings skill sets covering BaseSettings with env_file and env_prefix, nested settings with env_nested_delimiter, SecretStr for passwords and API keys, @field_validator and @model_validator, Literal type for environment enum, @lru_cache get_settings() singleton, DevelopmentSettings/ProductionSettings subclasses, load_settings() based on ENVIRONMENT, DatabaseSettings with dsn property, RedisSettings with url property, FastAPI Depends() integration, and settings_to_dict() for safe logging. Start with the free tier to try settings management code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free