ArgumentParser:使用某些参数选项时未强制执行的独占组

时间:2017-05-18 08:32:19

标签: python argparse

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('--foo', nargs='?', default=True, const=True)
group.add_argument('--bar', dest='foo', action='store_false')
parser.parse_args(['--foo', '--bar']) # no error

我有点困惑,尽管--foo--bar应该是独占的,但上面的代码不会产生任何错误。这是预期的行为吗?或者我们不应该在专属组中搞乱参数选项吗?

请注意,当default=True未作为--foo的参数传递时,会发生预期的错误。

我在Python 2.7.13以及3.5.3中观察到了这种行为。

1 个答案:

答案 0 :(得分:1)

相关代码隐藏在take_action

        argument_values = self._get_values(action, argument_strings)

        # error if this argument is not allowed with other previously
        # seen arguments, assuming that actions that use the default
        # value don't really count as "present"
        if argument_values is not action.default:
            seen_non_default_actions.add(action)
            for conflict_action in action_conflicts.get(action, []):
                if conflict_action in seen_non_default_actions:
                    msg = _('not allowed with argument %s')
                    action_name = _get_action_name(conflict_action)
                    raise ArgumentError(action, msg % action_name)

,特别是'argument_values is not action.default'测试。

可选位置(nargs='?')总是被看到, in the sense that an empty list satisfies its nargs . In that case it gets the action.default value. That's handled by a special case in _ get_values()`。

但是对于互斥测试我们不希望这些行为被“看到”,因此这个额外的测试来设置'seen_non_default_actions`集。

is测试非常严格。这些项目必须具有相同的id

您的示例失败,因为True中的const=True具有与default=True中相同的ID。

parser.parse_args(['--foo', '--bar'])foo设置为const。但是因为它与default匹配,所以它不会被看到,并且不会触发排他错误。

通常constdefault会有不同的值,利用'?'的3向解析。

is not测试在另一个案例中失败了。小于256的数字是唯一的。有关详情,请参阅http://bugs.python.org/issue18943

测试用例:

In [1]: import argparse
   ...: parser = argparse.ArgumentParser()
   ...: group = parser.add_mutually_exclusive_group()
   ...: a1=group.add_argument('--foo', nargs='?', default=True, const=True)
   ...: a2=group.add_argument('--bar', action='store_false')
   ...: 
In [2]: parser.parse_args(['--foo', '--bar'])
Out[2]: Namespace(bar=False, foo=True)      # the True's match

更改const

In [3]: a1.const
Out[3]: True
In [4]: a1.const='other'
In [5]: parser.parse_args(['--foo', '--bar'])
usage: ipython3 [-h] [--foo [FOO] | --bar]
ipython3: error: argument --bar: not allowed with argument --foo
An exception has occurred, use %tb to see the full traceback.
SystemExit: 2

另一种情况,其中值在is意义上匹配:

In [6]: a1.const=None;a1.default=None
In [7]: parser.parse_args(['--foo', '--bar'])
Out[7]: Namespace(bar=False, foo=None)

和小数字:

In [8]: a1.const=3;a1.default=3
In [9]: parser.parse_args(['--foo', '--bar'])
Out[9]: Namespace(bar=False, foo=3)

但不是大的:

In [10]: a1.const=300;a1.default=300
In [11]: parser.parse_args(['--foo', '--bar'])
usage: ipython3 [-h] [--foo [FOO] | --bar]
ipython3: error: argument --bar: not allowed with argument --foo
An exception has occurred, use %tb to see the full traceback.
SystemExit: 2

字符串可能很棘手。代码中的文字是唯一的,但通过拆分创建的文字不是:

In [12]: a1.default='test'
In [14]: parser.parse_args(['--foo', 'test', '--bar'])
Out[14]: Namespace(bar=False, foo='test')   # no error

这更像是命令行如何提供字符串:

In [16]: parser.parse_args('--foo test --bar'.split())
usage: ipython3 [-h] [--foo [FOO] | --bar]
ipython3: error: argument --bar: not allowed with argument --foo
An exception has occurred, use %tb to see the full traceback.
SystemExit: 2