2015-06-19 5 views
7

Sto provando a creare un argomento facoltativo per uno script che non può assumere valori o 2 valori, nient'altro. Riuscirai a farlo usando argparse?argparse - richiede 2 valori o nessuno per un argomento facoltativo

# desired output: 
# ./script.py -a --> works 
# ./script.py -a val1 --> error 
# ./script.py -a val1 val2 --> works 


versione 1 - accetta 0 o 1 Valori:

parser = argparse.ArgumentParser() 
parser.add_argument("-a", "--action", nargs="?", const=True, action="store", help="do some action") 
args = parser.parse_args() 

# output: 
# ./script.py -a --> works 
# ./script.py -a val1 --> works 
# ./script.py -a val1 val2 --> error 


Versione 2 - accetta esattamente 2 valori:

parser = argparse.ArgumentParser() 
parser.add_argument("-a", "--action", nargs=2, action="store", help="do some action") 
args = parser.parse_args() 

# output: 
# ./script.py -a --> error 
# ./script.py -a val1 --> error 
# ./script.py -a val1 val2 --> works 


Come si coniuga questi 2 versioni differenti in modo che lo script accetti 0 o 2 valori per l'argomento, ma rifiuta quando ha solo 1 valore?

risposta

6

Dovrai fare il tuo errore controllando qui. Accetta 0 o più valore, e rifiutare qualcosa di diverso da 0 o 2:

parser = argparse.ArgumentParser() 
parser.add_argument("-a", "--action", nargs='*', action="store", help="do some action") 
args = parser.parse_args() 

if args.action is not None and len(args.action) not in (0, 2): 
    parser.error('Either give no values for action, or two, not {}.'.format(len(args.action))) 

noti che args.action è impostato su None quando nessun interruttore -a è stato utilizzato:

>>> import argparse 
>>> parser = argparse.ArgumentParser() 
>>> parser.add_argument("-a", "--action", nargs='*', action="store", help="do some action") 
_StoreAction(option_strings=['-a', '--action'], dest='action', nargs='*', const=None, default=None, type=None, choices=None, help='do some action', metavar=None) 
>>> args = parser.parse_args([]) 
>>> args.action is None 
True 
>>> args = parser.parse_args(['-a']) 
>>> args.action 
[] 
+0

con questa soluzione "./script.py" si comporta come "./script.py -a" – xgord

+0

@xgord: in entrambi i casi non si vorrebbe un errore. Vuoi un errore quando usi './script.py -a foo' o' ./script.py -a foo bar baz' e più a lungo. –

+0

@xgord: questo controllo * non * sulla gestione del caso './Script.py', solo su un errore quando è stato usato' -a' ma con il numero sbagliato di argomenti. Cosa ti aspettavi che accadesse invece in quel caso? –

3

Basta gestire quel caso te stesso:

parser.add_argument("-a", "--action", nargs='*', action="store", help="do some action") 
args = parser.parse_args() 

if args.action is not None: 
    if len(args.action) not in (0, 2): 
     parser.error('Specify no or two actions') 

    # action was specified but either there were two actions or no action 
else: 
    # action was not specified 

Naturalmente è necessario aggiornare il testo della guida in questo caso in modo che l'utente ha la possibilità di conoscere questo prima di eseguire nell'errore.

+0

con questa soluzione "./script.py" agisce lo stesso di "-a ./script.py" – xgord

+0

Come c'è una differenza pratica tra i due? – poke

+1

@xgord In realtà non agisce allo stesso modo. se fai './script.py',' args.action' sarà 'None'; se fai './script.py -a', quindi' args.action' sarà '[]'. – poke

-1

Come circa l'argomento richiesto: parser.add_argument("-a", "--action", nargs=2, action="store", help="do some action", required=False)

+1

Questo non funziona, '-a' senza valori dà un errore perché non è esattamente 2 elementi. 'required = False' è * il default * in ogni caso. –

+1

Gli optionals ('--like-this -or-this') non sono richiesti di default con argparse. – poke

0

Avere la tua opzione prendere una singola stringa opzionale separata da virgola. Utilizzerai un tipo personalizzato per convertire quella stringa in un elenco e verificare che contenga esattamente due elementi.

def pair(value): 
    rv = value.split(',') 
    if len(rv) != 2: 
     raise argparse.ArgumentParser() 
    return rv 

parser.add_argument("-a", "--action", nargs='?', 
        type=pair, metavar='val1,val2', 
        help="do some action") 
print parser.parse_args() 

Poi devi usarlo come

$ ./script.py -a 
Namespace(action=None) 
$ ./script.py -a val1,val2 
Namespace(action=['val1','val2']) 
$ ./script.py -a val1 
usage: tmp.py [-h] [-a [ACTION]] 
script.py: error: argument -a/--action: invalid pair value: 'val1' 
$ ./script.py -a val1,val2,val3 
usage: tmp.py [-h] [-a [ACTION]] 
script.py: error: argument -a/--action: invalid pair value: 'val1,val2,val3' 

È possibile regolare la definizione di pair di utilizzare un separatore diverso e di restituire qualcosa di diverso da una lista (una tupla, per esempio).

metavar fornisce una migliore indicazione che l'argomento su action è una coppia di valori, piuttosto che uno solo.

$ ./script.py -h 
usage: script.py [-h] [-a [val1,val2]] 

optional arguments: 
    -h, --help   show this help message and exit 
    -a [val1,val2], --action [val1,val2] 
         do some action