argparse conflict resolver for options in subcommands turns keyword argument into positional argument

不打扰是莪最后的温柔 提交于 2019-12-01 17:59:34
hpaulj

I think you are pushing this conflict handler into unintended and untested territory. Normally parents are standalone parsers that don't get used. They are just a source for Actions. And conflicts regarding -h are handled with add_help=False.

By way of background: with the default conflict_handler (error) you'd get error messages when creating the wrappercommand subparser:

argparse.ArgumentError: argument -h/--help: conflicting option string(s): -h, --help

and after adding some add_help=False, you'd still get:

argparse.ArgumentError: argument --config: conflicting option string(s): --config

The resolve conflict handler replaces the error messages with some sort of 'resolution'. The script below demonstrates what is happening.

The resolve handler deleted the option_strings for the subcommand1 actions , while leaving the actions in place. In effect it turns both into positionals. And since help has nargs=0, it is always run. Hence, the help display.

The intention of _handle_conflict_resolve is to remove evidence of the first argument, so the new argument can be added. That works fine when the conflict is produced by two add_argument commands with the same option strings. But here the conflict is produced by 'copying' actions from 2 parents. But parent actions are copied by reference, so changes in the 'child' end up affecting the 'parent'.

Some possible solutions:

  • add the arguments to wrappercommand directly. This parents mechanism just adds arguments from the parents to the child. It does not 'run' the parents sequentially.

  • write your own _handle_conflict_... function to correctly resolve the conflict.

  • remove the conflicts so you can use the parents without using the resolve handler.


I have filed a bug report with this example 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)

which produces:

[(3077384012L, 'config', [])]
[(3076863628L, 'config', ['--config'])]
[(3076864428L, 'help', ['-h', '--help']), (3076863628L, 'config', ['--config'])]

Note the missing option_strings for parent1, and the matching id for the other 2. parent1 cannot be used again, either as a parent or a parser.


argparse - Combining parent parser, subparsers and default values is another case where copying parent's actions by reference creates complications (in changing defaults).

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!