python-configuration について
Python開発者が設定をコードから分離し、環境変数と型付き設定に移行するためのスキルです。Pydanticによる検証、シークレット、環境別設定、よくある設定上の問題を扱います。
主な機能
- 01Pydanticの型付き設定
- 02環境変数による設定
- 03起動時の検証と即時エラー
- 04環境別およびネストした設定
- 05シークレット管理と設定時の注意点
活用シーン
- 01Pythonプロジェクトの設定システム構築
- 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",
}
環境変数では、入れ子の区切りにアンダースコアを 2 つ使用します。
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を使用します - 設定のシングルトンをインポートする - コード全体で
os.getenv()を呼び出さないようにします - すべての変数を文書化する - README に必須の環境変数を記載します
- 早期に検証する - 起動時に設定が正しいことを確認します
- secrets_dir を使用する - コンテナー内のマウントされたシークレットに対応します
注意点
pydantic-settingsはインスタンス化時ではなく、クラスのインポート時に環境変数を読み込みます — インポート後に環境変数をモンキーパッチするテストでは、キャッシュされた値が使われます。モジュールを再読み込みするか、パッチを適用する前にインスタンス化してください。- ネストされた 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(...)の違いがあります。混在させると、経路によってはエラーなしに動作しなくなります。