我们正在尝试使用我们正在使用的命令行工具构建包装器脚本。我们想根据包装器脚本中的选项设置一些工具参数。我们还希望能够直接将命令行工具传递给命令行工具。
以下是我们提出的建议:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional')
parser.add_argument('-f', '--foo', action='store_true')
parser.add_argument('-b', '--bar', action='store_true')
parser.add_argument('native_arg', nargs='*')
args = parser.parse_args()
print (args)
positional
是强制性的。根据选项-f
和-b
,我们会为工具调用添加一些额外选项。之后留下的任何内容(如果有的话)应该被视为本机工具参数并直接提供给工具。使用-h
调用我们的脚本会产生以下用法:
usage: test.py [-h] [-f] [-b] positional [native_arg [native_arg ...]]
诀窍是这些原生参数本身就是工具的选项,并包含前导短划线,例如-native0
和-native1
。我们已经知道使用双短划线阻止 argparse 寻找更多选项的技巧。以下电话:
./test.py pos -- -native0 -native1
生成预期的解析参数:
Namespace(bar=False, foo=False, native_arg=['-native0', '-native1'], positional='pos')
但是,在第一个位置参数之后尝试添加选项并不起作用。更具体地说,以下呼叫:
./test.py pos --foo -- -native0 -native1
产生以下输出:
usage: [...shortened...]
test.py: error: unrecognized arguments: -- -native0 -native1
将可选参数放在位置之前:
./test.py --foo pos -- -native0 -native1
似乎有效,打印如下:
Namespace(bar=False, foo=True, native_arg=['-native0', '-native1'], positional='pos')
更奇怪的是,将nargs
的{{1}}的值更改为native_arg
适用于上述所有情况(当然需要注意的是,至少有一个'+'
预计)。
我们在Python代码中做错了什么,或者这是某种 argparse 错误?
答案 0 :(得分:3)
argparse
确实很难(有关错误报告的详细信息,请参阅https://stackoverflow.com/a/47208725/1399279)。我不打算提出解决这个问题的方法,而是提出另一种方法。
您应该查看为您描述的情况创建的parse_known_args
方法(即将选项传递给包装工具)。
In [1]: import argparse
In [2]: parser = argparse.ArgumentParser()
In [3]: parser.add_argument('positional')
In [4]: parser.add_argument('-f', '--foo', action='store_true')
In [5]: parser.add_argument('-b', '--bar', action='store_true')
In [6]: parser.parse_known_args(['pos', '--foo', '-native0', '-native1'])
Out[6]: (Namespace(bar=False, foo=True, positional='pos'), ['-native0', '-native1'])
与parse_args
不同,parse_known_args
的输出是一个双元素元组。第一个元素是您希望从Namespace
获得的parse_args
实例,它包含调用add_argument
定义的所有属性。第二个元素是解析器未知的所有参数的列表。
我个人更喜欢这种方法,因为用户不需要记住有关如何调用程序的任何技巧,或者哪个选项顺序不会导致错误。
答案 1 :(得分:2)
这是一个已知问题(https://bugs.python.org/issue15112, argparse:nargs =&#39; *&#39;如果前面有选项和其他位置,则位置参数不接受任何项目< /强>)
解析交替处理位置和选项。在处理位置时,它会尝试处理输入字符串所需的数量。但?
或*
位置符合[]
,这是一个空字符串列表。另一方面,+
至少需要一个字符串
./test.py pos --foo -- -native0 -native1
解析器给出了&#39; pos&#39;到positional
,[]
到native-arg
。然后它给出了&#39; - foo&#39;到它的可选。暂时没有positionals
剩下的字符串,所以它会引发错误。
输入字符串的分配是使用regex
字符串匹配的程式化形式完成的。想象一下,匹配看起来像AA?
的模式。
要纠正这个问题,解析器必须向前看,并延迟处理native-arg
。我们已经建议补丁,但它们还没有投入生产。
@ SethMMorton建议使用parse_known_args
是一个很好的建议。
早期的解析器(例如Optparse)处理所有标记的参数,但将其余的位置作为无差别列表返回。用户可以拆分该列表。 argparse
添加了命名和解析positionals
的功能,但该算法在修复nargs
时效果最佳,并且变量太多nargs
。