Argparse中的异常

3

我有以下代码用于从文件中读取参数并使用argparse处理它们,但是我遇到了错误,请问为什么会出现这种情况?

import argparse
from ConfigParser import ConfigParser
import shlex

parser = argparse.ArgumentParser(description='Short sample app',
                                 fromfile_prefix_chars='@')

parser.add_argument('--abool', action="store_true", default=False)
parser.add_argument('--bunit', action="store", dest="bunit",type=int)
parser.add_argument('--cpath', action="store", dest="c", type=str)

print parser.parse_args(['@argparse_fromfile_prefix_chars.txt']) #name of the file is argparse_fromfile_prefix_chars.txt

错误:

usage: -c [-h] [--abool] [--bunit BUNIT] [--cpath C]
-c: error: unrecognized arguments: --bunit 289 --cpath /path/to/file.txt
To exit: use 'exit', 'quit', or Ctrl-D.

argparse_fromfile_prefix_chars.txt文件的内容

--abool
--bunit 289
--cpath /path/to/file.txt
2个回答

1

fromfile_prefix_chars 的文档说明:

默认情况下,从文件中读取的参数每行只能读取一个(但请参见convert_arg_line_to_args()),并被视为在命令行上引用原始文件参数的相同位置。

请注意,一个参数不意味着一个选项加上它的所有参数,而是指一个命令行参数。当前整个行都被解释为单个参数。

换句话说,您的文件应该像这样:

--abool
--bunit
289
--cpath
/path/to/file.txt

或者,您可以重写convert_arg_line_to_args()方法以其他方式解析文件。文档已经提供了一种实现,它解析空格分隔的参数而不是行分隔的参数:

def convert_arg_line_to_args(self, arg_line):
    # consider using shlex.split() instead of arg_line.split()
    for arg in arg_line.split():
        if not arg.strip():
            continue
        yield arg

我相信你可以通过子类化ArgumentParser并重新实现此方法,或者甚至在ArgumentParser实例上设置属性也可以起作用。
由于某种原因,convert_arg_line_to_args 的默认实现无法正常工作:
$echo '--abool           
--bunit
289
--cpath
/here/is/a/path
' > file.txt
$cat test_argparse.py 
import argparse

parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('--abool', action='store_true')
parser.add_argument('--bunit', type=int)
parser.add_argument('--cpath')


print(parser.parse_args(['@file.txt']))
$python test_argparse.py 
usage: test_argparse.py [-h] [--abool] [--bunit BUNIT] [--cpath CPATH]
test_argparse.py: error: unrecognized arguments:

然而,如果您使用上述实现方法,它会起作用:

$cat test_argparse.py    
import argparse

def convert_arg_line_to_args(arg_line):
    for arg in arg_line.split():
        if not arg.strip():
            continue
        yield arg.strip()

parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('--abool', action='store_true')
parser.add_argument('--bunit', type=int)
parser.add_argument('--cpath')
parser.convert_arg_line_to_args = convert_arg_line_to_args

print(parser.parse_args(['@file.txt']))
$python test_argparse.py 
Namespace(abool=True, bunit=289, cpath='/here/is/a/path')

另一个解决方法是使用--option=argument语法:
--abool
--bunit=289
--cpath=/the/path/to/file.txt

然而,当选项有多个参数时,这种方法将无法使用。在这种情况下,您必须使用不同的convert_arg_line_to_args实现。


尝试调试时,似乎会使用空字符串调用convert_line_arg_to_args,并将其添加到参数中,空字符串被视为参数(未定义)。
问题在于文件末尾有两个换行符。实际上,如果不在文件末尾添加双换行符,则可以正常运行:
$echo -n '--abool
--bunit
289
--cpath
/here/is/a/path
' > file.txt
$python test_argparse.py 
Namespace(abool=True, bunit=289, cpath='/here/is/a/path')

(echo -n 不会在输出的末尾添加换行符)。


@Lanc 由于某种原因,“convert_arg_line_to_args”的默认实现无法正常工作。如果您使用我回答中的示例实现,它将起作用。 - Bakuriu
@Lanc 注意,输出末尾有多个换行符可能会导致问题。请参考更新后的答案。 - Bakuriu

1

argparse 期望从文件中获取的参数每行一个。这意味着整行是一个引用的参数。因此,您当前的 args 文件将被解释为:

python a.py '--abool' '--bunit 289' '--cpath /path/to/file.txt'

导致错误的原因是这个。相反,您的参数文件应该像这样。
--abool
--bunit
289
--cpath
/path/to/file.txt

2
或者他的文件可能包含以下三行内容:--abool--bunit=289--cpath=/path/to/file.txt - Robᵩ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接