как заставить argparse читать аргументы из файла с опцией, а не префиксом

Я хотел бы знать, как использовать модуль argparse python для чтения аргументов как из командной строки, так и, возможно, из текстовых файлов. Я знаю аргумент 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 или что-то в этом роде?

3 Solutions collect form web for “как заставить argparse читать аргументы из файла с опцией, а не префиксом”

Вы можете решить это, используя пользовательский 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.

Вы прокомментировали это

Мне нужно написать собственную функцию, чтобы прочитать этот файл и вернуть аргументы (это не в формате с одним аргументом в строке) –

В существующем обработчике префикс-файла есть положение, чтобы изменить способ чтения файла. Файл читается методом «private», 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 есть тестовый пример для этой альтернативы.


Но если вы все еще хотите вызвать это чтение с параметром аргумента, вместо символа префикса, то пользовательский подход к действию является хорошим.

Вы могли бы написать свою собственную функцию, которая обрабатывает 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 .

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) 
Python - лучший язык программирования в мире.