はじめに
幕が上がる 瞬間が好き♪ nikkieです。
pydantic-settingsでCLIの素振りの続き、今回はサブコマンドまわりを触ります。
目次
pydantic-settingsでCLIが作れる!
パースライブラリPydanticを設定にも適用するpydantic-settings。
なんと設定だけでなく、CLIアプリケーションも作れちゃいます!
pydantic-settingsで作るCLIのよさは、オプション引数を環境変数からも指定できること。
標準ライブラリのargparseでこれを実現するのはいささか骨が折れそうなのですが、pydantic-settingsではデフォルトでサポートされているんです!
argparseすきすきの民の私ですが、これは乗り換えを決意するのに十分すぎる理由でした(なので素振りをしています)
前回の素振りより、オプション引数・位置引数の扱いが分かっています。
pydantic_settings.BaseSettings
を継承したクラスにて
- オプション引数:
string: str = Field(validation_alias=AliasChoices("s", "string"))
-s
や--string
で指定できる- 環境変数
S
やSTRING
でも指定できる(素晴らしすぎる!!👏) - デフォルト値を持たせることも可能(
Field("デフォルト値", validation_alias=...)
)
- 位置引数:
string: CliPositionalArg[str]
サブコマンド
Rustのclapで素振りしたのと同様なCLIを今回作りました2。
% uv run --quiet cli.py position エミリー 5 エミリーエミリーエミリーエミリーエミリー % OPTION_S=エミリー uv run --quiet cli.py option --num 5 エミリーエミリーエミリーエミリーエミリー
動作環境
- uv 0.4.27 (Homebrew 2024-10-25)
- Python 3.13.0
- pydantic-settings 2.7.0
- Pydantic 2.10.4
ドキュメント「Subcommands and Positional Arguments」より
https://docs.pydantic.dev/latest/concepts/pydantic_settings/#subcommands-and-positional-arguments
- サブコマンドに使うのは
CliSubCommand
(によるアノテーション)
class Settings(BaseSettings): position: CliSubCommand[Position] option: CliSubCommand[Option]
- サブコマンドの実体は
- Pydanticの
BaseModel
を継承したクラス - または、
pydantic.dataclasses.dataclass
でデコレートしたクラス - (IMO:サブコマンドには
BaseSettings
は継承しないんですね!)
- Pydanticの
- BaseSettingsを継承したクラス(コマンドの大元)では、
CliApp.get_subcommand()
を使う- デフォルトでは、サブコマンドのインスタンスの
cli_cmd()
メソッドを呼び出す - https://docs.pydantic.dev/latest/api/pydantic_settings/#pydantic_settings.CliApp.run_subcommand
- デフォルトでは、サブコマンドのインスタンスの
- 設定のとき3と同様に、環境変数は「
BaseSettings
継承クラスの属性名」+「区切り文字」+「サブコマンドのエイリアス」でした- 区切り文字は
env_nested_delimiter
でアンダースコアを指定 - 列挙すると、
OPTION_S
・OPTION_STRING
・OPTION_N
・OPTION_NUM
- 区切り文字は
- (前回記事より)
CliApp.run(Settings)
でSettings
インスタンスのcli_cmd()
メソッドを呼び出してます
サブコマンドもできた〜!🙌
なお、Noteに制約の記述が
it does not allow for multiple subparsers with each subparser having its own set of subcommands.
直面したわけではないのでピンときてはいないのですが、argparseみたいにサブコマンド・サブサブコマンドとネストさせられないってことですかね?
BaseSettings
を継承したクラスをCliSubCommand
アノテーションできないようですし。
argparseでは使うことがあったのでちょっと痛いかもしれないですが、サブサブコマンドが必要になったときに考えようかと思います(サブコマンドだけに合わせて、コマンドの設計を見直すことになりそう)
終わりに
pydantic-settingsで作るCLIで、サブコマンドの作り方を完全に理解しました!
clapの素振りでも書きましたが、位置引数・オプション引数・サブコマンドを押さえたので、argparseで書いているCLIの8割くらいは、pydantic-settingsでも書けそうです(※馬鹿の山感)。
Pydanticですから書いてて型がバチバチ当たりますし、環境変数からオプションを指定できるのは広く使われるCLIは備えている印象のある便利機能で、pydantic-settingsは推したいライブラリになりました。
君が天才!
- argparse(やRustのclap)ではできていました。位置引数のデフォルト値をそれほど使っていない私としては、あまり痛くはないです↩
-
uvがinline script metadataを読んでいるという出力(「Reading inline script metadata」)をしなくする
--quiet
を見つけました。https://docs.astral.sh/uv/reference/cli/#uv-run↩ - 設定の素振りの様子:pydantic-settings素振りの記:ネストした設定 〜BaseModel継承クラスが全ての属性でデフォルト値を持つならば、インスタンス化してBaseSettings側のデフォルト値とする〜 - nikkie-ftnextの日記↩