Python Argparse有条件地需要参数

DJM*_*y12 32 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
Run Code Online (Sandbox Code Playgroud)

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

if args.argument and (args.a is None or args.b is None):
    # raise argparse error here
Run Code Online (Sandbox Code Playgroud)

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

Mir*_*ira 35

一段时间以来,我一直在寻找这个问题的简单答案.您需要做的就是检查是否'--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()
Run Code Online (Sandbox Code Playgroud)

这种方式required接收TrueFalse取决于用户是否使用--argument.已经测试了它,似乎工作,并保证-a-b相互之间的独立行为.

  • 简单,简洁,而且非常棒! (5认同)
  • 此外,这也不会正确显示在 -h/--help 消息中,因为当时没有足够的信息!所以用户可能会有点困惑 (3认同)
  • 这与位置参数不兼容,因为它忽略选项结束标记“--”。例如 `sys.argv[1:] = ['--', '--argument', 'foobar']` -> `-a` 和 `-b` 不应该是必需的,但它们确实是必需的。 (2认同)

che*_*ner 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])
Run Code Online (Sandbox Code Playgroud)

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

在该示例性解析器,我们保存到一个参考--a内部选项--argument的选择,并且当--argument被看作在命令行上,它设置required标志上--aTrue.处理完所有选项后,argparse验证是否已设置标记为必需的任何选项.

  • 正如所写,它不起作用,因为 `Action.__call__` 返回一个 `not Implement` 错误。但是调整“x”的“required”属性的基本想法应该可行。 (2认同)

pab*_*ouk 10

这是一个简单干净的解决方案,具有以下优点:

  • 不会因使用测试进行过度简化的解析而导致歧义和功能损失in sys.argv
  • 无需实施特殊argparse.Actionargparse.UsageGroup类。
  • 即使对于多个复杂的决策参数也可以简单使用。

我注意到一个相当大的缺点(有些人可能会认为这是可取的):帮助文本根据决定参数的状态而变化。

这个想法是使用argparse两次:

  1. 解析决定性的论点,而不是过度简化in sys.argv测试的使用。为此,我们使用一个不显示帮助的简短解析器和.parse_known_args()忽略未知参数的方法。
  2. 正常解析所有内容,同时重用第一步中的解析器作为父级,并获得第一个解析器的可用结果。
import argparse

# First parse the deciding arguments.
deciding_args_parser = argparse.ArgumentParser(add_help=False)
deciding_args_parser.add_argument(
        '--argument', required=False, action='store_true')
deciding_args, _ = deciding_args_parser.parse_known_args()

# Create the main parser with the knowledge of the deciding arguments.
parser = argparse.ArgumentParser(
        description='...', parents=[deciding_args_parser])
parser.add_argument('-a', required=deciding_args.argument)
parser.add_argument('-b', required=deciding_args.argument)
arguments = parser.parse_args()

print(arguments)
Run Code Online (Sandbox Code Playgroud)


hpa*_*ulj 6

您的解析后测试很好,特别是如果默认测试is None适合您的需求。

http://bugs.python.org/issue11588 'Add "necessarily inclusive" groups to argparse'研究使用该groups机制(mutual_exclusive_groups 的概括)来实现这样的测试。

我已经编写了一组UsageGroups实现测试,例如xor(互斥)and、、、ornot。我认为这些内容很全面,但我无法用这些操作来表达你的情况。(看起来我需要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())
Run Code Online (Sandbox Code Playgroud)

示例输出为:

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
Run Code Online (Sandbox Code Playgroud)

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')
Run Code Online (Sandbox Code Playgroud)

用法是(用于!()标记“nand”测试):

usage: issue25626109.py [-h] !(--arg C & !(-a A & -b B))
Run Code Online (Sandbox Code Playgroud)

我认为这是使用通用用途组来表达这个问题的最短、最清晰的方式。


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

''
'-a1'
'-a1 -b2'
'--arg=3 -a1 -b2'
Run Code Online (Sandbox Code Playgroud)

应该引发错误的是:

'--arg=3'
'--arg=3 -a1'
'--arg=3 -b2'
Run Code Online (Sandbox Code Playgroud)


haa*_*vee 5

对于争论,我想出了一个像这样的快速解决方案。假设:(1)“--help”应该显示帮助而不是抱怨所需的参数(2)我们正在解析sys.argv

p = argparse.ArgumentParser(...)
p.add_argument('-required', ..., required = '--help' not in sys.argv )
Run Code Online (Sandbox Code Playgroud)

可以轻松修改它以匹配特定设置。对于所需的位置(如果例如在命令行上给出“--help”,则将不再需要)我提出了以下建议:[位置不允许使用关键字参数required=...!]

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

基本上,如果指定了“--help”,这会将命令行上所需的“模式”出现次数从一个或多个变为零个或多个。