2013-09-04 16 views
7

Mi manca qualcosa di ovvio. L'obiettivo è utilizzare argparse con il primo parametro richiesto, un secondo opzionale e qualsiasi altro parametro facoltativo.Python argparse: Combina parametri facoltativi con nargs = argparse.REMAINDER

Per mostrare il problema sono stati eseguiti due parser di test; l'unica differenza tra loro è l'uso di nargs = argparse.REMAINDER in uno e nargs = '*' nell'altro.

def doParser1(argsin): 
    parser = argparse.ArgumentParser(description='Parser demo.') 
    parser.add_argument('req1', help='first required parameter') 
    parser.add_argument('--opt1', help='first optional parameter') 
    parser.add_argument('leftovers', nargs=argparse.REMAINDER, 
        help='all the other parameters') 
    argsout = parser.parse_args(args=argsin) 
    print argsout 
    return argsout 

def doParser2(argsin): 
    parser = argparse.ArgumentParser(description='Parser demo.') 
    parser.add_argument('req1', help='first required parameter') 
    parser.add_argument('--opt1', help='first optional parameter') 
    parser.add_argument('leftovers', nargs='*', 
        help='all the other parameters') 
    argsout = parser.parse_args(args=argsin) 
    print argsout 
    return argsout 

Se non ci sono parametri aggiuntivi, parser2 funziona. Questo è l'ingresso seguito da parser1 e parser 1:

input: ['req1value', '--opt1', 'opt1value'] 
Namespace(leftovers=['--opt1', 'opt1value'], opt1=None, req1='req1value') 
Namespace(leftovers=None, opt1='opt1value', req1='req1value') 

Se ci sono dei parametri aggiuntivi, il valore opt1 è mancato in parser1 e parser2 ottiene solo confuso:

input: ['req1value', '--opt1', 'opt1value', 'r1', 'r2'] 
Namespace(leftovers=['--opt1', 'opt1value', 'r1', 'r2'], opt1=None, req1='req1value') 
usage: py-argparse.py [-h] [--opt1 OPT1] 
        [-leftovers [LEFTOVERS [LEFTOVERS ...]]] 
        req1 
py-argparse.py: error: unrecognized arguments: r1 r2 

Il risultato atteso dovrebbe essere:

Namespace(leftovers=['r1', 'r2'], opt1='opt1value', req1='req1value') 

Sembra che questo dovrebbe essere un caso semplice e ciò che è qui è semplificato da quello che sto provando a fare davvero. Ho provato a rendere gli avanzi facoltativi, aggiungendo una varietà di altre opzioni, ma niente funziona meglio.

Qualsiasi aiuto sarebbe apprezzato.

risposta

3

--opt1 deve venire prima degli argomenti "senza nome". I suoi casi reali di test dovrebbero essere:

['--opt1', 'opt1value', 'req1value'] 

e

['--opt1', 'opt1value', 'req1value', 'r1', 'r2'] 
+0

Sapevo che doveva essere qualcosa di semplice. Non l'ho visto ovunque nei documenti, ma funziona con nargs = argparse.REMAINDER, e questo è tutto ciò di cui ho bisogno. Grazie. – OPunWide

4

si potrebbe usare parse_known_args:

import argparse 
parser = argparse.ArgumentParser(description='Parser demo.') 
parser.add_argument('req1', help='first required parameter') 
parser.add_argument('--opt1', help='first optional parameter') 

args, leftovers = parser.parse_known_args(['req1value', '--opt1', 'opt1value']) 
print(args, leftovers) 
# (Namespace(opt1='opt1value', req1='req1value'), []) 

args, leftovers = parser.parse_known_args(['req1value', '--opt1', 'opt1value', 'r1', 'r2']) 
print(args, leftovers) 
# (Namespace(opt1='opt1value', req1='req1value'), ['r1', 'r2']) 
+0

Lo archivierò per dopo. L'altra risposta si adattava a ciò di cui avevo bisogno. Sembra che questa soluzione non richieda le opzioni per essere la prima, giusto? – OPunWide

+0

Giusto. Questo accetterà gli argomenti in qualsiasi ordine. Quelli che non corrispondono finiscono in "avanzi". – unutbu

+0

Bella soluzione. Tuttavia, il precedente * resto * non verrà più visualizzato nella documentazione/guida. – hitzg

2

L'inter mescolanza di positionals e optional è difficile quando uno o più dei positionals è del tipo "zero o più" (? * REMAINDER). La soluzione più semplice è non mischiarli: prima gli optionals, poi tutti i posizionali.

Ecco quello che sta succedendo:

input: ['req1value', '--opt1', 'opt1value'] 
Namespace(leftovers=['--opt1', 'opt1value'], opt1=None, req1='req1value') 

A causa della stringa req1value il parser prima analizza per positionals. req1 desidera 1 stringa, leftovers accetta tutto il resto incluso --opt1.

Namespace(leftovers=None, opt1='opt1value', req1='req1value') 

Con *leftovers è soddisfatto [], nessuna stringa, quindi None (in realtà ottengo []). --opt1 viene analizzato come facoltativo.

input: ['req1value', '--opt1', 'opt1value', 'r1', 'r2'] 
... 
py-argparse.py: error: unrecognized arguments: r1 r2 

Come prima *leftovers è impostato su []. -opt1 viene elaborato. Ma ora ci sono 2 stringhe senza un posto dove metterle. Volevi che entrassero nello leftovers, ma era già in uso. Se leftovers era +, li avrebbe presi come desiderato.

La chiave è che quando tenta di analizzare il 1 ° posizionale, cerca anche di analizzare tutti i posizionali che può. A un livello parse_args sta facendo re.match('(A)(A*)','AOA') gruppi di produzione ('A', '').

Ci sono 2 patch proposte che trattano questo problema. Uno utilizza il 2 step parse_known_args per consentire una completa miscelazione di optionals e positionals. Questo è il tipo di comportamento che gli utenti di optparse potrebbero aspettarsi.

L'altra patch tenta di ritardare la gestione di posizionali che possono accettare stringhe di argomento 0http://bugs.python.org/issue15112.

Problemi correlati