Pythonのargparseでオプション引数を動的に変える話
背景
argparse は簡単に実装できる,Python標準のコマンドライン引数の解析モジュールです。チュートリアルもあるので,細かい説明は省きます。
自分はここで色々なファイルを開けるツールを作っていますが,ファイルによって取りたいオプション引数は変わってきます。それをすべて普通に設定してしまうと特定のファイルでしか使われないオプションも表示されてしまい,ヘルプメッセージが煩雑になってしまいます。
ではどうするのか,を色々試行錯誤したので,その結果をまとめます。
ちなみに,この方法を使えば隠しオプションも実装できそうです。
argparseの挙動
現時点のargparseのチュートリアルより,argparseはこんな感じの使い方をします
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)
-
ArgumentParser
インスタンスを作成 - インスタンスに
add_argument
で引数を追加 -
parse_args
で引数を解析
という流れだと理解しています。
実際にソースを読んでみると,
def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s') % ' '.join(argv)
if self.exit_on_error:
self.error(msg)
else:
raise ArgumentError(None, msg)
return args
こうなっています。
parse_known_args
は引数args
(None
の場合はsys.argv
)を解析し,add_args
によって指定されたコマンドラインの位置引数,オプション引数はnamespace
の形でargs
に,それ以外はargv
として返しているようです。そしてargv
が空でなければ(つまり余計なコマンドラインの引数があれば)エラー終了する,という流れのようです。
ちなみに,-h
を指定した場合はparse_known_args
の中でexit()
するようです。
解決策
ということで,parse_known_args
を使えば,余計なコマンドライン引数を渡しても処理を継続してくれそうです。
最終的には,こんな感じで実装をしてみました(イメージ)。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', help='file')
parser.add_argument('-t', '--type', help='specift the type',
choices=['A', 'B'])
tmpargs, rems = parser.parse_known_args()
if tmpargs.type == 'A':
parser.add_argument('--a_option', action='store_true')
elif tmpargs.type == 'B':
parser.add_argument('--b_option', nargs='*')
args = parser.parse_args()
main(args)
これで,コマンドラインで-t A
を指定した場合は--a_option
が取れ,-t B
を指定した場合は--b_option
が取れます。
おまけ
隠しオプションの実装
上で書いたように,(ArgumentParser
でadd_help=False
しない限り)コマンドラインで-h
をした場合は,parse_known_args
の中で終了するようです。
そのため,
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--opt')
parser.parse_known_args()
parser.add_argument('--hidden_opt')
args = parser.parse_args()
main(args)
とすると,--hidden_opt
はヘルプには表示されないが取ることはできる,"隠しオプション"として機能します。
位置引数について
同様の手法でコマンドラインの位置引数を動的に変えられないかと試行錯誤しましたが,こちらは駄目なようでした。どうやらこのあたりを読むと,parse_known_args
の中で必須の引数が揃っているか確認し,足りないようならエラー終了してしまうようです。
(ただしsys.argv
を使っていることは分かったので,自分はこれを使って対応しました。)
終わりに
以上です。argparse
を使ってコマンドラインのオプション引数を動的に変えたいような変人は少なそうですが,隠しオプション引数にも使えるので役に立てば幸いです。
Discussion