为什么Python argparse中的fromfile-prefix-chars不起作用?

我试图在
Python中使用argparse的fromfile-prefix-chars功能从文件加载我的所有命令行参数,但它一直在抱怨我没有指定一些参数.

代码:

import argparse

def go():
   parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
   parser.add_argument("--option1")
   parser.add_argument("--option2", type=int, required=True)
   args = parser.parse_args()

if __name__ == "__main__":
    go()

参数文件:

--option1 foo
--option2 1234

命令行和输出:

$python testargparse.py @testargs
usage: testargparse.py [-h] [--option1 OPTION1] --option2 OPTION2
testargparse.py: error: argument --option2 is required

你可以看到我在文件中提供了必需的参数,但是argparse没有看到它.

最佳答案 从文档:

Arguments read from a file must by default be one per line … and are treated as if they were in the same place as the original file referencing argument on the command line. So in the example above, the expression [‘-f’, ‘foo’, ‘@args.txt’] is considered equivalent to the expression [‘-f’, ‘foo’, ‘-f’, ‘bar’].

在示例中:

fp.write('-f\nbar')

所以该文件包含:

-f
bar

换句话说,每个文件行对应于命令行中的一个“字”(空白分隔). –option1 = foo是一个单词. –option1 foo被解释为就像在命令行中引用一样,例如. prog.py’ – option1 foo” – option2 1234′

https://docs.python.org/dev/library/argparse.html#argparse.ArgumentParser.convert_arg_line_to_args有一个自定义功能,可以在空格上分割线条.如果你想坚持参数文件,试验一下.

import argparse

with open('args.txt', 'w') as fp:
    fp.write('--option1 foo\n--option2 1234')  # error
    # but works with modifed 'convert...'
    #fp.write('--option1=foo\n--option2=1234')  # works
    #fp.write('--option1\nfoo\n--option2\n1234') # works

def convert_arg_line_to_args(arg_line):
    for arg in arg_line.split():
        if not arg.strip():
            continue
        yield arg
"""
default line converter:
def convert_arg_line_to_args(self, arg_line):
    return [arg_line]
"""

def go():
   parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
   parser.convert_arg_line_to_args = convert_arg_line_to_args
   parser.add_argument("--option1")
   parser.add_argument("--option2", type=int, required=True)
   args = parser.parse_args(['@args.txt'])
   print args

if __name__ == "__main__":
    go()

@toes建议使用shlex来解析文件. shlex有一个很好的功能,它会删除不必要的引号.

shlex可用于拆分文件的各行.

def sh_split(arg_line):
    for arg in shlex.split(arg_line):
        yield arg
parser.convert_arg_line_to_args = sh_split

或者它可以替换整个@file读取方法(_read_args_from_files) – 这应该与@toes应答相同,除了@file字符串可以在命令行中的任何位置(甚至可以重复).

def at_read_fn(arg_strings):
    # expand arguments referencing files
    new_arg_strings = []
    for arg_string in arg_strings:
        if not arg_string or not arg_string.startswith('@'):
            new_arg_strings.append(arg_string)
        else:
            with open(arg_string[1:]) as args_file:
                arg_strings = shlex.split(args_file.read())
                new_arg_strings.extend(arg_strings)
    return new_arg_strings
parser._read_args_from_files = at_read_fn

显然,更清洁的生产版本会在ArgumentParser子类中修改这些方法.

点赞