[英]How to handle CLI subcommands with argparse
我需要实现一个命令行界面,程序在其中接受子命令。
例如,如果程序名为“foo”,则 CLI 将如下所示
foo cmd1 <cmd1-options>
foo cmd2
foo cmd3 <cmd3-options>
cmd1
和cmd3
必须至少与它们的选项之一一起使用,并且三个cmd*
参数始终是互斥的。
我正在尝试在 argparse 中使用子解析器,但目前没有成功。 问题在于cmd2
,它没有参数:
如果我尝试添加不带参数的子解析器条目,则parse_args
返回的命名空间将不包含任何告诉我已选择此选项的信息(请参见下面的示例)。 如果我尝试将cmd2
作为参数添加到parser
(而不是子parser
),则 argparse 将期望cmd2
参数后跟任何子解析器参数。
有没有一种简单的方法可以使用argparse
实现这一点? 用例应该很常见……
以下是我迄今为止尝试的更接近我需要的内容:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='Functions')
parser_1 = subparsers.add_parser('cmd1', help='...')
parser_1.add_argument('cmd1_option1', type=str, help='...')
parser_2 = subparsers.add_parser(cmd2, help='...')
parser_3 = subparsers.add_parser('cmd3', help='...')
parser_3.add_argument('cmd3_options', type=int, help='...')
args = parser.parse_args()
首先,子解析器永远不会插入到命名空间中。 在您发布的示例中,如果您尝试将脚本运行为:
$python3 test_args.py cmd1 1
Namespace(cmd1_option1='1')
其中test_args.py
包含您提供的代码(开头是import argparse
,结尾是print(args)
)。
请注意,仅对其参数没有提及cmd1
。 这是设计使然。
正如评论中所指出的,您可以添加将dest
参数传递给add_subparsers
调用的信息。
处理这些情况的常用方法是使用子解析器的set_defaults
方法:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='Functions')
parser_1 = subparsers.add_parser('cmd1', help='...')
parser_1.add_argument('cmd1_option1', type=str, help='...')
parser_1.set_defaults(parser1=True)
parser_2 = subparsers.add_parser('cmd2', help='...')
parser_2.set_defaults(parser2=True)
parser_3 = subparsers.add_parser('cmd3', help='...')
parser_3.add_argument('cmd3_options', type=int, help='...')
parser_3.set_defaults(parser_3=True)
args = parser.parse_args()
print(args)
结果是:
$python3 test_args.py cmd1 1
Namespace(cmd1_option1='1', parser1=True)
$python3 test_args.py cmd2
Namespace(parser2=True)
通常,不同的子解析器在大多数情况下会以完全不同的方式处理参数。 通常的模式是使用不同的函数来运行不同的命令并使用set_defaults
来设置func
属性。 当您解析参数时,您只需调用该可调用对象:
subparsers = parser.add_subparsers()
parser_1 = subparsers.add_parser(...)
parser_1.set_defaults(func=do_command_one)
parser_k = subparsers.add_parser(...)
parser_k.set_defaults(func=do_command_k)
args = parser.parse_args()
if args.func:
args.func(args)
如果add_subparsers
命令被赋予一个dest
则可以将子解析器标识添加到主Namespace
。
从文档:
但是,如果有必要检查被调用的子解析器的名称,则 add_subparsers() 调用的 dest 关键字参数将起作用:
>>> parser = argparse.ArgumentParser()
>>> subparsers = parser.add_subparsers(dest='subparser_name')
>>> subparser1 = subparsers.add_parser('1')
>>> subparser1.add_argument('-x')
>>> subparser2 = subparsers.add_parser('2')
>>> subparser2.add_argument('y')
>>> parser.parse_args(['2', 'frobble'])
Namespace(subparser_name='2', y='frobble')
默认情况下dest
是argparse.SUPPRESS
,它阻止子subparsers
将名称添加到namespace
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.