Как остановить argparse.FileType, создающий файл, указанный по умолчанию

Мне нравится модуль argparse. argparse.FileType также полезен, если вы не хотите, чтобы по умолчанию было что-то отличное от sys.std*, поскольку выходной файл по умолчанию создается, даже если вы указываете значение.

Например:

parser.add_argument('--outfile', type=FileType('w'), default="out.txt")

создаст out.txt, даже если вы укажете файл с параметром --outfile.

Лучшее, что я могу придумать, это:

class MagicFileType(object):

    def __init__(self, *args, **kwargs):

        # save args/kwargs and set filetype to None
        self.filetype = None
        self.args = args
        self.kwargs = kwargs

    def __getattr__(self, attr):
        """ Delegate everything to the filetype """

        # If we haven't created it, now is the time to do so
        if self.filetype is None:
            self.filetype = FileType(*self.args, **self.kwargs)
            self.filetype = self.filetype(self.filename)

        return getattr(self.filetype, attr)

    def __call__(self, filename):
        """ Just cache the filename """

        # This is called when the default is created
        # Just cache the filename for now.
        self.filename = filename
        return self

Но если кажется, что это должно быть проще, я что-то упускаю?


person John Gill    schedule 27.09.2013    source источник
comment
Хотя мне очень нравится ваш вопрос и ваше решение, я не могу это воспроизвести. Я сделал небольшой пример и, когда я указываю --outfile, я получаю только тот файл, который я создал, а не оба. Какая у вас версия питона? Возможно, это ошибка в модуле argparse для определенной версии. я на 2.7.5   -  person Brian    schedule 27.09.2013
comment
Это интересно, @Brian . Эта программа утверждает мою установку 2.7.3 и мою версию 3.2.3.   -  person Robᵩ    schedule 27.09.2013
comment
comment
@Rob Эта ссылка сосредоточена на случае с файлом input, и решения касаются этого случая, но не случая выходного файла.   -  person John Gill    schedule 27.09.2013
comment
@ Брайан, я использую 2.7.3   -  person John Gill    schedule 27.09.2013


Ответы (1)


Относительно недавно произошли изменения в argparse, http://bugs.python.org/issue12776 (август 2012 г. ), что задерживает оценку значения по умолчанию. Первоначально строка по умолчанию будет передаваться через type (через _get_value) в начале синтаксического анализа, что приводит к открытию (и созданию) файла FileType (независимо от того, нужен он или нет). В этом патче строка записывается в пространство имен, но не оценивается до конца синтаксического анализа, когда она может определить, было ли предоставлено другое значение или нет. По сути, эта строка была перенесена с начала parse_known_args на конец _parse_known_args.

default = self._get_value(action, action.default)

В http://bugs.python.org/issue13824 я предложил патч, обеспечивающий тип FileContext. Его главное отличие от FileType заключается в том, что он заключает open(file...) в partial. Таким образом, файл не открывается (или не создается) до тех пор, пока он не будет использован в контексте with args.output() as f:.

Этот патч имеет дело с некоторыми другими вещами, такими как проверка того, может ли файл быть создан или нет (с использованием os.access) и обертывание stdin/out фиктивным контекстом, чтобы он не пытался закрыть его.

Без тестирования вы можете изменить FileType следующим образом:

class FileOpener(argparse.FileType):
    # delayed FileType;
    # sample use:
    # with args.input.open() as f: f.read()
    def __call__(self, string):
        # optionally test string
        self.filename = string
        return self
    def open(self):
        return super(FileOpener,self).__call__(self.filename)
    file =  property(open, None, None, 'open file property')
person hpaulj    schedule 27.09.2013