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

Файл как аргумент командной строки для argparse - сообщение об ошибке, если аргумент недействителен

В настоящее время я использую argparse следующим образом:

import argparse
from argparse import ArgumentParser

parser = ArgumentParser(description="ikjMatrix multiplication")
parser.add_argument("-i", dest="filename", required=True,
    help="input file with two matrices", metavar="FILE")
args = parser.parse_args()

A, B = read(args.filename)
C = ikjMatrixProduct(A, B)
printMatrix(C)

Теперь я хотел бы отметить, что аргумент -i должен быть читаемым. Как я могу это сделать?

Я пробовал добавить type=open, type=argparse.FileType('r'), и они работали, но если файл недействителен, я хотел бы получить сообщение об ошибке. Как я могу это сделать?

4b9b3361

Ответ 1

Это довольно легко. Вам просто нужно написать функцию, которая проверяет, действителен ли файл и записывает ошибку в противном случае. Используйте эту функцию с опцией type. Обратите внимание, что вы можете получить больше фантазий и создать настраиваемое действие путем подкласса argparse.Action, но я не думаю, что это необходимо здесь. В моем примере я возвращаю дескриптор открытого файла (см. Ниже):

#!/usr/bin/env python

from argparse import ArgumentParser
import os.path


def is_valid_file(parser, arg):
    if not os.path.exists(arg):
        parser.error("The file %s does not exist!" % arg)
    else:
        return open(arg, 'r')  # return an open file handle


parser = ArgumentParser(description="ikjMatrix multiplication")
parser.add_argument("-i", dest="filename", required=True,
                    help="input file with two matrices", metavar="FILE",
                    type=lambda x: is_valid_file(parser, x))
args = parser.parse_args()

A, B = read(args.filename)
C = ikjMatrixProduct(A, B)
printMatrix(C)

Ответ 2

Я только что нашел это:

def extant_file(x):
    """
    'Type' for argparse - checks that file exists but does not open.
    """
    if not os.path.exists(x):
        # Argparse uses the ArgumentTypeError to give a rejection message like:
        # error: argument input: x does not exist
        raise argparse.ArgumentTypeError("{0} does not exist".format(x))
    return x

if __name__ == "__main__":
    import argparse, sys, os
    from argparse import ArgumentParser

    parser = ArgumentParser(description="ikjMatrix multiplication")
    parser.add_argument("-i", "--input",
        dest="filename", required=True, type=extant_file,
        help="input file with two matrices", metavar="FILE")
    args = parser.parse_args()

    A, B = read(args.filename)
    C = ikjMatrixProduct(A, B)
    printMatrix(C, args.output)

Источник: fhcrc.github.com

Ответ 3

В Python 3.4 можно использовать класс argparse.FileType. Обязательно закройте входной поток, когда закончите. Это также полезно, потому что вы можете использовать псевдо-аргумент '-' для STDIN/STDOUT. Из документации:

Объекты FileType понимают псевдо-аргумент '-' и автоматически преобразуют его в sys.stdin для читаемых FileType объектов и sys.stdout для записываемых FileType объекты

Пример:

#!/usr/bin/env python3

import argparse

if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('--infile', type=argparse.FileType('r', encoding='UTF-8'), 
                      required=True)
  args = parser.parse_args()
  print(args)
  args.infile.close()

И затем, когда вы запустили...

  • Без аргумента:

    $ ./stack_overflow.py
    usage: stack_overflow.py [-h] --infile INFILE
    stack_overflow.py: error: the following arguments are required: --infile
    
  • С несуществующим файлом:

    $ ./stack_overflow.py --infile notme
    usage: stack_overflow.py [-h] --infile INFILE
    stack_overflow.py: error: argument --infile: can't open 'notme': [Errno 2] No such file or directory: 'notme'
    
  • С существующим файлом:

    $ ./stack_overflow.py --infile ./stack_overflow.py
    Namespace(infile=<_io.TextIOWrapper name='./stack_overflow.py' mode='r' encoding='UTF-8'>)
    
  • Использование '-' для STDIN:

    $ echo 'hello' | ./stack_overflow.py --infile -
    Namespace(infile=<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>)