概要
複数のBaseSettingsサブクラスをインスタンス化するときの、CLI引数の衝突回避方法についてです。
結論としては、cli_ignore_unknown_args=Trueを与えるとよいです。
環境変数とCLI引数でオプション名が違うことに気づかなくて数時間解決にかかったのでメモを残します。
背景
pydantic-settingsのBaseSettingsは環境変数とCLI引数を同時に定義することができて便利です。
複数の箇所でBaseSettingsを使用していて、それらを両方インスタンス化する必要があるとき、CLI引数の衝突がおこることがあります。
つまり、一方には存在し、もう一方には存在しない環境変数やCLI引数があるとエラーになってしまいます。
一方に存在しないCLI引数を指定してインスタンス化させてみます。
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class SettingsA(BaseSettings):
name: str = Field(default="", description="名前")
title: str = Field(default="", description="タイトル")
model_config = SettingsConfigDict(
env_file=None,
case_sensitive=False,
cli_parse_args=True,
extra = "ignore"
)
class SettingsB(BaseSettings):
title: str = Field(default="", description="タイトル")
model_config = SettingsConfigDict(
env_file=None,
case_sensitive=False,
cli_parse_args=True,
extra = "ignore"
)
def test_settings_with_cli_args():
"""CLIからの引数でSettingsインスタンスを作成するテスト"""
import sys
# SettingsAのテスト
sys.argv = [
"script.py",
"--name=test_user",
"--title=test_title",
]
settings_a = SettingsA()
settings_b = SettingsB(
title=settings_a.title,
)
if __name__ == "__main__":
test_settings_with_cli_args()
実行するとunrecognized argumentsでエラーになります。
usage: script.py [-h] [--title str]
script.py: error: unrecognized arguments: --name=test_user
一方でCLI引数ではなく環境変数を用いた場合にはエラーなく実行されます。
def test_settings_with_envs():
"""環境変数でSettingsインスタンスを作成するテスト"""
import os
# SettingsAのテスト1
os.environ["NAME"] = "test_user"
settings_a = SettingsA()
settings_b = SettingsB(title = settings_a.title)
if __name__ == "__main__":
# test_settings_with_cli_args() # エラー
test_settings_with_envs() # 成功
これはextra="ignore"を指定することで、不要な環境変数が無視されるためです。
一方で、extra="ignore"を指定してもCLI引数の衝突は無視できません。
解決方法
model_configでcli_ignore_unknown_args=Trueを指定すると引数の衝突を回避できます。
class SettingsB(BaseSettings):
title: str = Field(default="", description="タイトル")
model_config = SettingsConfigDict(
env_file=None,
case_sensitive=False,
cli_parse_args=True,
cli_ignore_unknown_args=True, # 追記
extra="ignore",
)
おわりに
オプション引数の存在に気づかず、model_config.cli_parse_argsのオーバーライドなどいろいろ検討して時間が過ぎてしまいました。
同じことでハマった方の参考になれば幸いです。