この記事について
Pythonのバリデーター(Pydantic)を、Pythonの引数のパーサーとして使う方法を紹介します
何が嬉しいの?
- Pydantic以外のOSSライブラリは不要です
- Pythonファイルに渡された引数を検証、型変換させることができます
- Pydanticで定義を書くだけでよいため、ArgumentParserよりも楽です
- IDEの補完が効くようになります
方法
BaseModelを継承したクラスに、以下の関数を書き加えます
@classmethod
def parse_args(cls):
parser = ArgumentParser()
for k in cls.schema()["properties"].keys():
parser.add_argument(f"-{k[0:1]}", f"--{k}")
return cls.parse_obj(parser.parse_args().__dict__)
これでpydanticでparse_argsができるようになります
ソースの例
実際のソースで動きを確認します。
app.py
from argparse import ArgumentParser
from pydantic import BaseModel
class Argments(BaseModel):
"""
Pythonファイルの引数を定義する
"""
bucketname: str # AWSのバケット名
filename: str # ファイル名
timeout: int # タイムアウト時間
@classmethod
def parse_args(cls):
# 付け足した関数
parser = ArgumentParser()
for k in cls.schema()["properties"].keys():
parser.add_argument(f"-{k[0:1]}", f"--{k}")
return cls.parse_obj(parser.parse_args().__dict__)
# 実行してみる: 引数を検証する
args = Argments.parse_args()
print(args)
print(args.bucketname)
print(args.timeout)
コマンドプロンプトで実行する
python app.py --bucketname=aws-bucket-contents-name --filename=filekey.json --timeout=123
実行結果
bucketname='aws-bucket-contents-name' filename='filekey.json' timeout=123
aws-bucket-contents-name
123
parse_argsが返す変数は、PydanticのBaseModelを継承した変数です
pythonファイルに渡された引数がデータとして入っていることが確認できます
嬉しいこと
Pydanticの型なので、VSCodeの補完の恩恵を受けられます
データ型も定義した型に変換されています
検証エラーをさせてみる
タイムアウトはint型が期待値なので、以下のようにすればエラーになります
コマンドプロンプトで実行する
python app.py --bucketname=aws-bucket-contents-name --filename=filekey.json --timeout=12.3
実行結果
pydantic.error_wrappers.ValidationError: 1 validation error for Argments
timeout
value is not a valid integer (type=type_error.integer)
必須になっているファイル名とタイムアウトを渡さないと、エラーになります
コマンドプロンプトで実行する
python app.py --bucketname=aws-bucket-contents-name
実行結果
pydantic.error_wrappers.ValidationError: 2 validation errors for Argments
filename
none is not an allowed value (type=type_error.none.not_allowed)
timeout
none is not an allowed value (type=type_error.none.not_allowed)
必須ではない引数を定義したいときは、Optionalを使います
from typing import Optional
class Argments(BaseModel):
mail: Optional[str] # メールアドレスは必須ではない
# 中略
この--mail
は、引数として渡さなくてもエラーになりません