我正在尝试向我的脚本添加参数依赖项。这个想法是 --clone 参数将需要非空 --gituser。

仔细阅读 this example 后,我尝试了以下操作

In [93]: class CloneAction(argparse.Action):
    ...:     def __call__(self, parser, namespace, _):
    ...:         if not namespace.git_user and namespace.clone:
    ...:             parser.error('"--clone" requires legal git user')
    ...:
In [94]: parser = argparse.ArgumentParser()

In [95]: parser.add_argument('-g', '--gituser', dest='git_user', type=str, default='', action=CloneAction)
Out[95]: CloneAction(option_strings=['-g', '--gituser'], dest='git_user', nargs=None, const=None, default='', type=<type 'str'>, choices=None, help=None, metavar=None)

In [96]: parser.add_argument('--clone', action='store_true', default=False)
Out[96]: _StoreTrueAction(option_strings=['--clone'], dest='clone', nargs=0, const=True, default=False, type=None, choices=None, help=None, metavar=None)

唉,它没有用
In [97]: parser.parse_args(['--clone'])
Out[97]: Namespace(clone=True, git_user='')

我做错了什么?

最佳答案

这种内部参数依赖在解析后更容易实现

args = parser.parse_args()
if not namespace.git_user and namespace.clone:
    parser.error('"--clone" requires legal git user')

此时,git_userclone 都已被解析,并有它们的最终值。

当您实现它时,自定义操作仅在存在 --gituser 参数时运行。所以我认为当你给它 --gituser 而没有 --clone 时它会引发错误。

您可以为 --clone 提供类似的自定义操作,但它也必须处理 store_true 详细信息。 --clone --gituser value 序列会发生什么? clone 操作将在 gituser 值被解析之前运行。像这样的测试会遇到一些棘手的参数顺序问题。

其他几个问题:
  • 您的自定义操作不存储任何值,无论错误如何。最好自定义 store 子类。
  • 自定义操作应该引发 argparse.ArgumentError 而不是直接调用 parser.error

  • unittest 文件 test/test_argparse.py 有一个自定义操作的示例,其中包含这样的相互测试。但它只是一个玩具,验证这样的代码是允许的。

    ==================

    理论上,您可以实现设置 --clone 操作的 required 属性的 --gituser 操作。这样,如果不使用 --gituserrequired 的最终 parse_args Action 测试将引发错误。但这需要保存对 out[95] 中显示的 Action 的引用(或在 parse._actions 列表中找到它。可行但很困惑。

    ====================

    这是来自 test/test_argparse.py 的一对交互自定义操作类的示例。
    class OptionalAction(argparse.Action):
    
        def __call__(self, parser, namespace, value, option_string=None):
            try:
                # check destination and option string
                assert self.dest == 'spam', 'dest: %s' % self.dest
                assert option_string == '-s', 'flag: %s' % option_string
                # when option is before argument, badger=2, and when
                # option is after argument, badger=<whatever was set>
                expected_ns = NS(spam=0.25)
                if value in [0.125, 0.625]:
                    expected_ns.badger = 2
                elif value in [2.0]:
                    expected_ns.badger = 84
                else:
                    raise AssertionError('value: %s' % value)
                assert expected_ns == namespace, ('expected %s, got %s' %
                                                  (expected_ns, namespace))
            except AssertionError:
                e = sys.exc_info()[1]
                raise ArgumentParserError('opt_action failed: %s' % e)
            setattr(namespace, 'spam', value)
    
    NSargparse.Namespace 的简写。
    class PositionalAction(argparse.Action):
    
        def __call__(self, parser, namespace, value, option_string=None):
            try:
                assert option_string is None, ('option_string: %s' %
                                               option_string)
                # check destination
                assert self.dest == 'badger', 'dest: %s' % self.dest
                # when argument is before option, spam=0.25, and when
                # option is after argument, spam=<whatever was set>
                expected_ns = NS(badger=2)
                if value in [42, 84]:
                    expected_ns.spam = 0.25
                elif value in [1]:
                    expected_ns.spam = 0.625
                elif value in [2]:
                    expected_ns.spam = 0.125
                else:
                    raise AssertionError('value: %s' % value)
                assert expected_ns == namespace, ('expected %s, got %s' %
                                                  (expected_ns, namespace))
            except AssertionError:
                e = sys.exc_info()[1]
                raise ArgumentParserError('arg_action failed: %s' % e)
            setattr(namespace, 'badger', value)
    

    它们用于
    parser = argparse.ArgumentParser()
    parser.add_argument('-s', dest='spam', action=OptionalAction,
            type=float, default=0.25)
    parser.add_argument('badger', action=PositionalAction,
            type=int, nargs='?', default=2)
    

    并且应该与:
    '-s0.125' producing: NS(spam=0.125, badger=2)),
    '42',                NS(spam=0.25, badger=42)),
    '-s 0.625 1',        NS(spam=0.625, badger=1)),
    '84 -s2',            NS(spam=2.0, badger=84)),
    

    这是可以进行的交叉检查类型的示例。但我要重申的是,通常交互最好在解析之后处理,而不是在解析期间处理。

    至于实现问题 - 如果用户没有给你 --gituser ,你的自定义 Action 永远不会被调用。 Action.__call__optional 仅在使用该参数时使用。 positionals 总是被使用,但不是 optionals

    关于python - Python 中的参数依赖项 - 无法使其工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39437461/

    10-12 19:06