⚙️

pydanticで設定ファイルのバリデーションをする

2022/07/17に公開

この記事のゴール

pydanticのBaseSettingsで設定ファイルのバリデーションを行い、開発者の意図しない設定値がコード内で使用されないようにする。

ディレクトリ構成

tree
.
├── main.py
├── config.py
└── requirements.txt

ソースコード

main.py
from config import settings


def main():
    print(f"NAME: {settings.NAME}")
    print(f"EMAIL: {settings.EMAIL}")
    print(f"BACKEND_URL: {settings.BACKEND_URL}")


if __name__ == "__main__":
    main()
config.py
from typing import List
from pydantic import BaseSettings, EmailStr, HttpUrl, validator


class Settings(BaseSettings):
    NAME: str = "My name"
    EMAIL: EmailStr = "email@dummy.com"

    BACKEND_ACCEPTION_URL_LIST: List = [
        "https://www.local.com",
        "https://www.develop.com"
    ]
    # 環境変数
    BACKEND_URL: HttpUrl

    # case_sensitive=Trueの場合、環境変数名はフィールド名と一致する必要がある
    class Config:
        case_sensitive = True

    # validate BACKEND_URL
    @validator('BACKEND_URL')
    def validate_backend_url_value(cls, v, values):
        assert v in values.get("BACKEND_ACCEPTION_URL_LIST")
        return v


settings = Settings()
requirements.txt
typing==3.7.4.3
pydantic[email]==1.9.1

BaseSettingsクラスについて

オブジェクトの型バリデーション

BaseSettingsを継承したクラスにおいて、型アノテーションによって明記された型以外がオブジェクトに代入されるとエラーを出力します。
例えば次のような場合、BACKEND_URLの型バリデーションが失敗するためエラーを出力します。

ソースコード

from pydantic import BaseSettings, EmailStr, HttpUrl, validator

class Settings(BaseSettings):
    BACKEND_URL: HttpUrl

settings = Settings()

実行コマンド

export BACKEND_URL=www.local.com
python3 config.py

環境変数の取得

BaseSettingsを継承したクラスにおいて、値が代入されていないオブジェクトは環境変数から値を読み込んで代入しようとします。
例えば次のような場合、BACKEND_URLは環境変数BACKEND_URLから値を読み込みます。
環境変数が存在しない場合は、エラーを出力します。

ソースコード

from pydantic import BaseSettings, EmailStr, HttpUrl, validator

class Settings(BaseSettings):
    BACKEND_URL: HttpUrl

settings = Settings()

実行コマンド

python3 config.py

Validatorデコレーターの使用

Validator デコレーターを使用すると、以下のような実装が可能です。

  • オブジェクトに対しての独自のバリデーションを実行できる
  • オブジェクト間の関係性についてバリデーションを実行できる

例えば次のような場合に、BACKEND_ACCEPTION_URLにBACKEND_URLが含まれていないため、バリデーションが失敗するためエラーを出力します。

ソースコード

config.py
from typing import List
from pydantic import BaseSettings, EmailStr, HttpUrl, validator


class Settings(BaseSettings):
    BACKEND_ACCEPTION_URL_LIST: List = [
        "https://www.local.com",
        "https://www.develop.com"
    ]
    # 環境変数
    BACKEND_URL: HttpUrl

    # case_sensitive=Trueの場合、環境変数名はオブジェクト名と一致する必要がある
    class Config:
        case_sensitive = True

    # validate BACKEND_URL
    @validator('BACKEND_URL')
    def validate_backend_url_value(cls, v, values):
        assert v in values.get("BACKEND_ACCEPTION_URL_LIST")
        return v


settings = Settings()

実行コマンド

export BACKEND_URL=https://www.local.com
python3 config.py

Discussion