как получить argparse для чтения аргументов из файла с опцией, а не префиксом
Я хотел бы знать, как использовать модуль argparse python для чтения аргументов как из командной строки, так и, возможно, из текстовых файлов. Я знаю argparse по fromfile_prefix_chars
но это не совсем то, чего я хочу. Мне нужно поведение, но не синтаксис. Мне нужен интерфейс, который выглядит так:
$ python myprogram.py --foo 1 -A somefile.txt --bar 2
когда argparse видит -A, он должен прекратить чтение из sys.argv или что я ему даю, и вызываю функцию, которую я пишу, которая будет читать какой-то файл.текст и возврат списка аргументы. Когда файл исчерпан, он должен возобновить синтаксический анализ sys.аргв или как его там. Важно, чтобы обработка аргументов в файле происходила по порядку (т. е.: -foo должен быть обработан, затем аргументы в файле, затем-bar, так что аргументы в файле могут переопределить --foo, и --bar может переопределить то, что в файле).
возможно ли такое? Могу ли я написать пользовательскую функцию, которая помещает новые аргументы в стек argparse или что-то в этом роде эффект?
3 ответов
вы можете решить эту проблему с помощью настраиваемого 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
будет также содержать любую конфигурацию, которая также была загружена из файла.
Если вам нужен более сложный анализ, вы также можете разбирать конфигурация в файле сначала отдельно, а затем выборочно выберите, какие значения должны быть приняты. Например, может иметь смысл запретить указание другое в файле config:
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.
Вы сказали, что
мне нужно иметь возможность написать свою собственную функцию, чтобы прочитать этот файл и вернуть аргументы (это не в формате одного аргумента на строку) -
существует положение в существующем обработчике префикс-файла, чтобы изменить способ чтения файла. Файл считывается "частным" методом,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
unittesting file существует тестовый случай для этой альтернативы.
но если вы все еще хотите запустить это чтение с параметром аргумента вместо символа префикса, тогда подход пользовательского действия является хорошим.
вы могли бы написать свою собственную функцию, которая обрабатывает 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
подкласс.
An 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)