Claude Code for python-decouple: Config from Environment — Claude Skills 360 Blog
Blog / AI / Claude Code for python-decouple: Config from Environment
AI

Claude Code for python-decouple: Config from Environment

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

python-decouple separates config from code. pip install python-decouple. Read: from decouple import config. SECRET_KEY = config("SECRET_KEY"). DEBUG = config("DEBUG", default=False, cast=bool). PORT = config("PORT", default=8000, cast=int). Sources: reads from .env file first, then environment variables (env vars override .env). Required: calling config("KEY") with no default raises UndefinedValueError if unset. Bool cast: "true", "True", "1", "yes" → True. "false", "0", "no" → False. Csv: from decouple import Csv. ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="localhost", cast=Csv()). ALLOWED_HOSTS = config("ALLOWED_HOSTS", cast=Csv(cast=str, delimiter=",", strip=" ")). Choices: from decouple import Choices. ENV = config("APP_ENV", cast=Choices(["dev","staging","prod"])) → raises ValueError if not in set. Explicit file: from decouple import RepositoryEnv; config = Config(RepositoryEnv(".env.prod")). Auto: from decouple import AutoConfig. config = AutoConfig() — searches up the directory tree for .env. Ini: also reads from .ini with [settings] section. Comment: .env supports # comments and blank lines. Override: env var takes precedence over .env. os.environ["KEY"] in tests. Claude Code generates decouple config modules, .env templates, and settings classes.

CLAUDE.md for python-decouple

## python-decouple Stack
- Version: python-decouple >= 3.8 | pip install python-decouple
- Read: config("KEY") — reads .env then os.environ; no default → required
- Types: config("PORT", cast=int) | config("DEBUG", cast=bool) | Csv() for lists
- Default: config("KEY", default="val") — optional setting with fallback
- Choices: config("ENV", cast=Choices(["dev","staging","prod"])) — validated enum
- File: Config(RepositoryEnv(".env.prod")) — explicit .env file path
- Override: env vars always override .env — safe for CI/CD secrets injection

python-decouple Configuration Pipeline

# app/settings.py — python-decouple config module
from __future__ import annotations

from pathlib import Path

from decouple import AutoConfig, Choices, Config, Csv, RepositoryEnv, UndefinedValueError, config

# ─────────────────────────────────────────────────────────────────────────────
# 1. Application settings — single module pattern
# ─────────────────────────────────────────────────────────────────────────────

# Core
SECRET_KEY:    str  = config("SECRET_KEY")                              # required
DEBUG:         bool = config("DEBUG",          default=False, cast=bool)
APP_ENV:       str  = config("APP_ENV",        default="development",
                              cast=Choices(["development","staging","production"]))

# Server
HOST:          str  = config("HOST",           default="0.0.0.0")
PORT:          int  = config("PORT",           default=8000, cast=int)
WORKERS:       int  = config("WORKERS",        default=1,    cast=int)
RELOAD:        bool = config("RELOAD",         default=DEBUG, cast=bool)

# Database
DATABASE_URL:  str  = config("DATABASE_URL",   default="sqlite:///./dev.db")
DB_POOL_SIZE:  int  = config("DB_POOL_SIZE",   default=5,    cast=int)
DB_MAX_OVERFLOW: int = config("DB_MAX_OVERFLOW", default=10, cast=int)
DB_ECHO:       bool = config("DB_ECHO",        default=False, cast=bool)

# Redis
REDIS_URL:     str  = config("REDIS_URL",      default="redis://localhost:6379/0")
CACHE_TTL:     int  = config("CACHE_TTL",      default=300,  cast=int)

# Security
ALLOWED_HOSTS: list = config("ALLOWED_HOSTS",  default="localhost,127.0.0.1",
                              cast=Csv())
CORS_ORIGINS:  list = config("CORS_ORIGINS",   default="http://localhost:3000",
                              cast=Csv())
JWT_ALGORITHM: str  = config("JWT_ALGORITHM",  default="HS256")
ACCESS_TOKEN_EXPIRE_MINUTES: int = config("ACCESS_TOKEN_EXPIRE_MINUTES",
                                          default=30, cast=int)

# Email
SMTP_HOST:     str  = config("SMTP_HOST",      default="localhost")
SMTP_PORT:     int  = config("SMTP_PORT",      default=587,  cast=int)
SMTP_TLS:      bool = config("SMTP_TLS",       default=True, cast=bool)
SMTP_USER:     str  = config("SMTP_USER",      default="")
SMTP_PASSWORD: str  = config("SMTP_PASSWORD",  default="")
EMAIL_FROM:    str  = config("EMAIL_FROM",      default="[email protected]")

# External APIs
STRIPE_SECRET_KEY:  str = config("STRIPE_SECRET_KEY",  default="")
SENDGRID_API_KEY:   str = config("SENDGRID_API_KEY",    default="")
S3_BUCKET:          str = config("S3_BUCKET",           default="")
AWS_REGION:         str = config("AWS_REGION",          default="us-east-1")

# Feature flags
FEATURE_SIGNUP:     bool = config("FEATURE_SIGNUP",  default=True,  cast=bool)
FEATURE_PAYMENTS:   bool = config("FEATURE_PAYMENTS", default=False, cast=bool)

# Logging
LOG_LEVEL:  str = config("LOG_LEVEL",  default="INFO",
                          cast=Choices(["DEBUG","INFO","WARNING","ERROR","CRITICAL"]))
LOG_FORMAT: str = config("LOG_FORMAT", default="json",
                          cast=Choices(["json","text"]))


# ─────────────────────────────────────────────────────────────────────────────
# 2. Environment-specific .env files
# ─────────────────────────────────────────────────────────────────────────────

def load_env_config(env: str | None = None) -> Config:
    """
    Load config from a specific .env.{env} file.
    Useful for running: python -c "from settings import load_env_config; cfg = load_env_config('staging')"
    """
    target_env = env or APP_ENV
    env_file = Path(f".env.{target_env}")
    if env_file.exists():
        return Config(RepositoryEnv(str(env_file)))
    # Fall back to .env or environment variables only
    return Config(RepositoryEnv(".env")) if Path(".env").exists() else Config({})


# ─────────────────────────────────────────────────────────────────────────────
# 3. Settings validation at startup
# ─────────────────────────────────────────────────────────────────────────────

def validate_settings() -> list[str]:
    """
    Check that all required settings are present and valid.
    Call at application startup to fail fast on misconfiguration.
    """
    errors: list[str] = []

    if not SECRET_KEY or len(SECRET_KEY) < 32:
        errors.append("SECRET_KEY must be at least 32 characters")

    if APP_ENV == "production":
        if DEBUG:
            errors.append("DEBUG must be False in production")
        if not SMTP_USER or not SMTP_PASSWORD:
            errors.append("SMTP_USER and SMTP_PASSWORD required in production")
        if not DATABASE_URL.startswith("postgresql"):
            errors.append("DATABASE_URL must be PostgreSQL in production")

    return errors


def assert_settings_valid() -> None:
    """Raise RuntimeError if any settings are invalid — call at app startup."""
    errors = validate_settings()
    if errors:
        raise RuntimeError("Configuration errors:\n" + "\n".join(f"  - {e}" for e in errors))


# ─────────────────────────────────────────────────────────────────────────────
# 4. FastAPI settings integration
# ─────────────────────────────────────────────────────────────────────────────

try:
    from fastapi import FastAPI

    def create_app() -> FastAPI:
        assert_settings_valid()
        return FastAPI(
            title="My API",
            debug=DEBUG,
            docs_url="/docs" if DEBUG else None,  # hide docs in prod
        )

except ImportError:
    pass


# ─────────────────────────────────────────────────────────────────────────────
# 5. Django settings integration
# ─────────────────────────────────────────────────────────────────────────────

# Example: paste into Django's settings.py
DJANGO_SETTINGS_EXAMPLE = """
from decouple import config, Csv

SECRET_KEY = config("SECRET_KEY")
DEBUG       = config("DEBUG", default=False, cast=bool)
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME":   config("DB_NAME",     default="myapp"),
        "USER":   config("DB_USER",     default="postgres"),
        "PASSWORD": config("DB_PASSWORD", default=""),
        "HOST":   config("DB_HOST",     default="localhost"),
        "PORT":   config("DB_PORT",     default="5432"),
    }
}
ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="localhost", cast=Csv())
"""


# ─────────────────────────────────────────────────────────────────────────────
# 6. .env file template
# ─────────────────────────────────────────────────────────────────────────────

ENV_TEMPLATE = """\
# ── Required ─────────────────────────────────────────────
SECRET_KEY=change-me-to-a-long-random-string-at-least-32-chars

# ── Application ──────────────────────────────────────────
DEBUG=true
APP_ENV=development
HOST=0.0.0.0
PORT=8000
WORKERS=1

# ── Database ─────────────────────────────────────────────
DATABASE_URL=postgresql://postgres:password@localhost/myapp_dev
DB_POOL_SIZE=5
DB_MAX_OVERFLOW=10
DB_ECHO=false

# ── Redis ────────────────────────────────────────────────
REDIS_URL=redis://localhost:6379/0
CACHE_TTL=300

# ── Security ─────────────────────────────────────────────
ALLOWED_HOSTS=localhost,127.0.0.1
CORS_ORIGINS=http://localhost:3000
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30

# ── Email ────────────────────────────────────────────────
SMTP_HOST=localhost
SMTP_PORT=587
SMTP_TLS=true
SMTP_USER=
SMTP_PASSWORD=
[email protected]

# ── Feature flags ────────────────────────────────────────
FEATURE_SIGNUP=true
FEATURE_PAYMENTS=false

# ── Logging ──────────────────────────────────────────────
LOG_LEVEL=DEBUG
LOG_FORMAT=text
"""


def write_env_template(path: str = ".env.example") -> None:
    """Write a .env.example template — commit this to version control."""
    Path(path).write_text(ENV_TEMPLATE)
    print(f"Wrote {path}")


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

if __name__ == "__main__":
    import os
    # Simulate environment for demo
    os.environ.setdefault("SECRET_KEY", "demo-secret-key-that-is-at-least-32-chars!!")
    os.environ.setdefault("DATABASE_URL", "sqlite:///./demo.db")

    # Reload config values after setting env vars
    from decouple import config as _c
    print(f"DEBUG:         {DEBUG}")
    print(f"APP_ENV:       {APP_ENV}")
    print(f"PORT:          {PORT}")
    print(f"ALLOWED_HOSTS: {ALLOWED_HOSTS}")
    print(f"LOG_LEVEL:     {LOG_LEVEL}")

    errors = validate_settings()
    print(f"\nValidation: {'PASS' if not errors else 'FAIL'}")
    for e in errors:
        print(f"  - {e}")

    write_env_template("/tmp/.env.example")

For the os.environ.get alternative — os.environ.get("SECRET_KEY", "") returns a raw string regardless of the intended type, silently uses the empty default if the variable is missing (no UndefinedValueError), requires manual int() / bool() casting, and does not read .env files, while config("SECRET_KEY") raises UndefinedValueError immediately if the variable is missing and no default is provided, cast=bool converts "true"/"1"/"yes" to True and "false"/"0"/"no" to False, cast=Csv() splits comma-separated strings into a list, and the value is read from .env first then from os.environ so a CI environment variable always overrides the file. For the pydantic-settings / BaseSettings alternative — pydantic-settings validates the full settings schema with type annotations and @validator decorators, making it the right choice when settings are complex models with cross-field validation, while python-decouple has zero transitive dependencies, reads .env, .ini, and os.environ with a single config() call, and is several times simpler to set up for projects that just need typed environment variable access without a full Pydantic model. The Claude Skills 360 bundle includes python-decouple skill sets covering config with required and default values, cast=int/bool/Csv for type conversion, Choices for validated enum values, UndefinedValueError on missing required settings, RepositoryEnv for explicit .env file selection, AutoConfig for directory-traversal discovery, .env template generation, startup validation, Django DATABASES and ALLOWED_HOSTS patterns, and FastAPI settings module integration. Start with the free tier to try environment configuration 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