Как передать аргументы командной строки, содержащиеся в файле, и сохранить имя этого файла? - PullRequest
5 голосов
/ 28 июня 2019

У меня есть скрипт, который использует аргументы командной строки, и я хотел бы реализовать две схемы передачи аргументов, а именно:

  • Ввод аргументов в командной строке.
  • Сохранение списка аргументов в файле и передача имени этого файла программе через командную строку.

Для этого я передаю аргумент fromfile_prefix_chars конструктору ArgumentParser.

script.py

from argparse import ArgumentParser
parser = ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)

args.txt

--foo
2
--bar
2

Примеры использования

$ python script.py --foo 3
Namespace(bar=1, filename=None, foo='3')
$ python script.py @args.txt --foo 3
Namespace(bar='2', filename=None, foo='3')

Я ожидал, что args.filename сохранит имя файла, но, что удивительно, вместо него будет значение None. Мне известно, что я могу получить имя файла из sys.argv путем небольшой обработки. Есть ли более чистый способ (в идеале argparse) для получения имени файла аргументов?

Ответы [ 3 ]

1 голос
/ 29 июня 2019

Ваш script.py плюс файл.Я добавил имя файла в сам файл.

args.txt

args.txt
--foo
2
--bar
2

тестирование:

1803:~/mypy$ python3 stack56811067.py --foo 3
Namespace(bar=1, filename=None, foo='3')
1553:~/mypy$ python3 stack56811067.py @args.txt --foo 3
Namespace(bar='2', filename='args.txt', foo='3')
1 голос
/ 28 июня 2019

Из моего тестирования использование fromfile_prefix_chars означает, что argparse фактически не передаст аргумент вашей программе.Вместо этого argparse видит @args.txt, перехватывает его, читает из него и передает аргументы без @args.txt в вашу программу.Вероятно, это потому, что большинству людей на самом деле не нужно имя файла, просто нужны аргументы внутри, поэтому argparse избавляет вас от необходимости создавать другой аргумент для хранения чего-то, что вам не нужно.

К сожалению, всеаргументы хранятся как локальные переменные в argparse.py, поэтому мы не можем получить к ним доступ.Я полагаю, что вы можете переопределить некоторые функции argparse.Имейте в виду, что это ужасное, отвратительное, хакерское решение, и я чувствую, что разбор sys.argv на 100% лучше.

from argparse import ArgumentParser

# Most of the following is copied from argparse.py
def customReadArgs(self, arg_strings):
    # expand arguments referencing files
    new_arg_strings = []
    for arg_string in arg_strings:

        # for regular arguments, just add them back into the list
        if not arg_string or arg_string[0] not in self.fromfile_prefix_chars:
            new_arg_strings.append(arg_string)

        # replace arguments referencing files with the file content
        else:
            try:
                fn = arg_string[1:]
                with open(fn) as args_file:

                    # What was changed: before was []
                    arg_strings = [fn]

                    for arg_line in args_file.read().splitlines():
                        for arg in self.convert_arg_line_to_args(arg_line):
                            arg_strings.append(arg)
                    arg_strings = self._read_args_from_files(arg_strings)
                    new_arg_strings.extend(arg_strings)
            except OSError:
                err = _sys.exc_info()[1]
                self.error(str(err))

    # return the modified argument list
    return new_arg_strings
ArgumentParser._read_args_from_files = customReadArgs
parser = ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)
0 голосов
/ 01 июля 2019

Просто для справки, вот быстрое и грязное решение, которое я придумала.В основном он состоит в создании копии parser и установке атрибута from_file_prefix_chars на None:

import copy

parser_dupe = copy.copy(parser)
parser_dupe.fromfile_prefix_chars = None
args_raw = parser_dupe.parse_args()
if args_raw.filename:
    args.filename = args_raw.filename[1:]
...