トホホな疑問(25) Python、argparse、help??

JosephHalfmoon

前回に引き続き「Microでない方の」Pythonネタです。普段、使い慣れており、まったく気にしていなかったモジュールがエラーを吐いて動かない。そういうときはダメージが大きいです。「何か悪いことをした?」と自分を責めつつ、パニくりまくり。今回は、コマンドライン引数を解釈する定番のモジュールargparseで体験したトホホです。

コマンドライン引数を解釈するのにargparseを使われている方は多いんじゃないかと思います。定番。私もPythonを使い始めた頃から長らくお世話になっております。使い方は特に難しいこともなく、手になじんでおりました。そのマニュアルなんか読まなくてもOKな感じがツボにハマる原因だったのですが。

さて、いつものように argparseモジュールを使ってコマンドライン引数を取り出して処理を進めるプログラムを作りました。

コマンドライン引数をいろいろ与え、テストなどしてOK

に見えました。最後に、-h オプションを付けてみました。argparseモジュールをお使いの方はよくご存知だと思いますが、argparseはコマンドラインに -h を与えると help メッセージ を出力してくれる、という機能までついているのです。これまた、普段からお世話になっている機能。自分で書いたプログラムでもどんな引数オプションあったのか、すぐ忘れるから。pydocでドキュメンテーションコメントを読むのも手ですが、それをしなくても、argparseの -h があれば、使用には十分なことが多い。-h つけて実行してみました。こんな感じ(テスト用にシンプル化したものですが)

$ python testArgParse.py -h
Traceback (most recent call last):
  File "testArgParse.py", line 33, in <module>
    main()
  File "testArgParse.py", line 23, in main
    args = parser.parse_args()
~途中略~
    return self._get_help_string(action) % params
ValueError: unsupported format character ']' (0x5d) at index 6

途中略のところには、argparseモジュール内のスタックトレースが嫌という長さで並んでいます。え、何か悪いことをした?さっきまで、ちゃんと引数受け付けて動いていたじゃん。なんで?

該当部分はこんな感じ。

parser = argparse.ArgumentParser(description='testArgParse.py.')
parser.add_argument('--ABC', nargs=1, help='XYZ [ms]')
parser.add_argument('--STU', nargs=1, help='OPQ [%]')
args = parser.parse_args()

一瞬、呆然としましたが、エラーメッセージをそのまま解釈すれば、サポートされていないフォーマット文字がある。フォーマット文字「角括弧閉じ」。そんなの書いた記憶がない。しかし、よく見れば、引数の単位を明確にするつもりで、

  • XYZ [ms]  ミリセカンドのつもり
  • OPQ [%] パーセントのつもり

などとhelpに文字列を与えていました。helpに与える文字列、こりゃ単なる文字列だと、長いこと「思い込んで」おりました。が、実は違いました。公式ドキュメントから1行引用させていただきます。

プログラム名は、sys.argv[0] から取られた場合でも prog= 引数で与えられた場合でも、ヘルプメッセージ中では %(prog)s フォーマット指定子で利用できます。

つまり、helpに与える文字列は、昔風の%で始まる書式指定を含む

フォーマット書式指定文字列

であったのでした。よって単位(%は物理的な単位ではないけれども。。。)の注釈のつもりで書いた [%]の%]の部分が書式指定として解釈され、sでもrでもない]などという指定は知らん!といって怒られていたのでした。そこで以下のように修正。

parser = argparse.ArgumentParser(description='testArgParse.py.')
parser.add_argument('--ABC', nargs=1, help='XYZ [ms]')
parser.add_argument('--STU', nargs=1, help='OPQ [%%]')
args = parser.parse_args()

ぶっちゃけ1文字加えただけです。でもこれで、意図どおりのhelpが表示されるようになりました。こんな感じ。

$ python testArgParse.py -h
usage: testArgParse.py [-h] [--ABC ABC] [--STU STU]

testArgParse.py.

optional arguments:
  -h, --help show this help message and exit
  --ABC ABC XYZ [ms]
  --STU STU OPQ [%]

慣れ過ぎて「忘れる」のもトホホの原因だね。。。

トホホな疑問(24) Python 3.8.0、chcp関係ない?

トホホな疑問(26) Python Windowsでの Shebang行