web-dev-qa-db-fra.com

Python: meilleures pratiques de validation de l'analyse des arguments

Est-il possible lors de l'utilisation du module argparse d'ajouter une validation lors de l'analyse des arguments?

from argparse import ArgumentParser

parser = ArgumentParser(description='Argument parser for PG restore')

parser.add_argument('--database', dest='database',
                    default=None, required=False, help='Database to restore')

parser.add_argument('--backup', dest='backup',
                    required=True, help='Location of the backup file')

parsed_args = parser.parse_args()

Serait-il possible, pour ajouter un contrôle de validation à cet analyseur d'arguments, de s'assurer que le fichier/base de données de sauvegarde existe? Plutôt que d'avoir à ajouter une vérification supplémentaire après cela pour chaque paramètre tel que:

from os.path import exists
if not database_exists(parsed_args.database):
    raise DatabaseNotFoundError
if not exists(parsed_args.backup):
    raise FileNotFoundError
17
AK47

argparse.FileType Est une classe d'usine type qui peut ouvrir un fichier et, bien sûr, dans le processus, générer une erreur si le fichier n'existe pas ou ne peut pas être créé. Vous pouvez regarder son code pour voir comment créer votre propre classe (ou fonction) pour tester vos entrées.

L'argument type paramètre est un appelable (fonction, etc.) qui prend une chaîne, la teste selon les besoins et la convertit (selon les besoins) en le type de valeur que vous souhaitez enregistrer dans args espace de noms. Il peut donc effectuer tout type de test que vous souhaitez. Si le type déclenche une erreur, alors l'analyseur crée un message d'erreur (et son utilisation) et quitte.

Maintenant, que ce soit le bon endroit pour faire le test ou non, cela dépend de votre situation. Parfois, ouvrir un fichier avec FileType est très bien, mais vous devez le fermer vous-même ou attendre la fin du programme. Vous ne pouvez pas utiliser ce fichier ouvert dans un contexte with open(filename) as f:. La même chose pourrait s'appliquer à votre base de données. Dans un programme complexe, vous ne voudrez peut-être pas ouvrir ou créer le fichier tout de suite.

J'ai écrit pour un Python/émettre une variation sur FileType qui a créé un context, un objet qui pourrait être utilisé dans le with J'ai également utilisé des tests os pour vérifier si le fichier existait ou pouvait être créé, sans le faire. Mais cela nécessitait d'autres astuces si le file était stdin/out que vous ne veut pas fermer. Parfois, essayer de faire des choses comme ça dans argparse est juste plus de travail que ça vaut la peine.

Quoi qu'il en soit, si vous avez une méthode de test simple, vous pouvez l'encapsuler dans une simple fonction type comme ceci:

def database(astring):
    from os.path import exists
    if not database_exists(astring):
        raise ValueError  # or TypeError, or `argparse.ArgumentTypeError
    return astring

parser.add_argument('--database', dest='database',
                type = database, 
                default=None, required=False, help='Database to restore')

Je ne pense pas que cela soit très important que vous implémentiez des tests comme celui-ci dans type ou Action. Je pense que le type est plus simple et plus conforme aux intentions du développeur.

19
hpaulj

Sûrement! Il vous suffit de spécifier une action personnalisée en tant que classe et de remplacer __call__(..). Lien vers la documentation.

Quelque chose comme:

import argparse

class FooAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if values != "bar":
            print "Got value:", values
            raise ValueError("Not a bar!")
        setattr(namespace, self.dest, values)


parser = argparse.ArgumentParser()
parser.add_argument("--foo", action=FooAction)

parsed_args = parser.parse_args()

Dans votre cas particulier, j'imagine que vous auriez DatabaseAction et FileAction (ou quelque chose comme ça).

13
UltraInstinct