我正在使用 Python 3.4,我尝试将
argparse
与子解析器一起使用,并且我希望与 Python 2.x 中的行为类似,如果我不提供位置参数(以指示子解析器/子程序)我会收到一条有用的错误消息。即,使用 python2
我会收到以下错误消息:
$ python2 subparser_test.py
usage: subparser_test.py [-h] {foo} ...
subparser_test.py: error: too few arguments
我按照
https://stackoverflow.com/a/22994500/3061818中的建议设置
required
属性,但这给了我Python 3.4.0的错误:TypeError: sequence item 0: expected str instance, NoneType found
- 完整回溯:
$ python3 subparser_test.py
Traceback (most recent call last):
File "subparser_test.py", line 17, in <module>
args = parser.parse_args()
File "/usr/local/Cellar/python3/3.4.0/Frameworks/Python.framework/Versions/3.4/lib/python3.4/argparse.py", line 1717, in parse_args
args, argv = self.parse_known_args(args, namespace)
File "/usr/local/Cellar/python3/3.4.0/Frameworks/Python.framework/Versions/3.4/lib/python3.4/argparse.py", line 1749, in parse_known_args
namespace, args = self._parse_known_args(args, namespace)
File "/usr/local/Cellar/python3/3.4.0/Frameworks/Python.framework/Versions/3.4/lib/python3.4/argparse.py", line 1984, in _parse_known_args
', '.join(required_actions))
TypeError: sequence item 0: expected str instance, NoneType found
这是我的程序
subparser_test.py
- 改编自https://docs.python.org/3.2/library/argparse.html#sub-commands:
import argparse
# sub-command functions
def foo(args):
print('"foo()" called')
# create the top-level parser
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
subparsers.required = True
# create the parser for the "foo" command
parser_foo = subparsers.add_parser('foo')
parser_foo.set_defaults(func=foo)
args = parser.parse_args()
args.func(args)
您需要给
subparsers
一个dest
。
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='cmd')
subparsers.required = True
现在:
1909:~/mypy$ argdev/python3 stack23349349.py
usage: stack23349349.py [-h] {foo} ...
stack23349349.py: error: the following arguments are required: cmd
为了发出此“缺少参数”错误消息,代码需要为该参数指定一个名称。对于位置参数(如子解析),该名称(默认情况下)是“dest”。在您链接的 SO 答案中有一个关于此的(小)注释。
上一个 Python 版本中
argparse
的少数“补丁”之一改变了它测试“必需”参数的方式。不幸的是,它引入了有关子解析器的错误。这需要在下一个版本中修复(如果不是更早的话)。
如果您想要 Py2 中的这种可选子解析器行为,看起来最好的选择是使用两阶段解析器,如
中所述如何使用 Python 2.7 的 Argparse 模块设置默认子解析器
相关错误/问题最近有一些活动
https://bugs.python.org/issue9253
不再需要前面提到的解决方法,因为
add_mutually_exclusive_group()
方法也接受必需的参数。
这将表明至少需要一个互斥参数。
parser = argparse.ArgumentParser(prog='PROG')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--foo', action='store_true')
group.add_argument('--bar', action='store_false')
> parser.parse_args([])
> usage: PROG [-h] (--foo | --bar)
> PROG: error: one of the arguments --foo --bar is required
取自argsparse文档:https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_mutually_exclusive_group
我想出了一个方便的填充层,它基本上将 Python 3.7 中添加的
parser.add_subparsers(required=True)
向后移植到 Python 3.6 或更早版本:
class ArgParseShim:
def __init__(self):
self.subparsers = []
def add_required_subparsers(self, p):
res = p.add_subparsers()
self.subparsers.append(res)
return res
def epilogue(self):
for sub in self.subparsers:
keys = sub.choices.keys()
assert len(keys) > 0, "required subparsers must have sub parser (mouthful it is!)"
sub.metavar = '{' + ','.join(keys) + '}'
sub.required = True
调用
shim.add_required_subparsers(p)
代替 p.add_subparsers(required=True)
,并在配置完所有命令后调用 shim.epilogue()
。用例:
if __name__ == '__main__':
shim = ArgParseShim()
parser = argparse.ArgumentParser()
subs = shim.add_required_subparsers(parser)
parser_warehouse = subs.add_parser("warehouse")
parser_warehouse.add_argument("--config", "-c")
warehouse_subs = shim.add_required_subparsers(parser_warehouse)
create_warehouse = warehouse_subs.add_parser("create")
drop_warehouse = warehouse_subs.add_parser("drop")
shim.epilogue()
parser.parse_args()
ctl = Controller()
create_warehouse.set_defaults(func=ctl.create_warehouse) # use set_defaults to link commands to workers as recommended in https://docs.python.org/3.6/library/argparse.html#:~:text=One%20particularly%20effective%20way
drop_warehouse.set_defaults(func=ctl.drop_warehouse)
parser.parse_args().func()