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. 01Pythonプロジェクトの設定システム構築
  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",
    }

環境変数では、入れ子の区切りにアンダースコアを 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

ベストプラクティスのまとめ

  1. 設定をハードコードしない - 環境固有の値はすべて環境変数から取得します
  2. 型付き設定を使用する - 検証機能を備えた Pydantic-settings を使用します
  3. 早期に失敗させる - 起動時に必須の設定が不足していればクラッシュさせます
  4. 開発用のデフォルト値を用意する - ローカル開発を簡単にします
  5. シークレットをコミットしない - .env ファイル(gitignore 対象)またはシークレットマネージャーを使用します
  6. 変数に名前空間を付ける - 明確にするため、DB_HOST や REDIS_URL を使用します
  7. 設定のシングルトンをインポートする - コード全体で os.getenv() を呼び出さないようにします
  8. すべての変数を文書化する - README に必須の環境変数を記載します
  9. 早期に検証する - 起動時に設定が正しいことを確認します
  10. 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(...) の違いがあります。混在させると、経路によってはエラーなしに動作しなくなります。

コメント