argparse子命令选项冲突解决器将关键字参数变为位置参数
我有一个Python脚本,它可以运行两个子命令,这两个子命令都接受一个相同的选项,叫做--config
。我想创建一个第三个子命令,可以把前两个子命令顺序地一起运行。
我使用argparse这个库,为每个子命令创建了一个子解析器,还创建了一个第三个子解析器,它的父级是这两个子命令。为了更清楚:
subcommand1 = subparsers.add_parser('subcommand1')
subcommand1.add_argument('--config', help="The config")
subcommand2 = subparsers.add_parser('subcommand2')
subcommand2.add_argument('--config', help="The config")
wrappercommand = subparsers.add_parser('wrappercommand',
parents=[subcommand1, subcommand2],
conflict_handler='resolve')
当我运行wrappercommand或者subcommand2时,一切都正常。但是,运行subcommand1时就出问题了,输出是:
$ run_command.py subcommand1 --config path_to_config.ini
usage: run_command.py subcommand1 config
optional arguments:
help show this help message and exit
config The config
看起来argparse把一个关键字参数(“--config”)变成了一个位置参数(“config”)。这是argparse在处理冲突选项时的正常行为吗?
1 个回答
我觉得你把这个冲突处理器推到了意想不到且未经测试的领域。通常情况下,parents
是独立的解析器,不会被使用。它们只是 Actions
的来源。而关于 -h
的冲突是通过 add_help=False
来处理的。
背景是这样的:使用默认的 conflict_handler
(错误处理)时,在创建 wrappercommand
子解析器时,你会收到错误信息:
argparse.ArgumentError: argument -h/--help: conflicting option string(s): -h, --help
然后在添加了一些 add_help=False
后,你仍然会收到:
argparse.ArgumentError: argument --config: conflicting option string(s): --config
resolve
冲突处理器会把错误信息替换成某种“解决方案”。下面的脚本演示了发生了什么。
resolve
处理器删除了 subcommand1
动作的 option_strings
,同时保留了这些动作。实际上,它把两者都变成了位置参数。而且,由于 help
的 nargs=0
,它总是会被执行。因此,帮助信息就会显示出来。
_handle_conflict_resolve
的目的是去掉第一个参数的痕迹,以便可以添加新的参数。当冲突是由两个 add_argument
命令使用相同的选项字符串引起时,这个方法是有效的。但在这里,冲突是由于从两个父解析器“复制”动作引起的。而父动作是通过引用复制的,所以在“子”解析器中的更改会影响到“父”解析器。
一些可能的解决方案:
直接将参数添加到
wrappercommand
。这个parents
机制只是把父解析器的参数添加到子解析器,并不会按顺序“运行”父解析器。编写你自己的
_handle_conflict_...
函数来正确解决冲突。去掉冲突,这样你就可以在不使用
resolve
处理器的情况下使用parents
。
我已经提交了一个错误报告,包含这个例子:http://bugs.python.org/issue22401:
parent1 = argparse.ArgumentParser(add_help=False)
parent1.add_argument('--config')
parent2 = argparse.ArgumentParser(add_help=False)
parent2.add_argument('--config')
parser = argparse.ArgumentParser(parents=[parent1,parent2],
conflict_handler='resolve')
def foo(parser):
print [(id(a), a.dest, a.option_strings) for a in parser._actions]
foo(parent1)
foo(parent2)
foo(parser)
这会产生:
[(3077384012L, 'config', [])]
[(3076863628L, 'config', ['--config'])]
[(3076864428L, 'help', ['-h', '--help']), (3076863628L, 'config', ['--config'])]
注意 parent1
的 option_strings
缺失,以及另外两个的匹配 id
。parent1
不能再被使用,无论是作为父解析器还是解析器。
argparse - 组合父解析器、子解析器和默认值 是另一个例子,说明通过引用复制父解析器的动作会造成复杂情况(在更改默认值时)。