我做了尽可能多的研究,但我没有找到最好的方法,只在某些条件下才能使某些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包中本地执行此操作?
答案 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
。已经过测试,似乎有效并保证-b
和points
之间具有独立的行为。
答案 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
} --a
到True
的标记。处理完所有选项后,argparse
将验证是否已设置标记为必需的任何选项。
答案 2 :(得分:3)
您的帖子解析测试很好,特别是如果使用is None
测试默认值符合您的需求。
http://bugs.python.org/issue11588 'Add "necessarily inclusive" groups to argparse'
使用groups
机制(mutuall_exclusive_groups的推广)来实现这样的测试。
我编写了一组UsageGroups
来实现xor
(互斥),and
,or
和not
等测试。我认为那些全面,但我无法用这些操作表达你的情况。 (看起来我需要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'已指定。