Settings Management¶
Pydantic Settingsは、環境変数やシークレットファイルから設定や構成クラスをロードするためのオプションのPydantic機能を提供します。
Warning
このドキュメントは、https://raw.githubusercontent.com/pydantic/pydantic-settings/main/docs/index.md にリンクされたドキュメントのコピーを翻訳しています。
Installation¶
インストールは次のように簡単です。
pip install pydantic-settings
Usage¶
BaseSettings
から継承するモデルを作成した場合、モデルイニシャライザは、キーワード引数として渡されていないフィールドの値を、環境から読み取って決定しようとします(一致する環境変数が設定されていない場合でも、デフォルト値が使用されます)。
これにより、次のことが容易になります。
- 明確に定義され、型をヒントにしたアプリケーション構成クラスの作成
- 環境変数から構成への変更を自動的な読み取り
- 必要に応じて、イニシャライザの特定の設定を手動での上書き(ユニットテストなど)
次に例を示します。
from typing import Any, Callable, Set
from pydantic import (
AliasChoices,
AmqpDsn,
BaseModel,
Field,
ImportString,
PostgresDsn,
RedisDsn,
)
from pydantic_settings import BaseSettings, SettingsConfigDict
class SubModel(BaseModel):
foo: str = 'bar'
apple: int = 1
class Settings(BaseSettings):
auth_key: str = Field(validation_alias='my_auth_key') # (1)!
api_key: str = Field(alias='my_api_key') # (2)!
redis_dsn: RedisDsn = Field(
'redis://user:pass@localhost:6379/1',
validation_alias=AliasChoices('service_redis_dsn', 'redis_url'), # (3)!
)
pg_dsn: PostgresDsn = 'postgres://user:pass@localhost:5432/foobar'
amqp_dsn: AmqpDsn = 'amqp://user:pass@localhost:5672/'
special_function: ImportString[Callable[[Any], Any]] = 'math.cos' # (4)!
# to override domains:
# export my_prefix_domains='["foo.com", "bar.com"]'
domains: Set[str] = set()
# to override more_settings:
# export my_prefix_more_settings='{"foo": "x", "apple": 1}'
more_settings: SubModel = SubModel()
model_config = SettingsConfigDict(env_prefix='my_prefix_') # (5)!
print(Settings().model_dump())
"""
{
'auth_key': 'xxx',
'api_key': 'xxx',
'redis_dsn': Url('redis://user:pass@localhost:6379/1'),
'pg_dsn': MultiHostUrl('postgres://user:pass@localhost:5432/foobar'),
'amqp_dsn': Url('amqp://user:pass@localhost:5672/'),
'special_function': math.cos,
'domains': set(),
'more_settings': {'foo': 'bar', 'apple': 1},
}
"""
- 環境変数名は
validation_alias
を使用してオーバーライドされます。この場合、環境変数my_auth_key
がauth_key
の代わりに読み込まれます。詳細については、Field
documentationを参照してください。
- 環境変数名は
alias
を使用してオーバーライドされます。この場合、環境変数my_api_key
が検証とシリアライゼーションの両方にapi_key
の代わりに使用されます。詳細については、Field
documentationを参照してください。
AliasChoices
クラスでは、1つのフィールドに対して複数の環境変数名を持つことができます。最初に見つかった環境変数が使用されます。詳細については、AliasChoices
を確認してください。
ImportString
クラスを使用すると、文字列からオブジェクトをインポートできます。この場合、環境変数special_function
が読み込まれ、関数math.cos
がインポートされます。
env_prefix
構成設定では、すべての環境変数に接頭辞を設定できます。詳細については、環境変数名のドキュメントを参照してください。
Validation of default values¶
pydanicのBaseModel
とは異なり、BaseSettings
フィールドのデフォルト値はデフォルトで検証されます。
この動作を無効にするには、model_config
またはField(validate_default=False)
のフィールドレベルでvalidate_default=False
を設定します。
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(validate_default=False)
# default won't be validated
foo: int = 'test'
print(Settings())
#> foo='test'
class Settings1(BaseSettings):
# default won't be validated
foo: int = Field('test', validate_default=False)
print(Settings1())
#> foo='test'
詳細については、Validation of default valuesを参照してください。
Environment variable names¶
デフォルトでは、環境変数名はフィールド名と同じです。
すべての環境変数のプレフィックスを変更するには、env_prefix
config設定を設定するか、インスタンス化時に_env_prefix
キーワード引数を使用します。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix='my_prefix_')
auth_key: str = 'xxx' # will be read from `my_prefix_auth_key`
Note
デフォルトのenv_prefix
は''
(空文字列)です。
1つのフィールドの環境変数名を変更する場合は、エイリアスを使用できます。
これには2つの方法があります。
Field(alias=...)
を使用する(上記のapi_key
を参照)Field(validation_alias=...)
を使用します(上記のauth_key
を参照)
エイリアスの詳細については、Field
aliases documentationを参照してください。
env_prefix
はエイリアスを持つフィールドには適用されません。これは、環境変数名がフィールドエイリアスと同じであることを意味します:
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix='my_prefix_')
foo: str = Field('xxx', alias='FooAlias') # (1)!
env_prefix
will be ignored and the value will be read fromFooAlias
environment variable.
Case-sensitivity¶
デフォルトでは、環境変数名の大文字と小文字は区別されません。
環境変数名の大文字と小文字を区別する場合は、case_sensitive
構成設定を設定します。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(case_sensitive=True)
redis_host: str = 'localhost'
case_sensitive
がTrue
の場合、環境変数名はフィールド名と一致する必要があります(オプションで接頭辞が必要)。したがって、この例ではredis_host
はexport redis_host
を介してのみ変更できます。
環境変数にすべて大文字の名前を付ける場合は、attributeにもすべて大文字の名前を付ける必要があります。
Field(validation_alias=...)
を使用して、環境変数に任意の名前を付けることができます。
大文字と小文字の区別は、インスタンス化時に_case_sensitive
キーワード引数を使用して設定することもできます。
ネストされたモデルの場合、case_sensitive
設定はすべてのネストされたモデルに適用されます。
import os
from pydantic import BaseModel, ValidationError
from pydantic_settings import BaseSettings
class RedisSettings(BaseModel):
host: str
port: int
class Settings(BaseSettings, case_sensitive=True):
redis: RedisSettings
os.environ['redis'] = '{"host": "localhost", "port": 6379}'
print(Settings().model_dump())
#> {'redis': {'host': 'localhost', 'port': 6379}}
os.environ['redis'] = '{"HOST": "localhost", "port": 6379}' # (1)!
try:
Settings()
except ValidationError as e:
print(e)
"""
1 validation error for Settings
redis.host
Field required [type=missing, input_value={'HOST': 'localhost', 'port': 6379}, input_type=dict]
For further information visit https://errors.pydantic.dev/2/v/missing
"""
- 環境変数名が
HOST
(すべて大文字)であるため、host
フィールドが見つからないことに注意してください。
Note
Windowsでは、Pythonのos
モジュールは常に環境変数を大文字小文字を区別しないものとして扱うので、case_sensitive
設定には何の効果もありません。設定は常に大文字小文字を無視して更新されます。
Parsing environment variable values¶
デフォルトでは、環境変数は値が空の場合も含めて逐語的にパースされます。env_ignore_empty
設定をTrue
に設定することで、空の環境変数を無視することを選択できます。
これは、環境からの空の値ではなく、フィールドのデフォルト値を使用する場合に便利です。
ほとんどの単純なフィールド型(int
、float
、str
など)では、環境変数の値はイニシャライザに(文字列として)直接渡された場合と同じようにパースされます。
list
、set
、dict
、サブモデルなどの複雑な型は、環境変数の値をJSONでエンコードされた文字列として扱うことによって、環境から生成されます。
ネストされた複合変数を設定するもう1つの方法は、env_nested_delimiter
構成設定でモデルを構成し、ネストされたモジュールフィールドを指す名前の環境変数を使用することです。
これは単に、変数をネストされたモデルやディクテーションに展開するだけです。
ですから、変数FOO__BAR__BAZ=123
を定義すると、FOO={'BAR':{'BAZ':123}}
に変換されます。
同じ構造を持つ複数の変数がある場合、それらはマージされます。
Note
サブモデルはpydantic.BaseModel
から継承する必要があります。そうしないと、pydantic-settings
がサブモデルを初期化し、サブモデルフィールドの値を個別に収集し、予期しない結果になる可能性があります。
たとえば、次の環境変数があるとします。
# your environment
export V0=0
export SUB_MODEL='{"v1": "json-1", "v2": "json-2"}'
export SUB_MODEL__V2=nested-2
export SUB_MODEL__V3=3
export SUB_MODEL__DEEP__V4=v4
次の設定モデルにロードすることができます。
from pydantic import BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict
class DeepSubModel(BaseModel): # (1)!
v4: str
class SubModel(BaseModel): # (2)!
v1: str
v2: bytes
v3: int
deep: DeepSubModel
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_nested_delimiter='__')
v0: str
sub_model: SubModel
print(Settings().model_dump())
"""
{
'v0': '0',
'sub_model': {'v1': 'json-1', 'v2': b'nested-2', 'v3': 3, 'deep': {'v4': 'v4'}},
}
"""
- サブモデルは
pydantic.BaseModel
から継承する必要があります。
- サブモデルは
pydantic.BaseModel
から継承する必要があります。
env_nested_delimiter
は、上記のようにmodel_config
を介して、またはインスタンス化時に_env_nested_delimiter
キーワード引数を介して設定できます。
ネストされた環境変数は、トップレベルの環境変数JSONよりも優先されます(例えば、上の例ではSUB_MODEL__V2
がSUB_MODEL
より優先されます)。
また、独自のソースクラスを指定して複合型を生成することもできます。
import json
import os
from typing import Any, List, Tuple, Type
from pydantic.fields import FieldInfo
from pydantic_settings import (
BaseSettings,
EnvSettingsSource,
PydanticBaseSettingsSource,
)
class MyCustomSource(EnvSettingsSource):
def prepare_field_value(
self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool
) -> Any:
if field_name == 'numbers':
return [int(x) for x in value.split(',')]
return json.loads(value)
class Settings(BaseSettings):
numbers: List[int]
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (MyCustomSource(settings_cls),)
os.environ['numbers'] = '1,2,3'
print(Settings().model_dump())
#> {'numbers': [1, 2, 3]}
Dotenv (.env) support¶
.envファイル(通常は.env
)は、プラットフォームに依存しない方法で環境変数を簡単に使用できるようにする一般的なパターンです。
.envファイルは、すべての環境変数と同じ一般原則に従い、次のようになります。
# ignore comment
ENVIRONMENT="production"
REDIS_ADDRESS=localhost:6379
MEANING_OF_LIFE=42
MY_VAR='Hello world'
.env
ファイルに変数を入れると、pydanticは以下の2つの方法でロードをサポートします。
BaseSettings
クラスのmodel_config
でenv_file
(およびOSのデフォルトエンコーディングが不要な場合はenv_file_encoding
)を設定します。from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8')
_env_file
キーワード引数(および必要に応じて_env_file_encoding
)を使用してBaseSettings
派生クラスをインスタンス化します。from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8') settings = Settings(_env_file='prod.env', _env_file_encoding='utf-8')
どちらの場合も、渡される引き数の値は任意の有効なパスまたはファイル名(絶対パスまたは現在の作業ディレクトリからの相対パス)にすることができます。そこから、pydanticは変数をロードして検証することで、すべてを処理します。
Note
env_file
にファイル名が指定されている場合、Pydanticは現在の作業ディレクトリのみをチェックし、.env
ファイルの親ディレクトリはチェックしません。
.envファイルを使用している場合でも、pydanticは環境変数とdotenvファイルを読み込みます。環境変数は常にdotenvファイルからロードされた値よりも優先されます。
インスタンス化(方法2)で_env_file
キーワード引数を介してファイルパスを渡すと、model_config
クラスに設定された値(もしあれば)が上書きされます。上記のスニペットが一緒に使用された場合、prod.env
がロードされ、.env
は無視されます。
複数のdotenvファイルをロードする必要がある場合は、複数のファイルパスをタプルまたはリストとして渡すことができます。ファイルは順番にロードされ、各ファイルが前のファイルを上書きします。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
# `.env.prod` takes priority over `.env`
env_file=('.env', '.env.prod')
)
settings=Settings(_env_file=None)
のように、キーワード引数overrideを使用して、インスタンス化キーワード引数としてNone
を渡すことで、(model_config
クラスにファイルが設定されていても)ファイルをまったくロードしないようにPydanticに指示することもできます。
python-dotenvはファイルをパースするために使用されるので、export
のようなbashのようなセマンティクスを使用することができます。(お使いのOSや環境によっては)dotenvファイルをsource
でも使用できる場合があります。詳細については、python-dotenv's documentationを参照してください。
Pydanticの設定では、dotenvファイルの場合にextra
configを考慮します。これは、model_config
でextra=forbid
(default)を設定し、dotenvファイルに設定モデルで定義されていないフィールドのエントリが含まれている場合、設定の構築でValidationError
が発生することを意味します。
pydantic 1.x BaseSettingsとの互換性のために、extra=ignore
を使用してください。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file='.env', extra='ignore')
Note
Pydantic設定は、モデルのenv_prefix
に関係なく、dotenvファイルからすべての値をロードしてモデルに渡します。
したがって、dotenvファイルに追加の値を指定すると、それらがenv_prefix
で始まるかどうかにかかわらず、ValidationError
が発生します。
Command Line Support¶
Pydantic設定は統合されたCLIサポートを提供し、Pydanticモデルを使用してCLIアプリケーションを簡単にすばやく定義できるようにします。Pydantic設定CLIの主なユースケースは次の2つです。
- CLIを使用してPydanticモデルのフィールドを上書きする場合。
- Pydanticモデルを使用してCLIを定義する場合。
デフォルトでは、エクスペリエンスはユースケース#1に合わせて調整され、parsing environment variablesで確立された基礎の上に構築されます。ユースケースが主に#2に該当する場合は、enforcing required arguments at the CLIを有効にする必要があります。
The Basics¶
最初に、parsing environment variablesで示した例を、Pydantic設定CLIを使用してもう一度見てみましょう。
import sys
from pydantic import BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict
class DeepSubModel(BaseModel):
v4: str
class SubModel(BaseModel):
v1: str
v2: bytes
v3: int
deep: DeepSubModel
class Settings(BaseSettings):
model_config = SettingsConfigDict(cli_parse_args=True)
v0: str
sub_model: SubModel
sys.argv = [
'example.py',
'--v0=0',
'--sub_model={"v1": "json-1", "v2": "json-2"}',
'--sub_model.v2=nested-2',
'--sub_model.v3=3',
'--sub_model.deep.v4=v4',
]
print(Settings().model_dump())
"""
{
'v0': '0',
'sub_model': {'v1': 'json-1', 'v2': b'nested-2', 'v3': 3, 'deep': {'v4': 'v4'}},
}
"""
CLIでのパースを有効にするには、単にcli_parse_args
フラグを有効な値に設定します。このフラグはargparse
で定義されているのと同様の意味を保持します。あるいは、インスタンス化時にパースする引数を直接指定することもできます。
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
this_foo: str
print(Settings(_cli_parse_args=['--this_foo', 'is such a foo']).model_dump())
#> {'this_foo': 'is such a foo'}
Note that a CLI settings source is the topmost source by default unless its priority value is customised: CLIの設定ソースは、priority value is customisedでない限り、デフォルトでthe topmost sourceであることに注意してください。
import os
import sys
from typing import Tuple, Type
from pydantic_settings import (
BaseSettings,
CliSettingsSource,
PydanticBaseSettingsSource,
)
class Settings(BaseSettings):
my_foo: str
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return env_settings, CliSettingsSource(settings_cls, cli_parse_args=True)
os.environ['MY_FOO'] = 'from environment'
sys.argv = ['example.py', '--my_foo=from cli']
print(Settings().model_dump())
#> {'my_foo': 'from environment'}
Lists¶
CLI argument parsing of lists supports intermixing of any of the below three styles: リストのCLI引数のパースでは、次の3つのスタイルの混在がサポートされています。
- JSON style
--field='[1,2]'
- Argparse style
--field 1 --field 2
- Lazy style
--field=1,2
import sys
from typing import List
from pydantic_settings import BaseSettings
class Settings(BaseSettings, cli_parse_args=True):
my_list: List[int]
sys.argv = ['example.py', '--my_list', '[1,2]']
print(Settings().model_dump())
#> {'my_list': [1, 2]}
sys.argv = ['example.py', '--my_list', '1', '--my_list', '2']
print(Settings().model_dump())
#> {'my_list': [1, 2]}
sys.argv = ['example.py', '--my_list', '1,2']
print(Settings().model_dump())
#> {'my_list': [1, 2]}
Dictionaries¶
CLI argument parsing of dictionaries supports intermixing of any of the below two styles: ディクショナリのCLI引数のパースでは、次の2つのスタイルの混在がサポートされています。
- JSON style
--field='{"k1": 1, "k2": 2}'
- Environment variable style
--field k1=1 --field k2=2
これらは、次のようにリスト形式と組み合わせて使用することもできます。
--field k1=1,k2=2 --field k3=3 --field '{"k4": 4}'
etc.
import sys
from typing import Dict
from pydantic_settings import BaseSettings
class Settings(BaseSettings, cli_parse_args=True):
my_dict: Dict[str, int]
sys.argv = ['example.py', '--my_dict', '{"k1":1,"k2":2}']
print(Settings().model_dump())
#> {'my_dict': {'k1': 1, 'k2': 2}}
sys.argv = ['example.py', '--my_dict', 'k1=1', '--my_dict', 'k2=2']
print(Settings().model_dump())
#> {'my_dict': {'k1': 1, 'k2': 2}}
Literals and Enums¶
CLI argument parsing of literals and enums are converted into CLI choices. リテラルおよび列挙型のCLI引数のパースは、CLIでの選択項目に変換されます。
import sys
from enum import IntEnum
from typing import Literal
from pydantic_settings import BaseSettings
class Fruit(IntEnum):
pear = 0
kiwi = 1
lime = 2
class Settings(BaseSettings, cli_parse_args=True):
fruit: Fruit
pet: Literal['dog', 'cat', 'bird']
sys.argv = ['example.py', '--fruit', 'lime', '--pet', 'cat']
print(Settings().model_dump())
#> {'fruit': <Fruit.lime: 2>, 'pet': 'cat'}
Aliases¶
Pydanticフィールドエイリアスは、CLI引数エイリアスとして追加されます。長さ1のエイリアスは、短いオプションに変換されます。
import sys
from pydantic import AliasChoices, AliasPath, Field
from pydantic_settings import BaseSettings
class User(BaseSettings, cli_parse_args=True):
first_name: str = Field(
validation_alias=AliasChoices('f', 'fname', AliasPath('name', 0))
)
last_name: str = Field(
validation_alias=AliasChoices('l', 'lname', AliasPath('name', 1))
)
sys.argv = ['example.py', '--fname', 'John', '--lname', 'Doe']
print(User().model_dump())
#> {'first_name': 'John', 'last_name': 'Doe'}
sys.argv = ['example.py', '-f', 'John', '-l', 'Doe']
print(User().model_dump())
#> {'first_name': 'John', 'last_name': 'Doe'}
sys.argv = ['example.py', '--name', 'John,Doe']
print(User().model_dump())
#> {'first_name': 'John', 'last_name': 'Doe'}
sys.argv = ['example.py', '--name', 'John', '--lname', 'Doe']
print(User().model_dump())
#> {'first_name': 'John', 'last_name': 'Doe'}
Subcommands and Positional Arguments¶
サブコマンドと位置引数は、CliSubCommand
およびCliPositionalArg
アノテーションを使用して表現されます。
これらの注釈は必須フィールド(つまり、デフォルト値のないフィールド)にのみ適用できます。
さらに、サブコマンドはpydanicBaseModel
またはpydanic.dataclassesdataclass
から派生した有効な型でなければなりません。
Note
CLI設定サブコマンドは、モデルごとに1つのサブパーサーに制限されています。つまり、モデルのすべてのサブコマンドは1つのサブパーサーにグループ化されます。各サブパーサーが独自のサブコマンドセットを持つ複数のサブパーサーは許可されません。サブパーサーの詳細については、argparse subcommandsを参照してください。
Note
CliSubCommand
とCliPositionalArg
は常に大文字と小文字が区別され、エイリアスをサポートしていません。
import sys
from pydantic import BaseModel, Field
from pydantic.dataclasses import dataclass
from pydantic_settings import (
BaseSettings,
CliPositionalArg,
CliSubCommand,
)
@dataclass
class FooPlugin:
"""git-plugins-foo - Extra deep foo plugin command"""
x_feature: bool = Field(default=False, description='Enable "X" feature')
@dataclass
class BarPlugin:
"""git-plugins-bar - Extra deep bar plugin command"""
y_feature: bool = Field(default=False, description='Enable "Y" feature')
@dataclass
class Plugins:
"""git-plugins - Fake plugins for GIT"""
foo: CliSubCommand[FooPlugin] = Field(description='Foo is fake plugin')
bar: CliSubCommand[BarPlugin] = Field(description='Bar is fake plugin')
class Clone(BaseModel):
"""git-clone - Clone a repository into a new directory"""
repository: CliPositionalArg[str] = Field(description='The repo ...')
directory: CliPositionalArg[str] = Field(description='The dir ...')
local: bool = Field(default=False, description='When the repo ...')
class Git(BaseSettings, cli_parse_args=True, cli_prog_name='git'):
"""git - The stupid content tracker"""
clone: CliSubCommand[Clone] = Field(description='Clone a repo ...')
plugins: CliSubCommand[Plugins] = Field(description='Fake GIT plugins')
try:
sys.argv = ['example.py', '--help']
Git()
except SystemExit as e:
print(e)
#> 0
"""
usage: git [-h] {clone,plugins} ...
git - The stupid content tracker
options:
-h, --help show this help message and exit
subcommands:
{clone,plugins}
clone Clone a repo ...
plugins Fake GIT plugins
"""
try:
sys.argv = ['example.py', 'clone', '--help']
Git()
except SystemExit as e:
print(e)
#> 0
"""
usage: git clone [-h] [--local bool] [--shared bool] REPOSITORY DIRECTORY
git-clone - Clone a repository into a new directory
positional arguments:
REPOSITORY The repo ...
DIRECTORY The dir ...
options:
-h, --help show this help message and exit
--local bool When the repo ... (default: False)
"""
try:
sys.argv = ['example.py', 'plugins', 'bar', '--help']
Git()
except SystemExit as e:
print(e)
#> 0
"""
usage: git plugins bar [-h] [--my_feature bool]
git-plugins-bar - Extra deep bar plugin command
options:
-h, --help show this help message and exit
--y_feature bool Enable "Y" feature (default: False)
"""
Customizing the CLI Experience¶
次のフラグを使用して、必要に応じてCLIエクスペリエンスをカスタマイズできます。
Change the Displayed Program Name¶
cli_prog_name
を設定して、ヘルプテキストの使用方法に表示されるデフォルトのプログラム名を変更します。デフォルトでは、argparseと同様に、現在実行中のプログラムの名前をsys.argv[0]
から取得します。
import sys
from pydantic_settings import BaseSettings
class Settings(BaseSettings, cli_parse_args=True, cli_prog_name='appdantic'):
pass
try:
sys.argv = ['example.py', '--help']
Settings()
except SystemExit as e:
print(e)
#> 0
"""
usage: appdantic [-h]
options:
-h, --help show this help message and exit
"""
Change Whether CLI Should Exit on Error¶
cli_exit_on_error
を使用して、CLI内部パーサーがエラー時に終了するか、SettingsError
例外を発生させるかを変更します。デフォルトでは、CLI内部パーサーはエラー時に終了します。
import sys
from pydantic_settings import BaseSettings, SettingsError
class Settings(BaseSettings, cli_parse_args=True, cli_exit_on_error=False): ...
try:
sys.argv = ['example.py', '--bad-arg']
Settings()
except SettingsError as e:
print(e)
#> error parsing CLI: unrecognized arguments: --bad-arg
Enforce Required Arguments at CLI¶
Pydantic設定は、モデルをインスタンス化するときにさまざまなソースから値を取得するように設計されている。 つまり、必須フィールドは、単一のソース(CLIなど)からは厳密に必須ではありません。 代わりに重要なのは、ソースの1つが必要な値を提供することだけです。
しかし、Pydanticモデルを使用してCLIを定義するユースケース#2では、必須フィールドをCLIで厳密に必須にする必要があります。この動作を有効にするには、cli_enforce_required
を使用します。
import os
import sys
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsError
class Settings(
BaseSettings,
cli_parse_args=True,
cli_enforce_required=True,
cli_exit_on_error=False,
):
my_required_field: str = Field(description='a top level required field')
os.environ['MY_REQUIRED_FIELD'] = 'hello from environment'
try:
sys.argv = ['example.py']
Settings()
except SettingsError as e:
print(e)
#> error parsing CLI: the following arguments are required: --my_required_field
Change the None Type Parse String¶
解析されるCLI文字列値("null"、"void"、"None"など)をNone
に変更するには、cli_parse_none_str
を設定します。デフォルトでは、設定されている場合はenv_parse_none_str
値が使用されます。設定されていない場合は、cli_avoid_json
がFalse
の場合はデフォルトで"null"になり、cli_avoid_json
がTrue
の場合はデフォルトで"None"になります。
import sys
from typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings
class Settings(BaseSettings, cli_parse_args=True, cli_parse_none_str='void'):
v1: Optional[int] = Field(description='the top level v0 option')
sys.argv = ['example.py', '--v1', 'void']
print(Settings().model_dump())
#> {'v1': None}
Hide None Type Values¶
cli_hide_none_type
を有効にして、CLIヘルプテキストからNone
値を非表示にします。
import sys
from typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings
class Settings(BaseSettings, cli_parse_args=True, cli_hide_none_type=True):
v0: Optional[str] = Field(description='the top level v0 option')
try:
sys.argv = ['example.py', '--help']
Settings()
except SystemExit as e:
print(e)
#> 0
"""
usage: example.py [-h] [--v0 str]
options:
-h, --help show this help message and exit
--v0 str the top level v0 option (required)
"""
Avoid Adding JSON CLI Options¶
cli_avoid_json
を有効にして、CLIでJSON文字列になる複雑なフィールドを追加しないようにします。
import sys
from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings
class SubModel(BaseModel):
v1: int = Field(description='the sub model v1 option')
class Settings(BaseSettings, cli_parse_args=True, cli_avoid_json=True):
sub_model: SubModel = Field(
description='The help summary for SubModel related options'
)
try:
sys.argv = ['example.py', '--help']
Settings()
except SystemExit as e:
print(e)
#> 0
"""
usage: example.py [-h] [--sub_model.v1 int]
options:
-h, --help show this help message and exit
sub_model options:
The help summary for SubModel related options
--sub_model.v1 int the sub model v1 option (required)
"""
Use Class Docstring for Group Help Text¶
デフォルトでは、ネストされたモデルのグループヘルプテキストを設定すると、フィールドの説明から取得されます。 代わりに、クラスdocstringからプルするようにCLI設定を構成することもできます。
Note
フィールドがネストされたモデルの結合である場合、cli_use_class_docs_for_groups
がTrue
に設定されていても、グループのヘルプテキストは常にフィールドの説明から取得されます。
import sys
from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings
class SubModel(BaseModel):
"""The help text from the class docstring."""
v1: int = Field(description='the sub model v1 option')
class Settings(BaseSettings, cli_parse_args=True, cli_use_class_docs_for_groups=True):
"""My application help text."""
sub_model: SubModel = Field(description='The help text from the field description')
try:
sys.argv = ['example.py', '--help']
Settings()
except SystemExit as e:
print(e)
#> 0
"""
usage: example.py [-h] [--sub_model JSON] [--sub_model.v1 int]
My application help text.
options:
-h, --help show this help message and exit
sub_model options:
The help text from the class docstring.
--sub_model JSON set sub_model from JSON string
--sub_model.v1 int the sub model v1 option (required)
"""
Integrating with Existing Parsers¶
CLI設定ソースは、デフォルトのCLI設定ソースをroot_parser
オブジェクトを指定するユーザ定義の設定ソースで上書きすることで、既存のパーサと統合できます。
import sys
from argparse import ArgumentParser
from pydantic_settings import BaseSettings, CliSettingsSource
parser = ArgumentParser()
parser.add_argument('--food', choices=['pear', 'kiwi', 'lime'])
class Settings(BaseSettings):
name: str = 'Bob'
# Set existing `parser` as the `root_parser` object for the user defined settings source
cli_settings = CliSettingsSource(Settings, root_parser=parser)
# Parse and load CLI settings from the command line into the settings source.
sys.argv = ['example.py', '--food', 'kiwi', '--name', 'waldo']
print(Settings(_cli_settings_source=cli_settings(args=True)).model_dump())
#> {'name': 'waldo'}
# Load CLI settings from pre-parsed arguments. i.e., the parsing occurs elsewhere and we
# just need to load the pre-parsed args into the settings source.
parsed_args = parser.parse_args(['--food', 'kiwi', '--name', 'ralph'])
print(Settings(_cli_settings_source=cli_settings(parsed_args=parsed_args)).model_dump())
#> {'name': 'ralph'}
CliSettingsSource
は、パーサメソッドを使用してsettings_cls
フィールドをコマンドライン引数として追加することにより、root_parser
オブジェクトと接続します。
CliSettingsSource
内部パーサー表現はargparse
ライブラリに基づいているので、対応するargparse
と同じ属性をサポートするパーサーメソッドが必要です。
カスタマイズできる使用可能なパーサーメソッドと、対応するargparse(デフォルト)を次に示します。
parse_args_method
- (argparse.ArgumentParser.parse_args
)add_argument_method
- (argparse.ArgumentParser.add_argument
)add_argument_group_method
- (argparse.ArgumentParser.add_argument_group
)add_parser_method
- (argparse._SubParsersAction.add_parser
)add_subparsers_method
- (argparse.ArgumentParser.add_subparsers
)formatter_class
- (argparse.HelpFormatter
)
非argparseパーサの場合、サポートされていなければパーサメソッドをNone
に設定することができます。CLI設定では、パーサメソッドが必要であるがNone
に設定されている場合にのみ、ルートパーサに接続するときにエラーが発生します。
Secrets¶
ファイルに秘密の値を設定することは、アプリケーションに機密性の高い設定を提供するための一般的なパターンです。
シークレット・ファイルは、1つの値のみを含み、ファイル名がキーとして使用されることを除いて、dotenvファイルと同じプリンシパルに従います。シークレット・ファイルは次のようになります。
super_secret_database_password
シークレットファイルを取得すると、pydanticは次の2つの方法でそのファイルのロードをサポートします。
BaseSettings
クラスのmodel_config
のsecrets_dir
を、シークレットファイルが保存されているディレクトリに設定します。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(secrets_dir='/var/run')
database_password: str
_secrets_dir
キーワード引数を使用してBaseSettings
派生クラスのインスタンス化:settings = Settings(_secrets_dir='/var/run')
どちらの場合も、渡される引数の値は、任意の有効なディレクトリ(絶対ディレクトリまたは現在の作業ディレクトリに対する相対ディレクトリ)にすることができます。存在しないディレクトリは警告を生成するだけであることに注意してください。 そこから、pydanticは変数をロードして検証することで、すべてを処理します。
secretsディレクトリを使用する場合でも、pydanticはdotenvファイルまたは環境から環境変数を読み込みます。dotenvファイルと環境変数は、secretsディレクトリからロードされた値よりも常に優先されます。
インスタンス化(メソッド2)で_secrets_dir
キーワード引数を介してファイルパスを渡すと、model_config
クラスに設定された値(もしあれば)が上書きされます。
Use Case: Docker Secrets¶
Docker Secretsは、Dockerコンテナで実行されているアプリケーションに機密性の高い設定を提供するために使用できる。 これらのシークレットをpydanticアプリケーションで使用するプロセスは簡単です。Dockerでのシークレットの作成、管理、使用に関する詳細は、公式のDocker documentationを参照してください。
まず、secretsディレクトリを指定するSettingsConfigDict
でSettings
クラスを定義します。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(secrets_dir='/run/secrets')
my_secret_data: str
Note
デフォルトでは、Dockerは/run/secrets
をターゲットマウントポイントとして使用します。別の場所を使用したい場合は、それに応じてConfig.secrets_dir
を変更してください。
次に、Docker CLIを使用してシークレットを作成します。
printf "This is a secret" | docker secret create my_secret_data -
最後に、Dockerコンテナ内でアプリケーションを実行し、新しく作成したシークレットを指定します。
docker service create --name pydantic-with-secrets --secret my_secret_data pydantic-app:latest
Azure Key Vault¶
次の2つのパラメータを設定する必要があります。
url
:例えばhttps://my-resource.vault.azure.net/
です。credential
:DefaultAzureCredential
を使用する場合は、ローカルでaz login
を実行してIDクレデンシャルを取得できます。シークレットにアクセスできるように、IDにロールが割り当てられている必要があります(推奨されるのはKey Vault Secrets User
です)。
フィールド名には、キー・ヴォールト・シークレット名と同じ命名規則を使用する必要があります。たとえば、シークレットの名前がSqlServerPassword
の場合、フィールド名は同じである必要があります。別名も使用できます。
Key Vaultでは、ネストされたモデルは--
セパレータでサポートされます。たとえば、SqlServer--Password
のようになります。
Key Vault配列(例えばMySecret--0
、MySecret--1
)はサポートされていません。
import os
from typing import Tuple, Type
from azure.identity import DefaultAzureCredential
from pydantic import BaseModel
from pydantic_settings import (
AzureKeyVaultSettingsSource,
BaseSettings,
PydanticBaseSettingsSource,
)
class SubModel(BaseModel):
a: str
class AzureKeyVaultSettings(BaseSettings):
foo: str
bar: int
sub: SubModel
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
az_key_vault_settings = AzureKeyVaultSettingsSource(
settings_cls,
os.environ['AZURE_KEY_VAULT_URL'],
DefaultAzureCredential(),
)
return (
init_settings,
env_settings,
dotenv_settings,
file_secret_settings,
az_key_vault_settings,
)
Other settings source¶
その他の設定ソースは、一般的な設定ファイルに使用できます。
json_file
およびjson_file_encoding
引数を使用したJsonConfigSettingsSource
- (オプション)
pyproject_toml_depth
および(オプション)pyproject_toml_table_header
引数を使用したPyprojectTomlConfigSettingsSource
toml_file
引数を使用したTomlConfigSettingsSource
yaml_file
およびyaml_file_encoding引数を使用したYamlConfigSettingsSource
パスのリストを指定して、複数のファイルを指定することもできます。
toml_file = ['config.default.toml', 'config.custom.toml']
これらを使用するには、ここで説明したのと同じメカニズムを使用できます。
from typing import Tuple, Type
from pydantic import BaseModel
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
SettingsConfigDict,
TomlConfigSettingsSource,
)
class Nested(BaseModel):
nested_field: str
class Settings(BaseSettings):
foobar: str
nested: Nested
model_config = SettingsConfigDict(toml_file='config.toml')
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (TomlConfigSettingsSource(settings_cls),)
これにより、ワーキングディレクトリにある次の"config.toml"ファイルを読み込むことができます。
foobar = "Hello"
[nested]
nested_field = "world!"
pyproject.toml¶
"pyproject.toml"は、Pythonプロジェクトで構成値を提供するための標準化されたファイルです。PEP 518では、任意のツール構成を提供するために使用できる[tool]
テーブルが定義されています。
[tool]
テーブルを使用することをお勧めしますが、PyprojectTomlConfigSettingsSource
は"pyproject.toml"ファイル内の任意の場所から変数をロードするために使用できます。
これはSettingsConfigDict(pyproject_toml_table_header=tuple[str, .])
を提供することで制御されます。ここで、値はヘッダー部分のタプルです。
デフォルトではpyproject_toml_table_header=('tool','pydantic-settings')
で、[tool.pydantic-settings]
テーブルから変数をロードします。
from typing import Tuple, Type
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
PyprojectTomlConfigSettingsSource,
SettingsConfigDict,
)
class Settings(BaseSettings):
"""Example loading values from the table used by default."""
field: str
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (PyprojectTomlConfigSettingsSource(settings_cls),)
class SomeTableSettings(Settings):
"""Example loading values from a user defined table."""
model_config = SettingsConfigDict(
pyproject_toml_table_header=('tool', 'some-table')
)
class RootSettings(Settings):
"""Example loading values from the root of a pyproject.toml file."""
model_config = SettingsConfigDict(extra='ignore', pyproject_toml_table_header=())
これは、作業ディレクトリにある以下の"pyproject.toml"ファイルを読み込むことができ、結果はSettings(field='default-table')
、SomeTableSettings(field='some-table')
、RootSettings(field='root')
になります。
field = "root"
[tool.pydantic-settings]
field = "default-table"
[tool.some-table]
field = "some-table"
デフォルトでは、PyprojectTomlConfigSettingsSource
は現在の作業ディレクトリ内の"pyproject.toml"のみを検索します。
ただし、この動作を変更するには2つのオプションがあります。
SettingsConfigDict(pyproject_toml_depth=<int>)
を提供して、"pyproject.toml"が現在の作業ディレクトリに見つからない場合に、ディレクトリツリー内のディレクトリ数upをチェックすることができます。デフォルトでは、親ディレクトリはチェックされません。- ソースがインスタンス化されるときに、明示的なファイルパスをソースに提供することができます(例:
PyprojectTomlConfigSettingsSource(settings_cls, Path('~/.config').resolve()/'pyproject.toml')
)。ファイルパスがこのように提供される場合、絶対パスとして扱われます(他の場所はチェックされません)。
from pathlib import Path
from typing import Tuple, Type
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
PyprojectTomlConfigSettingsSource,
SettingsConfigDict,
)
class DiscoverSettings(BaseSettings):
"""Example of discovering a pyproject.toml in parent directories in not in `Path.cwd()`."""
model_config = SettingsConfigDict(pyproject_toml_depth=2)
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (PyprojectTomlConfigSettingsSource(settings_cls),)
class ExplicitFilePathSettings(BaseSettings):
"""Example of explicitly providing the path to the file to load."""
field: str
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (
PyprojectTomlConfigSettingsSource(
settings_cls, Path('~/.config').resolve() / 'pyproject.toml'
),
)
Field value priority¶
同じ"設定"フィールドに複数の方法で値が指定されている場合、選択された値は次のように決定されます(優先順位の高い順に)。
cli_parse_args
が有効な場合、CLIに渡される引数Settings
クラスのイニシャライザに渡される引数- 環境変数。例えば、上で説明した
my_prefix_special_function
- dotenv(
.env
)ファイルからロードされた変数 - secretsディレクトリからロードされた変数
Settings
モデルのデフォルトのフィールド値
Customise settings sources¶
デフォルトの優先順位があなたのニーズに合わない場合は、Settings
のsettings_customise_sources
メソッドをオーバーライドすることで変更できます。
settings_customise_sources
は引数として4つのcallableを取り、任意の数のcallableをタプルとして返します。
次に、これらの呼び出し可能オブジェクトが呼び出されて、settingsクラスのフィールドへの入力が作成されます。
各呼び出し可能オブジェクトは、設定クラスのインスタンスを唯一の引数として取り、dict
を返します。
Changing Priority¶
返される呼び出し可能オブジェクトの順序によって、入力の優先順位が決まります。最初の項目が最も高い優先順位になります。
from typing import Tuple, Type
from pydantic import PostgresDsn
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
class Settings(BaseSettings):
database_dsn: PostgresDsn
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return env_settings, init_settings, file_secret_settings
print(Settings(database_dsn='postgres://postgres@localhost:5432/kwargs_db'))
#> database_dsn=MultiHostUrl('postgres://postgres@localhost:5432/kwargs_db')
env_settings
とinit_settings
を反転することで、環境変数が__init__
kwargsよりも優先されるようになりました。
Adding sources¶
すでに説明したように、pydanticには複数の設定ソースが組み込まれています。ただし、独自のカスタムソースを追加する必要がある場合もあります。settings_customise_sources
を使用すると、これを非常に簡単に行うことができます。
import json
from pathlib import Path
from typing import Any, Dict, Tuple, Type
from pydantic.fields import FieldInfo
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
SettingsConfigDict,
)
class JsonConfigSettingsSource(PydanticBaseSettingsSource):
"""
A simple settings source class that loads variables from a JSON file
at the project's root.
Here we happen to choose to use the `env_file_encoding` from Config
when reading `config.json`
"""
def get_field_value(
self, field: FieldInfo, field_name: str
) -> Tuple[Any, str, bool]:
encoding = self.config.get('env_file_encoding')
file_content_json = json.loads(
Path('tests/example_test_config.json').read_text(encoding)
)
field_value = file_content_json.get(field_name)
return field_value, field_name, False
def prepare_field_value(
self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool
) -> Any:
return value
def __call__(self) -> Dict[str, Any]:
d: Dict[str, Any] = {}
for field_name, field in self.settings_cls.model_fields.items():
field_value, field_key, value_is_complex = self.get_field_value(
field, field_name
)
field_value = self.prepare_field_value(
field_name, field, field_value, value_is_complex
)
if field_value is not None:
d[field_key] = field_value
return d
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file_encoding='utf-8')
foobar: str
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
JsonConfigSettingsSource(settings_cls),
env_settings,
file_secret_settings,
)
print(Settings())
#> foobar='test'
Accessing the result of previous sources¶
設定の各ソースは、前の設定の出力にアクセスできます。
from typing import Any, Dict, Tuple
from pydantic.fields import FieldInfo
from pydantic_settings import PydanticBaseSettingsSource
class MyCustomSource(PydanticBaseSettingsSource):
def get_field_value(
self, field: FieldInfo, field_name: str
) -> Tuple[Any, str, bool]: ...
def __call__(self) -> Dict[str, Any]:
# Retrieve the aggregated settings from previous sources
current_state = self.current_state
current_state.get('some_setting')
# Retrieve settings from all sources individually
# self.settings_sources_data["SettingsSourceName"]: Dict[str, Any]
settings_sources_data = self.settings_sources_data
settings_sources_data['SomeSettingsSource'].get('some_setting')
# Your code here...
Removing sources¶
また、ソースを無効にすることもできます。
from typing import Tuple, Type
from pydantic import ValidationError
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
class Settings(BaseSettings):
my_api_key: str
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
# here we choose to ignore arguments from init_settings
return env_settings, file_secret_settings
try:
Settings(my_api_key='this is ignored')
except ValidationError as exc_info:
print(exc_info)
"""
1 validation error for Settings
my_api_key
Field required [type=missing, input_value={}, input_type=dict]
For further information visit https://errors.pydantic.dev/2/v/missing
"""
In-place reloading¶
既存の設定をインプレイスで再ロードしたい場合は、__init__
メソッドを使って行うことができます。
import os
from pydantic import Field
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
foo: str = Field('foo')
mutable_settings = Settings()
print(mutable_settings.foo)
#> foo
os.environ['foo'] = 'bar'
print(mutable_settings.foo)
#> foo
mutable_settings.__init__()
print(mutable_settings.foo)
#> bar
os.environ.pop('foo')
mutable_settings.__init__()
print(mutable_settings.foo)
#> foo