web-dev-qa-db-fra.com

Fichier comme argument de ligne de commande pour argparse - message d'erreur si l'argument n'est pas valide

J'utilise actuellement argparse comme ceci:

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)

Maintenant, je voudrais noter que l'argument de -i Devrait être un fichier lisible. Comment puis je faire ça?

J'ai essayé d'ajouter type=open, type=argparse.FileType('r') et ils ont fonctionné, mais si le fichier n'est pas valide, je voudrais obtenir un message d'erreur. Comment puis je faire ça?

49
Martin Thoma

C'est assez facile en fait. Vous avez juste besoin d'écrire une fonction qui vérifie si le fichier est valide et écrit une erreur sinon. Utilisez cette fonction avec l'option type. Notez que vous pouvez obtenir plus de fantaisie et créer une action personnalisée en sous-classant argparse.Action, mais je ne pense pas que ce soit nécessaire ici. Dans mon exemple, je renvoie un descripteur de fichier ouvert (voir ci-dessous):

#!/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)
76
mgilson

Une façon de le faire dans Python 3.4 est d'utiliser le argparse.FileType classe. Assurez-vous de fermer le flux d'entrée lorsque vous avez terminé. Ceci est également utile car vous pouvez utiliser le pseudo-argument '-' pour STDIN/STDOUT. De la documentation:

Les objets FileType comprennent le pseudo-argument '-' et le convertir automatiquement en sys.stdin pour des objets lisibles FileType et sys.stdout pour les objets inscriptibles FileType

Exemple:

#!/usr/bin/env python3

import argparse

if __== '__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()

Et puis quand couru ...

  • Sans argument:

    $ ./stack_overflow.py
    usage: stack_overflow.py [-h] --infile INFILE
    stack_overflow.py: error: the following arguments are required: --infile
    
  • Avec un fichier inexistant:

    $ ./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'
    
  • Avec un fichier existant:

    $ ./stack_overflow.py --infile ./stack_overflow.py
    Namespace(infile=<_io.TextIOWrapper name='./stack_overflow.py' mode='r' encoding='UTF-8'>)
    
  • En utilisant '-' pour STDIN:

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

Je viens de trouver celui-ci:

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 __== "__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)

Source: fhcrc.github.com

17
Martin Thoma