2016-05-26 15 views
9

E 'possibile quando si utilizza il modulo argparse per aggiungere la convalida durante l'analisi degli argomenti?Python: best practice di convalida dell'analisi degli argomenti

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() 

Sarebbe possibile, di aggiungere un controllo di convalida a questo argomento parser, per assicurarsi che il file/database di backup esiste? Piuttosto che dover aggiungere un ulteriore controllo dopo questo per ogni parametro ...

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

risposta

11

Il argparse.FileType è una classe di fabbrica type che può aprire un file e, naturalmente, nel processo genera un errore se il file non esiste o non può essere creato. Puoi guardare il suo codice per vedere come creare la tua classe (o funzione) per testare i tuoi input.

L'argomento type parametro è un callable (funzione, ecc) che prende una stringa, mette alla prova in base alle esigenze, e lo converte (se necessario) in quel tipo di valore che si desidera salvare il args namespace. Quindi può fare qualsiasi tipo di test tu voglia. Se lo type genera un errore, il parser crea un messaggio di errore (e l'utilizzo) ed esce.

Ora, se questo è il posto giusto per fare il test o meno dipende dalla vostra situazione. A volte aprire un file con FileType va bene, ma poi devi chiuderlo da solo, o aspettare che il programma finisca. Non è possibile utilizzare quel file aperto in un contesto with open(filename) as f:. Lo stesso potrebbe valere per il tuo database. In un programma complesso potresti non voler aprire o creare il file subito.

Ho scritto per un bug Python/emettere una variazione su FileType che ha creato un context, un oggetto che potrebbe essere utilizzato nel contesto with. Ho anche utilizzato i test os per verificare se il file esiste o potrebbe essere creato, senza farlo effettivamente. Ma richiedeva ulteriori trucchi se il era stdin/out che non si desidera chiudere. A volte provare a fare cose del genere in argparse è solo più lavoro di quanto valga.

In ogni modo, se si dispone di un metodo di prova facile, si potrebbe avvolgere in una semplice funzione type come questa:

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') 

non credo conta un bel po 'se si sceglie di implementare il test come questo in il type o Action. Penso che lo type sia più semplice e più in linea con le intenzioni dello sviluppatore.

+1

Ottima risposta! Mi piacerebbe raccomandare di usare argparse.ArgumentTypeError (messaggio) per stampare 'messaggio' sulla console. – tabata

10

Sicuramente! Devi solo specificare un'azione personalizzata come classe e sovrascrivere __call__(..). Link to documentation.

Qualcosa di simile:

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() 

Nel tuo caso particolare, ho immaginare che avresti avuto DatabaseAction e FileAction (o qualcosa di simile).