mcpField
P
python-configuration

python-configuration

github.com/julianobarbosa开发1594 次安装2 天前更新
代码仓库

关于 python-configuration

帮助 Python 开发者将配置从代码中移出,改用环境变量和类型化设置。内容涉及 Pydantic 验证、密钥、环境差异配置及常见配置问题。

主要功能

  1. 01Pydantic 类型化设置
  2. 02环境变量配置
  3. 03启动验证与快速失败
  4. 04环境专属及嵌套设置
  5. 05密钥处理与配置陷阱

适用场景

  1. 01为 Python 项目建立配置系统
  2. 02用环境变量替代硬编码值
  3. 03管理开发与部署环境中的密钥
  4. 04排查配置验证和环境变量加载问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/julianobarbosa/claude-code-skills --skill python-configuration

安装后在 Claude Code 中输入/python-configuration确认已加载。

详细介绍

Python 配置管理

使用环境变量和类型化设置将配置从代码中外置。管理良好的配置可让同一份代码无需修改即可在任何环境中运行。

适用场景

  • 为新项目搭建配置系统
  • 将硬编码值迁移为环境变量
  • 使用 pydantic-settings 实现类型化配置
  • 管理密钥和敏感值
  • 创建特定环境的设置(dev/staging/prod)
  • 在应用启动时验证配置

核心概念

1. 外置配置

所有特定于环境的值(URL、密钥、功能标志)都来自环境变量,而不是代码。

2. 类型化设置

在启动时将配置解析并验证为类型化对象,而不是将其分散在代码中。

3. 尽早失败

在应用启动时验证所有必需配置。缺少配置时应立即崩溃并显示清晰的错误信息。

4. 合理的默认值

为本地开发提供合理的默认值,同时要求敏感设置必须显式指定。

快速入门

from pydantic_settings import BaseSettings
from pydantic import Field

class Settings(BaseSettings):
    database_url: str = Field(alias="DATABASE_URL")
    api_key: str = Field(alias="API_KEY")
    debug: bool = Field(default=False, alias="DEBUG")

settings = Settings()  # Loads from environment

基本模式

模式 1:使用 Pydantic 的类型化设置

创建一个集中式设置类,用于加载并验证所有配置。

from pydantic_settings import BaseSettings
from pydantic import Field, PostgresDsn, ValidationError
import sys

class Settings(BaseSettings):
    """Application configuration loaded from environment variables."""

    # Database
    db_host: str = Field(alias="DB_HOST")
    db_port: int = Field(default=5432, alias="DB_PORT")
    db_name: str = Field(alias="DB_NAME")
    db_user: str = Field(alias="DB_USER")
    db_password: str = Field(alias="DB_PASSWORD")

    # Redis
    redis_url: str = Field(default="redis://localhost:6379", alias="REDIS_URL")

    # API Keys
    api_secret_key: str = Field(alias="API_SECRET_KEY")

    # Feature flags
    enable_new_feature: bool = Field(default=False, alias="ENABLE_NEW_FEATURE")

    model_config = {
        "env_file": ".env",
        "env_file_encoding": "utf-8",
    }

# Create singleton instance at module load
try:
    settings = Settings()
except ValidationError as e:
    print(f"Configuration error:\n{e}")
    sys.exit(1)

在整个应用中导入 settings:

from myapp.config import settings

def get_database_connection():
    return connect(
        host=settings.db_host,
        port=settings.db_port,
        database=settings.db_name,
    )
模式 2:配置缺失时尽早失败

必需设置缺失时,应立即使应用崩溃并显示清晰的错误。

from pydantic_settings import BaseSettings
from pydantic import Field, ValidationError
import sys

class Settings(BaseSettings):
    # Required - no default means it must be set
    api_key: str = Field(alias="API_KEY")
    database_url: str = Field(alias="DATABASE_URL")

    # Optional with defaults
    log_level: str = Field(default="INFO", alias="LOG_LEVEL")

try:
    settings = Settings()
except ValidationError as e:
    print("=" * 60)
    print("CONFIGURATION ERROR")
    print("=" * 60)
    for error in e.errors():
        field = error["loc"][0]
        print(f"  - {field}: {error['msg']}")
    print("\nPlease set the required environment variables.")
    sys.exit(1)

在启动时显示清晰的错误信息,胜过在请求处理中出现难以理解的 None 错误。

模式 3:本地开发默认值

为本地开发提供合理的默认值,同时要求密钥必须显式指定。

class Settings(BaseSettings):
    # Has local default, but prod will override
    db_host: str = Field(default="localhost", alias="DB_HOST")
    db_port: int = Field(default=5432, alias="DB_PORT")

    # Always required - no default for secrets
    db_password: str = Field(alias="DB_PASSWORD")
    api_secret_key: str = Field(alias="API_SECRET_KEY")

    # Development convenience
    debug: bool = Field(default=False, alias="DEBUG")

    model_config = {"env_file": ".env"}

为本地开发创建 .env 文件(切勿提交此文件):

# .env (add to .gitignore)
DB_PASSWORD=local_dev_password
API_SECRET_KEY=dev-secret-key
DEBUG=true
模式 4:带命名空间的环境变量

为相关变量添加前缀,以便清晰区分并轻松调试。

# Database configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=admin
DB_PASSWORD=secret

# Redis configuration
REDIS_URL=redis://localhost:6379
REDIS_MAX_CONNECTIONS=10

# Authentication
AUTH_SECRET_KEY=your-secret-key
AUTH_TOKEN_EXPIRY_SECONDS=3600
AUTH_ALGORITHM=HS256

# Feature flags
FEATURE_NEW_CHECKOUT=true
FEATURE_BETA_UI=false

这样,env | grep DB_ 就能用于调试。

高级模式

模式 5:类型强制转换

Pydantic 会自动处理常见的类型转换。

from pydantic_settings import BaseSettings
from pydantic import Field, field_validator

class Settings(BaseSettings):
    # Automatically converts "true", "1", "yes" to True
    debug: bool = False

    # Automatically converts string to int
    max_connections: int = 100

    # Parse comma-separated string to list
    allowed_hosts: list[str] = Field(default_factory=list)

    @field_validator("allowed_hosts", mode="before")
    @classmethod
    def parse_allowed_hosts(cls, v: str | list[str]) -> list[str]:
        if isinstance(v, str):
            return [host.strip() for host in v.split(",") if host.strip()]
        return v

用法:

ALLOWED_HOSTS=example.com,api.example.com,localhost
MAX_CONNECTIONS=50
DEBUG=true
模式 6:特定环境的配置

使用环境枚举来切换行为。

from enum import Enum
from pydantic_settings import BaseSettings
from pydantic import Field, computed_field

class Environment(str, Enum):
    LOCAL = "local"
    STAGING = "staging"
    PRODUCTION = "production"

class Settings(BaseSettings):
    environment: Environment = Field(
        default=Environment.LOCAL,
        alias="ENVIRONMENT",
    )

    # Settings that vary by environment
    log_level: str = Field(default="DEBUG", alias="LOG_LEVEL")

    @computed_field
    @property
    def is_production(self) -> bool:
        return self.environment == Environment.PRODUCTION

    @computed_field
    @property
    def is_local(self) -> bool:
        return self.environment == Environment.LOCAL

# Usage
if settings.is_production:
    configure_production_logging()
else:
    configure_debug_logging()
模式 7:嵌套配置组

将相关设置组织到嵌套模型中。

from pydantic import BaseModel
from pydantic_settings import BaseSettings

class DatabaseSettings(BaseModel):
    host: str = "localhost"
    port: int = 5432
    name: str
    user: str
    password: str

class RedisSettings(BaseModel):
    url: str = "redis://localhost:6379"
    max_connections: int = 10

class Settings(BaseSettings):
    database: DatabaseSettings
    redis: RedisSettings
    debug: bool = False

    model_config = {
        "env_nested_delimiter": "__",
        "env_file": ".env",
    }

环境变量使用双下划线表示嵌套:

DATABASE__HOST=db.example.com
DATABASE__PORT=5432
DATABASE__NAME=myapp
DATABASE__USER=admin
DATABASE__PASSWORD=secret
REDIS__URL=redis://redis.example.com:6379
模式 8:从文件读取密钥

在容器环境中,从挂载的文件读取密钥。

from pydantic_settings import BaseSettings
from pydantic import Field
from pathlib import Path

class Settings(BaseSettings):
    # Read from environment variable or file
    db_password: str = Field(alias="DB_PASSWORD")

    model_config = {
        "secrets_dir": "/run/secrets",  # Docker secrets location
    }

如果未设置环境变量,Pydantic 会查找 /run/secrets/db_password。

模式 9:配置验证

为复杂要求添加自定义验证。

from pydantic_settings import BaseSettings
from pydantic import Field, model_validator

class Settings(BaseSettings):
    db_host: str = Field(alias="DB_HOST")
    db_port: int = Field(alias="DB_PORT")
    read_replica_host: str | None = Field(default=None, alias="READ_REPLICA_HOST")
    read_replica_port: int = Field(default=5432, alias="READ_REPLICA_PORT")

    @model_validator(mode="after")
    def validate_replica_settings(self):
        if self.read_replica_host and self.read_replica_port == self.db_port:
            if self.read_replica_host == self.db_host:
                raise ValueError(
                    "Read replica cannot be the same as primary database"
                )
        return self

最佳实践总结

  1. 切勿硬编码配置 - 所有特定于环境的值都应来自环境变量
  2. 使用类型化设置 - 使用带验证功能的 Pydantic-settings
  3. 尽早失败 - 启动时缺少必需配置就应崩溃
  4. 提供开发默认值 - 让本地开发更轻松
  5. 切勿提交密钥 - 使用 .env 文件(加入 gitignore)或密钥管理器
  6. 为变量添加命名空间 - 使用 DB_HOST、REDIS_URL 等名称,便于区分
  7. 导入 settings 单例 - 不要在代码中到处调用 os.getenv()
  8. 记录所有变量 - README 应列出必需的环境变量
  9. 尽早验证 - 在启动时检查配置是否正确
  10. 使用 secrets_dir - 支持容器中的挂载密钥

注意事项

  • pydantic-settings 在类导入时读取环境变量,而不是在实例化时读取——导入后对环境变量进行 monkeypatch 的测试会看到缓存值。请重新加载模块,或在执行 monkeypatch 前实例化。
  • 嵌套的 BaseSettings 模型需要显式设置 env_nested_delimiter——否则,DB_HOST 和 DB_PORT 不会自动绑定到 Settings.db.host。
  • SecretStr 会在 __repr__ 中隐藏内容,但不会在 __str__ 中隐藏——print(secret) 会泄露;f"{secret}" 会泄露;只有 repr(secret) 会隐藏内容。
  • .env 文件与进程环境变量的优先级:pydantic-settings 会先读取 .env,再用进程环境变量覆盖。CI 中进程环境变量里的密钥优先于开发者的 .env;但如果进程环境变量中缺少某个键,过时的 .env 值仍会保留。
  • Pydantic v1 与 v2 的 BaseSettings API 各不相同——前者使用 Field(env=...),后者使用 model_config = SettingsConfigDict(...)。混用在某些情况下会静默失效。

评论

支持 Markdown