Pydantic でコマンドライン引数をパースする¶
argparseでもtypeやrequiredを指定すれば、型変換と必須チェックはこなせる。ただし引数が増えるほど、その宣言は受け取り側の型定義と二重管理になる。Pydantic のモデルへ寄せると、型変換と入力チェックの定義が 1 か所で済む。
方法は 2 つ試した。argparseと Pydantic を自分でつなぐ方法と、pydantic-settingsの CLI 機能を使う方法になる。検証環境は Python 3.14.7、pydantic 2.13.4、pydantic-settings 2.15.0 とする。
argparse と組み合わせる¶
モデルのフィールド名からargparseのオプションを組み立て、解析結果をモデルへ渡す。参考にした記事の方法を Pydantic v2 の API へ置き換えると、次のコードになる。
timeoutにはargparseが文字列を渡すが、モデルを通るとintに変わる。
参考にした記事は Pydantic v1 の頃のもので、schema()とparse_obj()を呼んでいる。v2 では前者がmodel_json_schema()、後者がmodel_validate()へ名前を変えた。旧名も 2.13.4 ではPydanticDeprecatedSince20の警告つきで動くが、警告文のとおり V3.0 で削除される予定になっている。
フィールド名を取り出すだけなら、スキーマを組み立てずにmodel_fieldsを読めば足りる。
短いオプション(-bなど)を頭文字から機械的に足す実装も見かける。ただしフィールドの頭文字が重なると、引数を渡す前にArgumentParserの組み立て自体が落ちる。
filenameとfilesizeのように、同じ文字で始まるフィールドは珍しくない。長いオプションだけに絞るほうが安全だ。
pydantic-settings を使う¶
pydantic-settingsは CLI 解析を標準で備えているため、argparseを書かずに済む。
モデルに書いた既定値がそのまま効く。
--helpもモデルの定義から組み立てる。型と、必須か既定値かの区別まで出るので、help文字列を手で書いて保守する必要がない。
複数のモデルを切り替えたい場合は、CliSubCommandでフィールドとして並べ、CliApp.run_subcommand()へ渡す。自分で mixin を組んで分岐する必要はない。
UploadとDownloadはそれぞれcli_cmd()を持つBaseModelにする。実行したサブコマンドのcli_cmd()だけが呼ばれ、--helpにも{upload,download}として並ぶ。
検証エラーの見え方には注意する¶
エラーは 2 段階に分かれる。どこで弾かれるかによって、画面に出る内容が変わる。
パーサー段のエラーは 1 行で終わる¶
pydantic-settingsにはcli_exit_on_errorがあり、既定で有効になっている。受け持つのはパーサー段のエラーで、たとえば未知のオプションを渡すと usage を添えた 1 行で終わる。
検証段のエラーはトレースバックになる¶
型の不一致や必須項目の欠落はパーサーを抜けた後の検証で見つかるため、cli_exit_on_errorの守備範囲から外れる。ここで失敗するとValidationErrorがそのまま飛び、トレースバックが利用者の画面に出る。
argparseと自分でつなぐ方法でも、検証はmodel_validate()が担うので同じトレースバックになる。他人に渡す CLI なら、どちらを選んでもValidationErrorを捕まえて自前のメッセージへ翻訳する処理が別に要る。
まとめ¶
| 観点 | argparse と組み合わせる | pydantic-settings |
|---|---|---|
| 追加の依存 | 不要 | pydantic-settingsが必要 |
--help | 掲載例では型も既定値も出ない | モデルから自動生成 |
| 既定値 | add_argumentへ自分で渡す | モデルの定義がそのまま効く |
| サブコマンド | 自分で組む | CliSubCommandが対応 |
| パーサー段のエラー | usage つき 1 行 | usage つき 1 行 |
| 検証エラー | トレースバック | トレースバック |
既存のargparseの実装を活かすなら Pydantic と組み合わせ、新しく書くならpydantic-settingsを選ぶとよい。ただし検証エラーの見せ方だけは、どちらを選んでも自分で決めることになる。