我正在编写一个工具,它将参数传递给另一个提供如下参数的命令:
foo --arg1 -b -c bar -v --number=42
在这个例子中,
foo
是我的工具,--arg1 -b -c
应该是由foo
解析的参数,而-v --number=42
是去往bar
的参数,这是由foo
调用的命令。
因此,这与
strace
非常相似,您可以向 strace
提供参数,同时仍然提供带有自定义参数的命令。
argparse
提供了parse_known_arguments()
,但它会解析它知道的所有参数,甚至是那些在bar
之后的参数。
一些工具使用特殊的语法元素(例如
--
)来分隔具有不同语义的参数,但因为我知道 foo
只会处理名称参数,所以我想避免这种情况。
手动找到您可能想到的第一个参数并不太难,这就是我目前正在做的事情:
parser.add_argument("--verbose", "-v", action="store_true")
all_args = args or sys.argv[1:]
with suppress(StopIteration):
split_at = next(i for i, e in enumerate(all_args) if not e.startswith("-" ))
return parser.parse_args(all_args[:split_at]), all_args[split_at:]
raise RuntimeError("No command provided")
这适用于我提供的示例。但是使用
argparse
,您可以指定带有值的参数,这些值可以但不必提供 =
:
foo --file=data1 --file data2 bar -v --number=42
因此,在这里手动识别
data2
作为第二个 --file
参数的值,以及 bar
作为第一个位置参数会更加困难。
我当前的方法是手动拆分参数(向后)并查看所有“左手”参数是否成功解析:
def parse_args():
class MyArgumentParser(ArgumentParser):
def error(self, message: str) -> NoReturn:
raise RuntimeError()
parser = MyArgumentParser()
parser.add_argument("--verbose", "-v", action="store_true")
parser.add_argument("--with-value", type=str)
all_args = sys.argv[1:]
for split_at in (i for i, e in reversed(list(enumerate(all_args))) if not e.startswith("-")):
with suppress(RuntimeError):
return parser.parse_args(all_args[:split_at]), all_args[split_at:]
parser.print_help(sys.stderr)
print("No command provided", file=sys.stderr)
raise SystemExit(-1)
这对我有用,但除了能够手动处理解析器错误所需的笨拙的额外
MyArgumentParser
之外,我现在需要手动对错误进行分类,因为ArgumentError
变成了自然发生的事情。
那么有没有办法告诉
argparse
仅解析到第一个位置参数,然后停止,即使在该参数之后还有它知道的参数?
定义解析器:
In [3]: parser = argparse.ArgumentParser()
...: parser.add_argument('--file', action='append')
...: parser.add_argument('-f')
...: parser.add_argument('rest', nargs=argparse.PARSER);
唯一特别的是这个'rest'带有一个未记录的
nargs
(与subparsers
使用的相同。它就像'+',但是除了第一个之外的值可以看起来像标志。
In [4]: parser.print_help()
usage: ipykernel_launcher.py [-h] [--file FILE] [-f F] rest ...
positional arguments:
rest
options:
-h, --help show this help message and exit
--file FILE
-f F
并测试您的输入(我没有考虑
foo
论点):
In [5]: args = parser.parse_args('--file=data1 --file data2 -f1 bar -v --number=42 --file=data3'.split())
In [6]: args
Out[6]: Namespace(file=['data1', 'data2'], f='1', rest=['bar', '-v', '--number=42', '--file=data3'])
省略位置,我们会得到一个错误:
In [7]: args = parser.parse_args('--file=data1 --file data2 -f1 -v --number=42 --file=data3'.split())
usage: ipykernel_launcher.py [-h] [--file FILE] [-f F] rest ...
ipykernel_launcher.py: error: the following arguments are required: rest