argparseによるコマンドライン引数のvalidation
はじめに
タイトルが意識高い系のメールみたいになってしまった。
キーワードは英語の方が検索性が高い気がする(適当)。
コマンドライン引数を受け取り、定型処理を行うプログラムを書くことがある。
その場合のvalidationはしないかmain()
で書いていた。小規模だし、自分しか使わないし...(言い訳)。
流石にこれはスマートとは言えないので調べてみたところ、add_argument()
引数のtype=
, choices=
で大半のことは実現可能だと分かった。
基本文法
parser = argparse.ArgumentParser() parser.add_argument('-a', '--aaa', ...) ... args = parser.parse_args() print(args.aaa) #属性みたいに.で引数の値を使える
type=
でできること
引数を任意の型へ変換
コマンドライン引数はデフォルトではstr
であるため、type=
に型を指定すると変換した値を得られる。
parser.add_argument('--age', type=int, choices=range(1,101), required=True, help='年鄭。intかつ1~100の必須引数。')
自作関数で変換と検証
アルファベット以外はargparse.ArgumentTypeError
例外を投げ、
アルファベットの場合は頭の一文字を大文字に変換して返す関数を定義する。
def ascii_human_name(name_arg:str): if not name_arg.isalpha(): raise argparse.ArgumentTypeError(f'{name_arg} is not alpha.') return name_arg.capitalize()
nargs='+'
で--name
を一つ以上受け取れる。
日本人であれば姓 名
(Yamada Tarou)だが、外国では'姓 名'の2つとは限らないので...
parser.add_argument('--name', required=True, type=ascii_human_name, nargs='+', help='名前。ASCIIで空白区切り。自動で先頭一文字が大文字になります。')
choices=
でできること
カテゴリ変数の指定
最終学歴を引数で入力するとする。ゆたぼん君を考慮して小卒を用意した。
短大・専門?いえ、知らない子ですね。
parser.add_argument('--education', choices=['elementary_school', 'junior_high_school', 'high_school', 'undergraduate', 'graduate'], required=True, help='最終学歴。')
値範囲の制限
type=
とchoices=
がある場合、'type='で型変換されてからchoices=
の評価が実行されるらしい。
Note that inclusion in the choices container is checked after any type conversions have been performed, so the type of the objects in the choices container should match the type specified:
https://docs.python.org/3/library/argparse.html#choices
parser.add_argument('--age', type=int, choices=range(1,101), required=True, help='年鄭。intかつ1~100の必須引数。')
公式ではchoices=
で範囲指定しているが、数が多いと-h
, --help
で大変なことになる。
追記
metavar=
で回避可能っぽい。公式リファレンスはちゃんと読もう(戒め)。
実行例
今までの処理をまとめてCLValidation.py
として保存する。
import argparse from pprint import pprint def ascii_human_name(name_arg:str): if not name_arg.isalpha(): raise argparse.ArgumentTypeError(f'{name_arg} is not alpha.') return name_arg.capitalize() parser = argparse.ArgumentParser(description='validation test program') parser.add_argument('--name', required=True, type=ascii_human_name, nargs='+', help='名前。ASCIIで空白区切り。自動で先頭一文字が大文字になります。') parser.add_argument('--age', type=int, choices=range(1,101), required=True, metavar='AGE (1 ~ 100)', help='年齢。intかつ1~100の必須引数。') parser.add_argument('--education', choices=['elementary_school', 'junior_high_school', 'high_school', 'undergraduate', 'graduate'], required=True, help='最終学歴。') args = parser.parse_args() pprint(args.__dict__, width=1)
勝手にヘルプが生成されるのは便利。
python .\CLValidation.py -h usage: CLValidation.py [-h] --name NAME [NAME ...] --age AGE 1 ~ 100 --education {elementary_school,junior_high_school,high_school,undergraduate,graduate} validation test program optional arguments: -h, --help show this help message and exit --name NAME [NAME ...] 名前。ASCIIで空白区切り。自動で先頭一文字が大文字になります。 --age AGE (1 ~ 100) 年齢。intかつ1~100の必須引数。 --education {elementary_school,junior_high_school,high_school,undergraduate,graduate} 最終学歴。
値が正常な場合
python .\CLValidation.py --name Yamada Tarou --age 15 --education high_school {'age': 15, 'education': 'high_school', 'name': ['Yamada', 'Tarou']}
値が異常な場合
python .\CLValidation.py --name Yamada Lv.100 --age 15 --education high_school usage: CLValidation.py [-h] --name NAME [NAME ...] --age AGE 1 ~ 100 --education {elementary_school,junior_high_school,high_school,undergraduate,graduate} CLValidation.py: error: argument --name: Lv.100 is not alpha.
python .\CLValidation.py --name --age 15 --education high_school usage: CLValidation.py [-h] --name NAME [NAME ...] --age AGE 1 ~ 100 --education {elementary_school,junior_high_school,high_school,undergraduate,graduate} CLValidation.py: error: argument --name: expected at least one argument
python .\CLValidation.py --name Yamada Tarou --age 150 --education high_school usage: CLValidation.py [-h] --name NAME [NAME ...] --age AGE 1 ~ 100 --education {elementary_school,junior_high_school,high_school,undergraduate,graduate} CLValidation.py: error: argument --age: invalid choice: 150 (choose from 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)
エラーメッセージは変わらずうるさいが、仕方ないのか...?
python .\CLValidation.py --name Yamada Tarou --age 15 --education none usage: CLValidation.py [-h] --name NAME [NAME ...] --age AGE 1 ~ 100 --education {elementary_school,junior_high_school,high_school,undergraduate,graduate} CLValidation.py: error: argument --education: invalid choice: 'none' (choose from 'elementary_school', 'junior_high_school', 'high_school', 'undergraduate', 'graduate')
おわりに
type=
最強!