Подтвердить что ты не робот

Указание имен файлов по умолчанию с помощью argparse, но не открытие их на --help?

Скажем, у меня есть script, который выполняет некоторую работу над файлом. Он принимает это имя файла в командной строке, но если он не указан, по умолчанию используется известное имя файла (content.txt, скажем). С python argparse я использую следующее:

parser = argparse.ArgumentParser(description='my illustrative example')
parser.add_argument('--content', metavar='file', 
                     default='content.txt', type=argparse.FileType('r'),
                     help='file to process (defaults to content.txt)')
args = parser.parse_args()
# do some work on args.content, which is a file-like object

Это отлично работает. Единственная проблема заключается в том, что если я запустил python myscript --help, я получаю ArgumentError, если файл отсутствует (что, я думаю, имеет смысл), и текст справки не отображается. Я бы предпочел, чтобы он не пытался открыть файл, если пользователь просто хочет --help. Есть какой-либо способ сделать это? Я знаю, что могу сделать аргумент строкой и позаботиться о том, чтобы открыть файл позже (и я это делал), но было бы удобно, если argparse позаботится об этом.

4b9b3361

Ответ 1

Посмотрев на код argparse, я вижу:

  • ArgumentParser.parse_args вызывает parse_known_args и гарантирует, что не будет проанализирован ожидающий аргумент.
  • ArgumentParser.parse_known_args устанавливает значения по умолчанию и вызовы ArgumentParser._parse_known_args

Следовательно, обходным путем было бы использовать ArgumentParser._parse_known_args непосредственно для обнаружения -h и после этого использовать ArgumentParser.parse_args, как обычно.

import sys, argparse
parser = argparse.ArgumentParser(description='my illustrative example', argument_default=argparse.SUPPRESS)
parser.add_argument('--content', metavar='file',
                     default='content.txt', type=argparse.FileType('r'),
                     help='file to process (defaults to content.txt)')
parser._parse_known_args(sys.argv[1:], argparse.Namespace())
args = parser.parse_args()

Обратите внимание, что ArgumentParser._parse_known_args требуется пара параметров: аргументы из командной строки и пространства имен.

Конечно, я бы не рекомендовал этот подход, так как он использует внутреннюю реализацию argparse и может измениться в будущем. Тем не менее, я не считаю это слишком грязным, поэтому вы все равно можете использовать его, если считаете, что риски обслуживания окупаются.

Ответ 2

Вы можете подклассом argparse.FileType:

import argparse
import warnings

class ForgivingFileType(argparse.FileType):
    def __call__(self, string):
        try:
            super(ForgivingFileType,self).__call__(string)
        except IOError as err:
            warnings.warn(err)

parser = argparse.ArgumentParser(description='my illustrative example')
parser.add_argument('--content', metavar='file', 
                     default='content.txt', type=ForgivingFileType('r'),
                     help='file to process (defaults to content.txt)')
args = parser.parse_args()

Это работает без необходимости касаться частных методов, таких как ArgumentParser._parse_known_args.

Ответ 3

Использовать stdin по умолчанию:

parser.add_argument('file', default='-', nargs='?', type=argparse.FileType('r'))

Ответ 4

Возможно, вы могли бы определить свой собственный type или action в вызове add_argument, который проверяет, существует ли файл, и возвращает дескриптор файла, если он есть, и None (или что-то еще) в противном случае.

Это потребовало бы, чтобы вы тоже писали код самостоятельно, но если значение по умолчанию не всегда может быть использовано, вам, вероятно, придется рано или поздно выполнять некоторые проверки. Как утверждает Мэнни Д, вы можете пересмотреть свое значение по умолчанию.