使用 argparse 处理子进程的参数:"Expected one argument"

Processing arguments for subprocesses using argparse: "Expected one argument"

首先,我很抱歉标题没有完全说明我的问题是什么,我有点挣扎,所以我将用一个可重现的例子更清楚地说明它:

我有以下代码:

example.py:

import argparse


def parse_args():
    """Command line argument parser

    Returns:
        The extra arguments
    """

    parser = argparse.ArgumentParser(description="Arguments for test runner")
    parser.add_argument('-e', '--extra_args', type=str, default="", help="Extra arguments. Enter as a single line encapsulated by quotes. Example: -e \"--repeat=0 otherstuff -x anotherpositional\"")

    arguments = parser.parse_args()

    return arguments


if __name__ == "__main__":
    args = parse_args()
    print(args.extra_args)

然后 --extra_args 参数将用作子进程调用的位置参数。

我需要传递的参数如下:--repeat=0。它不起作用,我得到以下结果:

python example.py -e "toto" # This print toto
python example.py -e "--repeat = 0" # This print --repeat = 0
python example.py -e "--repeat" # error: argument -e/--extra_args: expected one argument
python example.py -e "--repeat=0" # error: argument -e/--extra_args: expected one argument

我的理解是解析器将 --repeat 作为参数处理,找不到它的值,然后中断。但不幸的是,我别无选择,只能写“--repeat=0”,因为软件接收它。

您知道解决方法吗?

我找到了一个丑陋的解决方法,但如果它退出,我会喜欢更好的解决方案。

我添加了以下功能:

def __workaround_for_extra_utr_args():
    """This workaround is necessary because utr can receives args at \"--repeat=0\" which are not processable by arg parse, even as a string"""
    import sys

    index_of_extra_args = [sys.argv.index(v) for v in ['-e', '--extra_utr_args'] if v in sys.argv]
    space = " "

    if index_of_extra_args:
        index_of_extra_args = index_of_extra_args[0]

        if space not in sys.argv[index_of_extra_args + 1]:
            sys.argv[index_of_extra_args + 1] = sys.argv[index_of_extra_args + 1] + space

在调用解析器之前调用它,就可以return这个字符串,然后可以在解析后去掉多余的space。

parse_args 解释 arg 字符串开头的双破折号的方式似乎有问题。

如果您添加前缀,它可以是单个字符,例如字符串中的 space,这可能是一种解决方法。

python example.py -e " --repeat=0" # OK
python example.py -e ":--repeat=0" # OK
python example.py -e " --repeat=0 --foo=1 --boo=2" # OK

然后在您的代码中去除第一个字符或任何前缀的 extra_args 会很容易。

import argparse


def parse_args():
    """Command line argument parser

    Returns:
        A tuple containing the toolset version and a force flag
    """

    parser = argparse.ArgumentParser(description="Arguments for test runner")
    parser.add_argument('-e', '--extra_args', type=str, default="", help="Extra arguments. Enter as a single line encapsulated by quotes. Example: -e \"--repeat=0 otherstuff -x anotherpositional\"")

    arguments = parser.parse_args()
    arguments.extra_args = arguments.extra_args[1:]

    return arguments


if __name__ == "__main__":
    args = parse_args()
    print(args.extra_args)

不理想,但一个简单的解决方法。

处理以“--”开头的值是 argparse 的一个挑战。它标记一个标志(与您的 --extra_args 一样)的想法是 argparse 不可或缺的一部分,因此很难解决它。

简单地添加引号没有帮助。它们控制 shell 处理字符串的方式,但不会改变 sys.argv.

中的内容

https://docs.python.org/3/library/argparse.html#arguments-containing

描述了 pseudo-argument“--”的用法,但这只适用于位置参数。 (我不得不再次查看代码以了解为什么它对可选值没有任何作用。)

因此:

parser = argparse.ArgumentParser(description="Arguments for test runner")
parser.add_argument('extra_args', nargs='*')
arguments = parser.parse_known_args()
print(arguments)

我得到:

919:~/mypy$ python3 stack72129874.py -- --repeat
argv ['stack72129874.py', '--', '--repeat']
(Namespace(extra_args=['--repeat']), [])

0919:~/mypy$ python3 stack72129874.py -- --repeat=0
argv ['stack72129874.py', '--', '--repeat=0']
(Namespace(extra_args=['--repeat=0']), [])

0920:~/mypy$ python3 stack72129874.py -- --repeat = 0
argv ['stack72129874.py', '--', '--repeat', '=', '0']
(Namespace(extra_args=['--repeat', '=', '0']), [])

0920:~/mypy$ python3 stack72129874.py -- "--repeat = 0"
argv ['stack72129874.py', '--', '--repeat = 0']
(Namespace(extra_args=['--repeat = 0']), [])

有一个 nargs=argparse.REMAINDER 与 pseudo-argument 类似,但同样只适用于位置。而且在使用上有一些问题,所以现在没有记录。

添加空格,例如“--repeat = 0”,在之前关于该主题的 SO 中已经提到。

如果您真的不需要 argparse 机制的其余部分(帮助、错误处理、其他参数),那么您自己解析 sys.argv 是一个完美的解决方案。