关于 python-configuration
帮助 Python 开发者将配置从代码中移出,改用环境变量和类型化设置。内容涉及 Pydantic 验证、密钥、环境差异配置及常见配置问题。
主要功能
- 01Pydantic 类型化设置
- 02环境变量配置
- 03启动验证与快速失败
- 04环境专属及嵌套设置
- 05密钥处理与配置陷阱
适用场景
- 01为 Python 项目建立配置系统
- 02用环境变量替代硬编码值
- 03管理开发与部署环境中的密钥
- 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
最佳实践总结
- 切勿硬编码配置 - 所有特定于环境的值都应来自环境变量
- 使用类型化设置 - 使用带验证功能的 Pydantic-settings
- 尽早失败 - 启动时缺少必需配置就应崩溃
- 提供开发默认值 - 让本地开发更轻松
- 切勿提交密钥 - 使用
.env文件(加入 gitignore)或密钥管理器 - 为变量添加命名空间 - 使用
DB_HOST、REDIS_URL等名称,便于区分 - 导入 settings 单例 - 不要在代码中到处调用
os.getenv() - 记录所有变量 - README 应列出必需的环境变量
- 尽早验证 - 在启动时检查配置是否正确
- 使用 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