@Vikasによって提供されるソリューションは、サブコマンド固有のオプションの引数に対して失敗しますが、アプローチは有効です。改善されたバージョンは次のとおりです。
import argparse
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
parser_b = subparsers.add_parser('command_b', help='command_b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')
argv = ['--foo', 'command_a', '12', 'command_b', '--baz', 'Z']
while argv:
print(argv)
options, argv = parser.parse_known_args(argv)
print(options)
if not options.subparser_name:
break
これはのparse_known_args
代わりにを使用しますparse_args
。parse_args
現在のサブパーサーに不明な引数が検出されるとすぐに中止されます。parse_known_args
れ、返されたタプルの2番目の値として返されます。このアプローチでは、残りの引数が再びパーサーに送られます。したがって、コマンドごとに、新しい名前空間が作成されます。
この基本的な例では、すべてのグローバルオプションが最初のオプションの名前空間にのみ追加され、後続の名前空間には追加されないことに注意してください。
このアプローチはほとんどの状況で問題なく機能しますが、3つの重要な制限があります。
- 次のように、異なるサブコマンドに同じオプションの引数を使用することはできません。
myprog.py command_a --foo=bar command_b --foo=bar
。
- サブコマンド(
nargs='?'
または)で可変長の位置引数を使用することはできません。nargs='+'
またはnargs='*'
)。
- 既知の引数はすべて、新しいコマンドで「中断」することなく解析されます。たとえば
PROG --foo command_b command_a --baz Z 12
、上記のコードで--baz Z
はcommand_b
、によってではなく、によって消費されますcommand_a
ます。
これらの制限は、argparseの直接的な制限です。これは、単一のサブコマンドを使用している場合でも、argparseの制限を示す簡単な例です。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('spam', nargs='?')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
parser_b = subparsers.add_parser('command_b', help='command_b help')
options = parser.parse_args('command_a 42'.split())
print(options)
これにより、が発生しerror: argument subparser_name: invalid choice: '42' (choose from 'command_a', 'command_b')
ます。
原因は、内部メソッドargparse.ArgParser._parse_known_args()
が貪欲すぎて、それがcommand_a
オプションのspam
引数の値であると想定しているためです。特に、オプションの引数と位置引数を「分割」する場合、引数_parse_known_args()
の名前(command_a
またはなどcommand_b
)は調べませんが、引数リストのどこにあるかだけを調べます。また、サブコマンドが残りのすべての引数を消費することも前提としています。この制限argparse
、マルチコマンドサブパーサーの適切な実装も妨げられます。残念ながら、これは、適切な実装には、argparse.ArgParser._parse_known_args()
200行以上のコードであるメソッドの完全な書き直しが必要であることを意味します。
これらの制限を考えると、サブコマンドの代わりに単一の多肢選択式引数に単純に戻すオプションである可能性があります。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--bar', type=int, help='bar help')
parser.add_argument('commands', nargs='*', metavar='COMMAND',
choices=['command_a', 'command_b'])
options = parser.parse_args('--bar 2 command_a command_b'.split())
print(options)
使用情報にさまざまなコマンドをリストすることも可能です。私の回答を参照してくださいhttps://stackoverflow.com/a/49999185/428542