2011-01-22 3 views
17

Se ho gli argomenti '-a', '-b', '-c', '-d', con la funzione add_mutually_exclusive_group() il mio programma dovrà utilizzare solo uno di essi. C'è un modo per combinarlo, in modo che il programma accetti solo '-a 999 -b 999' o '-c 999 -d 999'?Argparse (python) supporta gruppi di argomenti mutuamente esclusivi?

Edit: l'aggiunta di un semplice programma per maggiore chiarezza:

>>> parser = argparse.ArgumentParser() 
>>> group = parser.add_mutually_exclusive_group() 
>>> group.add_argument('-a') 
>>> group.add_argument('-b') 
>>> group.add_argument('-c') 
>>> group.add_argument('-d') 

Poi può essere chiamato solo ./app.py -a | ./app.py -b | ./app.py -c | ./app.py -d. È possibile avere gruppi di esclusione in argparse, in modo che venga chiamato solo ./app.py -a .. -b .. | ./app.py -c .. -d ..?

risposta

6

EDIT: Non importa. Perché argparse rende l'orribile scelta di dover creare un'opzione quando si richiama group.add_argument. Questa non sarebbe la mia scelta di design. Se siete disperati per questa funzione, si può provare a farlo con ConflictsOptionParser:

# exclusivegroups.py 
import conflictsparse 

parser = conflictsparse.ConflictsOptionParser() 
a_opt = parser.add_option('-a') 
b_opt = parser.add_option('-b') 
c_opt = parser.add_option('-c') 
d_opt = parser.add_option('-d') 

import itertools 
compatible_opts1 = (a_opt, b_opt) 
compatible_opts2 = (c_opt, d_opt) 
exclusives = itertools.product(compatible_opts1, compatible_opts2) 
for exclusive_grp in exclusives: 
    parser.register_conflict(exclusive_grp) 


opts, args = parser.parse_args() 
print "opts: ", opts 
print "args: ", args 

Così, quando invochiamo esso, possiamo vedere si ottiene l'effetto desiderato.

$ python exclusivegroups.py -a 1 -b 2 
opts: {'a': '1', 'c': None, 'b': '2', 'd': None} 
args: [] 
$ python exclusivegroups.py -c 3 -d 2 
opts: {'a': None, 'c': '3', 'b': None, 'd': '2'} 
args: [] 
$ python exclusivegroups.py -a 1 -b 2 -c 3 
Usage: exclusivegroups.py [options] 

exclusivegroups.py: error: -b, -c are incompatible options. 

Il messaggio di avviso non informa che entrambi '-a' e '-b' sono incompatibili con '-c', comunque un messaggio di errore più appropriata potrebbe essere realizzato. Più vecchio, risposta errata di seguito.

ANZIANI EDIT:[Questa modifica è sbagliato, anche se non sarebbe solo un mondo perfetto se argparse lavorato in questo modo?] La mia risposta precedente in realtà non era corretta, si dovrebbe essere in grado di fare questo con argparse specificando un gruppo per opzioni mutuamente esclusive. Possiamo anche usare itertools per generalizzare il processo. E fallo in modo che non dobbiamo digitare tutte le combinazioni in modo esplicito:

import itertools 
compatible_opts1 = ('-a', '-b') 
compatible_opts2 = ('-c', '-d') 
exclusives = itertools.product(compatible_opts1, compatible_opts2) 
for exclusive_grp in exclusives: 
    group = parser.add_mutually_exclusive_group() 
    group.add_argument(exclusive_grp[0]) 
    group.add_argument(exclusive_grp[1]) 
+1

http://bugs.python.org/issue10984 ha una patch che consente di inserire un argomento in più di un gruppo mutuamente esclusivo. Fare questo è un cambiamento facile. La produzione di un utilizzo significativo con gruppi sovrapposti è più coinvolta. – hpaulj

5

Solo inciampato su questo problema me stesso. Dalla mia lettura dei documenti argparse, non sembra esserci un modo semplice per ottenerlo all'interno di argparse. Ho preso in considerazione l'utilizzo di parse_known_args, ma ciò equivale presto a scrivere una versione speciale di argparse ;-)

Forse un bug report è in ordine. Nel frattempo, se sei disposto a fare in modo che il tuo utente esegua un po 'di digitazione in più, puoi simularlo con sottogruppi (come funzionano gli argomenti di git e svn), ad es.

subparsers = parser.add_subparsers() 
    p_ab = subparsers.add_parser('ab') 
    p_ab.add_argument(...) 

    p_cd = subparsers.add_parser('cd') 
    p_cd.add_argument(...) 

Non è l'ideale, ma almeno ti dà il bene dal argparse senza troppo brutto aggiustamenti. Alla fine ho eliminato gli switch e ho semplicemente utilizzato le operazioni subparser con i sottomenu necessari.