我想知道如何使用python的argparse模块从命令行和文本文件中读取参数。我知道argparse的fromfile_prefix_chars
,但这不是我想要的。我想要这个行为,但我不想要语法。我想要一个如下所示的界面:
$ python myprogram.py --foo 1 -A somefile.txt --bar 2
当argparse看到-A时,它应该停止从sys.argv或者我给它的任何内容读取,并调用我编写的函数来读取somefile.text并返回一个参数列表。当文件用尽时,它应该继续解析sys.argv或其他什么。重要的是文件中的参数处理顺序发生(即:应该处理-foo,然后处理文件中的参数,然后是-bar,以便文件中的参数可以覆盖--foo,和 - bar可能会覆盖文件中的内容)。
这样的事情可能吗?我可以编写一个将新参数推送到argparse的堆栈的自定义函数,或者那种效果吗?
答案 0 :(得分:22)
您可以使用打开文件的自定义argparse.Action
来解决此问题,解析文件内容,然后添加参数。
例如,这将是一个非常简单的操作:
class LoadFromFile (argparse.Action):
def __call__ (self, parser, namespace, values, option_string = None):
with values as f:
parser.parse_args(f.read().split(), namespace)
你可以这样使用:
parser = argparse.ArgumentParser()
# other arguments
parser.add_argument('--file', type=open, action=LoadFromFile)
args = parser.parse_args()
args
中生成的命名空间也将包含也从文件加载的任何配置。
如果您需要更复杂的解析,您还可以先分别解析文件内配置,然后有选择地选择应该接管哪些值。例如,禁止在配置文件中指定另一个文件是有意义的:
def __call__ (self, parser, namespace, values, option_string=None):
with values as f:
contents = f.read()
data = parser.parse_args(contents.split(), namespace=namespace)
for k, v in vars(data).items():
if v and k != option_string.lstrip('-'):
setattr(namespace, k, v)
当然,您也可以使文件读取更复杂,例如首先从JSON读取。
答案 1 :(得分:2)
你评论说
我需要能够编写自己的函数来读取该文件并返回参数(它不是以每个参数的每行格式) -
现有的prefix-file处理程序中有一项规定可以更改文件的读取方式。该文件由私人'方法,parser._read_args_from_files
,但它调用一个简单的公共方法,将一行转换为字符串,默认的一个参数每行操作:
def convert_arg_line_to_args(self, arg_line):
return [arg_line]
它是以这种方式编写的,因此您可以轻松自定义它。 https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.convert_arg_line_to_args
此方法的有用覆盖是将每个以空格分隔的单词视为参数:
def convert_arg_line_to_args(self, arg_line):
for arg in arg_line.split():
if not arg.strip():
continue
yield arg
在test_argparse.py
单元测试文件中,有一个测试用例。
但是如果您仍然希望使用参数选项而不是前缀字符来触发此读取,则自定义Action方法是一个很好的方法。
您可以编写自己的函数,在将argv
传递给parser
之前对其进行处理。它可以在parser._read_args_from_files
上建模。
所以你可以写一个像:
这样的函数def read_my_file(argv):
# if there is a '-A' string in argv, replace it, and the following filename
# with the contents of the file (as strings)
# you can adapt code from _read_args_from_files
new_argv = []
for a in argv:
....
# details left to user
return new_argv
然后使用:
调用您的解析器parser.parse_args(read_my_file(sys.argv[1:]))
是的,这可以包含在ArgumentParser
子类中。
答案 2 :(得分:1)
Action
在调用时会在其参数中获得parser
和namespace
。
因此,您可以将文件放在前者中以更新后者:
class ArgfileAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
extra_args = <parse_the_file>(values)
#`namespace' is updated in-place when specified
parser.parse_args(extra_args,namespace)