使用 argparse 是否可以仅处理最后一个非位置参数?

问题描述 投票:0回答:1

我正在编写一个工具,它将参数传递给另一个提供如下参数的命令:

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
仅解析到第一个位置参数,然后停止,即使在该参数之后还有它知道的参数?

python command-line-arguments argparse positional-argument
1个回答
0
投票

定义解析器:

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
© www.soinside.com 2019 - 2024. All rights reserved.