Python Argparse有条件地需要参数

时间:2014-09-02 14:41:21

标签: python argparse

我做了尽可能多的研究,但我没有找到最好的方法,只在某些条件下才能使某些cmdline参数成为必要,在这种情况下,只有在给出其他参数的情况下。这是我想要在一个非常基本的层面上做的事情:

p = argparse.ArgumentParser(description='...')
p.add_argument('--argument', required=False)
p.add_argument('-a', required=False) # only required if --argument is given
p.add_argument('-b', required=False) # only required if --argument is given

从我所看到的,其他人似乎只是在最后添加自己的支票:

if args.argument and (args.a is None or args.b is None):
    # raise argparse error here

有没有办法在argparse包中本地执行此操作?

6 个答案:

答案 0 :(得分:24)

我一直在寻找这个问题的简单答案。您需要做的就是检查'--argument'sys.argv是否在import argparse import sys if __name__ == '__main__': p = argparse.ArgumentParser(description='...') p.add_argument('--argument', required=False) p.add_argument('-a', required='--argument' in sys.argv) #only required if --argument is given p.add_argument('-b', required='--argument' in sys.argv) #only required if --argument is given args = p.parse_args() ,所以基本上您可以执行以下代码示例:

required

这种方式True会收到False--argument,具体取决于用户是否使用-a。已经过测试,似乎有效并保证-bpoints之间具有独立的行为。

答案 1 :(得分:10)

您可以通过为--argument提供自定义操作来实施检查,该操作将使用其他关键字参数来指定在使用--argument时应该需要哪些其他操作。

import argparse

class CondAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, **kwargs):
        x = kwargs.pop('to_be_required', [])
        super(CondAction, self).__init__(option_strings, dest, **kwargs)
        self.make_required = x

    def __call__(self, parser, namespace, values, option_string=None):
        for x in self.make_required:
            x.required = True
        try:
            return super(CondAction, self).__call__(parser, namespace, values, option_string)
        except NotImplementedError:
            pass

p = argparse.ArgumentParser()
x = p.add_argument("--a")
p.add_argument("--argument", action=CondAction, to_be_required=[x])

CondAction的确切定义将取决于--argument应该做什么。但是,例如,如果--argument是一个常规的,采用一个参数并保存它的操作类型,那么只需继承argparse._StoreAction就足够了。

在示例解析器中,我们在--a选项中保存对--argument选项的引用,当在命令行上看到--argument时,它会设置required } --aTrue的标记。处理完所有选项后,argparse将验证是否已设置标记为必需的任何选项。

答案 2 :(得分:3)

您的帖子解析测试很好,特别是如果使用is None测试默认值符合您的需求。

http://bugs.python.org/issue11588 'Add "necessarily inclusive" groups to argparse'使用groups机制(mutuall_exclusive_groups的推广)来实现这样的测试。

我编写了一组UsageGroups来实现xor(互斥),andornot等测试。我认为那些全面,但我无法用这些操作表达你的情况。 (看起来我需要nand - 不是,见下文)

此脚本使用自定义Test类,它基本上实现了解析后测试。 seen_actions是解析看到的操作列表。

class Test(argparse.UsageGroup):
    def _add_test(self):
        self.usage = '(if --argument then -a and -b are required)'
        def testfn(parser, seen_actions, *vargs, **kwargs):
            "custom error"
            actions = self._group_actions
            if actions[0] in seen_actions:
                if actions[1] not in seen_actions or actions[2] not in seen_actions:
                    msg = '%s - 2nd and 3rd required with 1st'
                    self.raise_error(parser, msg)
            return True
        self.testfn = testfn
        self.dest = 'Test'
p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
g1 = p.add_usage_group(kind=Test)
g1.add_argument('--argument')
g1.add_argument('-a')
g1.add_argument('-b')
print(p.parse_args())

示例输出为:

1646:~/mypy/argdev/usage_groups$ python3 issue25626109.py --arg=1 -a1
usage: issue25626109.py [-h] [--argument ARGUMENT] [-a A] [-b B]
                        (if --argument then -a and -b are required)
issue25626109.py: error: group Test: argument, a, b - 2nd and 3rd required with 1st

usage并且错误消息仍然需要工作。并且它没有做任何后解析测试不能做的事情。


如果(argument & (!a or !b)),您的测试会引发错误。相反,允许的是!(argument & (!a or !b)) = !(argument & !(a and b))。通过向nand类添加UsageGroup测试,我可以将您的案例实现为:

p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
g1 = p.add_usage_group(kind='nand', dest='nand1')
arg = g1.add_argument('--arg', metavar='C')
g11 = g1.add_usage_group(kind='nand', dest='nand2')
g11.add_argument('-a')
g11.add_argument('-b')

用法是(使用!()标记'nand'测试):

usage: issue25626109.py [-h] !(--arg C & !(-a A & -b B))

我认为这是使用通用用法组表达此问题的最短且最清晰的方式。


在我的测试中,成功解析的输入是:

''
'-a1'
'-a1 -b2'
'--arg=3 -a1 -b2'

应该引发错误的是:

'--arg=3'
'--arg=3 -a1'
'--arg=3 -b2'

答案 3 :(得分:0)

http://bugs.python.org/issue11588解决之前,我只使用nargs

p = argparse.ArgumentParser(description='...')
p.add_argument('--arguments', required=False, nargs=2, metavar=('A', 'B'))

这样,如果任何人提供--arguments,它将有2个值。

也许它的CLI结果可读性较差,但代码要小得多。您可以通过优秀的文档/帮助解决这个问题。

答案 4 :(得分:0)

这实际上与@Mira的答案相同,但是我想在给出选项要求额外的arg的情况下显示它:

例如,如果给定--option foo,则还需要一些参数,而给定--option bar则不需要这些参数:

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--option', required=True,
        help='foo and bar need different args')

    if 'foo' in sys.argv:
        parser.add_argument('--foo_opt1', required=True,
           help='--option foo requires "--foo_opt1"')
        parser.add_argument('--foo_opt2', required=True,
           help='--option foo requires "--foo_opt2"')
        ...

    if 'bar' in sys.argv:
        parser.add_argument('--bar_opt', required=True,
           help='--option bar requires "--bar_opt"')
        ...

这并不完美-例如proggy --option foo --foo_opt1 bar含糊不清,但我需要做的还可以。

答案 5 :(得分:-1)

对于争论,我提出了一个像这样的快速解决方案。 假设:(1)' - 帮助'应该显示帮助而不是抱怨所要求的论点和(2)我们正在解析sys.argv

p = argparse.ArgumentParser(...)
p.add_argument('-required', ..., required = '--help' not in sys.argv )

可以轻松修改此设置以匹配特定设置。 对于所需的定位(如果在命令行上给出了帮助,将会不再需要)我已经提出以下内容:[位置不允许required=...关键字arg!]

p.add_argument('pattern', ..., narg = '+' if '--help' not in sys.argv else '*' )

基本上这会改变所需出现的'模式'在命令行中,从一个或多个到零或更多的情况下' - help'已指定。