为什么 argparse 不接受“--”作为参数?

Mar*_*ter 22 python argparse

我的脚本采用-d,--delimiter作为参数:

parser.add_argument('-d', '--delimiter')
Run Code Online (Sandbox Code Playgroud)

但是当我将它--作为分隔符传递时,它是空的

script.py --delimiter='--' 
Run Code Online (Sandbox Code Playgroud)

我知道--参数/参数解析很特殊,但我在表单中使用它--option='--'并引用它。

为什么它不起作用?我正在使用Python 3.7.3

这是测试代码:

#!/bin/python3

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--delimiter')
parser.add_argument('pattern')

args = parser.parse_args()

print(args.delimiter)
Run Code Online (Sandbox Code Playgroud)

当我运行它时,script --delimiter=-- AAA它打印为空args.delimiter

use*_*ica 19

这看起来像一个错误。你应该报告一下。

中的代码argparse.py是 的开始_get_values,它是用于解析值的主要辅助函数之一:

if action.nargs not in [PARSER, REMAINDER]:
    try:
        arg_strings.remove('--')
    except ValueError:
        pass
Run Code Online (Sandbox Code Playgroud)

该代码接收--参数作为列表的单个元素['--']'--'它尝试从列表中删除,因为当用作--选项结束标记时,该'--'字符串将最终出现在arg_strings其中一个_get_values调用中。然而,当'--'实际参数值是什么时候,代码仍然会删除它,所以arg_strings最终会成为一个空列表而不是单元素列表。

然后,代码通过 else-if 链来处理不同类型的参数(此处省略分支主体以节省空间):

# optional argument produces a default when not present
if not arg_strings and action.nargs == OPTIONAL:
    ...
# when nargs='*' on a positional, if there were no command-line
# args, use the default if it is anything other than None
elif (not arg_strings and action.nargs == ZERO_OR_MORE and
      not action.option_strings):
    ...
# single argument or optional argument produces a single value
elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
    ...
# REMAINDER arguments convert all values, checking none
elif action.nargs == REMAINDER:
    ...
# PARSER arguments convert all values, but check only the first
elif action.nargs == PARSER:
    ...
# SUPPRESS argument does not put anything in the namespace
elif action.nargs == SUPPRESS:
    ...
# all other types of nargs produce a list
else:
    ...
Run Code Online (Sandbox Code Playgroud)

这段代码应该经过第三个分支,

# single argument or optional argument produces a single value
elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
Run Code Online (Sandbox Code Playgroud)

但因为 中缺少参数arg_stringslen(arg_strings)所以为 0。它反而达到了最终的情况,该情况应该处理完全不同类型的参数。该分支最终返回一个空列表,而不是'--'应该返回的字符串,这就是为什么args.delimiter最终是一个空列表而不是'--'字符串。


这个错误也体现在位置参数上。例如,

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('a')
parser.add_argument('b')

args = parser.parse_args(["--", "--", "--"])

print(args)
Run Code Online (Sandbox Code Playgroud)

印刷

Namespace(a='--', b=[])
Run Code Online (Sandbox Code Playgroud)

因为当_get_values处理b参数时,它接收['--']asarg_strings并删除'--'. 在处理a参数时,它接收['--', '--'],表示一个选项结束标记和一个实际--参数值,并且它成功删除了选项结束标记,但在处理 时b,它删除了实际参数值。

  • 这是几年前报道的,但没有修复,https://bugs.python.org/issue14364 (17认同)

hpa*_*ulj 8

现有错误报告

已建议修补程序,但尚未应用。Argparse 错误地将“--”处理为选项的参数

一些简单的例子:

In [1]: import argparse
In [2]: p = argparse.ArgumentParser()
In [3]: a = p.add_argument('--foo')
In [4]: p.parse_args(['--foo=123'])
Out[4]: Namespace(foo='123')
Run Code Online (Sandbox Code Playgroud)

意想不到的情况:

In [5]: p.parse_args(['--foo=--'])
Out[5]: Namespace(foo=[])
Run Code Online (Sandbox Code Playgroud)

完全引用通过 - 但我不会讨论如何通过 shell 调用实现这一点:

In [6]: p.parse_args(['--foo="--"'])
Out[6]: Namespace(foo='"--"')
Run Code Online (Sandbox Code Playgroud)

'--' 作为单独的字符串:

In [7]: p.parse_args(['--foo','--'])
usage: ipython3 [-h] [--foo FOO]
ipython3: error: argument --foo: expected one argument
...
Run Code Online (Sandbox Code Playgroud)

双引号的另一个例子:

In [8]: p.parse_args(['--foo','"--"'])
Out[8]: Namespace(foo='"--"')
Run Code Online (Sandbox Code Playgroud)

在 中_parse_known_args,输入被扫描并分类为“O”或“A”。'--' 被处理为

        # all args after -- are non-options
        if arg_string == '--':
            arg_string_pattern_parts.append('-')
            for arg_string in arg_strings_iter:
                arg_string_pattern_parts.append('A')
Run Code Online (Sandbox Code Playgroud)

我认为“--”之后被删除了,但我还没有找到这部分代码。我也没有发现 '--foo=...' 版本是否被处理。

我隐约记得在处理多​​次出现的“--”时存在一些错误/问题。迁移到 github 后,我不再argparse像以前那样关注开发情况了。

编辑

get_values以。。开始:

def _get_values(self, action, arg_strings):
    # for everything but PARSER, REMAINDER args, strip out first '--'
    if action.nargs not in [PARSER, REMAINDER]:
        try:
            arg_strings.remove('--')
        except ValueError:
            pass
Run Code Online (Sandbox Code Playgroud)

为什么会导致空列表需要更多的思考和测试。

'=' 在 中处理_parse_optional,在第一次扫描期间使用:

    # if the option string before the "=" is present, return the action
    if '=' in arg_string:
        option_string, explicit_arg = arg_string.split('=', 1)
        if option_string in self._option_string_actions:
            action = self._option_string_actions[option_string]
            return action, option_string, explicit_arg
Run Code Online (Sandbox Code Playgroud)

老错误问题

argparse 不正确地处理 args 中的多个“--”

argparse:允许使用 -- 打破 nargs 并进入子解析器