2014-04-25 10 views
9

Sto usando argparse per costruire un comando con sottocomandi:Permesso argparse bandiere globali dopo sottocomando

mycommand [Global Flags] sottocomando [flags]

vorrei le bandiere globali di lavorare se sono prima o dopo il sottocomando. C'è un modo pulito per farlo che non coinvolga il codice ripetuto?

Ad esempio:

parser = argparse.ArgumentParser() 
    subparsers = parser.add_subparsers(dest='subparser_name') 

    parser.add_argument('--disable') # This flag... 

    sp = subparsers.add_parser('compile') 
    sp.add_argument('zones', nargs='*') 
    sp.add_argument('--disable')  # Is repeated... 

    sp = subparsers.add_parser('launch') 
    sp.add_argument('zones', nargs='*') 
    sp.add_argument('--disable')  # over and over... 

che voglio fare questo per molte bandiere, quindi ripetermi più e sembra finita ... unpythonic.

risposta

10

Si tratta di un caso d'uso perfetto per parents caratteristica argparse:

A volte, diversi parser condividono un insieme comune di argomenti. Piuttosto piuttosto che ripetere le definizioni di questi argomenti, un singolo parser con tutti gli argomenti condivisi e passato ai genitori = argomento a ArgumentParser può essere usato.

Definire un genitore di base ArgumentParser, aggiungere argomenti che verranno condivisi tra i subparatori. Quindi, aggiungere subparsers e impostare il parser di base come un genitore, fornendo argomento parents parola chiave:

parser = argparse.ArgumentParser() 
subparsers = parser.add_subparsers(dest='subparser_name') 

base_subparser = argparse.ArgumentParser(add_help=False) 
# define common shared arguments 
base_subparser.add_argument('--disable') 

sp = subparsers.add_parser('compile', parents=[base_subparser]) 
# define custom arguments 
sp = subparsers.add_parser('launch', parents=[base_subparser]) 
# define custom arguments 

Nota che add_help=False qui aiuta ad evitare problemi con conflicting help argument.

Vedere anche: Python argparse - Add argument to multiple subparsers.

+3

"Vorrei che le bandiere globali funzionassero * sia prima che dopo il sottocomando. *" Credo che questa soluzione non funzioni quando i flag sono forniti prima del sottocomando sulla riga di comando. Ho provato * anche * applicando il basic_subparser come genitore al parser di primo livello, ma questo si comporta male per required = True arguments. – Weeble

1

Stai chiedendo per soluzione argparse, ma come si chiama anche per una soluzione Pythonic, io volutamente proporre un'alternativa utilizzando il pacchetto docopt:

ottenerlo:

$ pip install docopt 

scrivere il codice in mycommand File:

""" 
Usage: 
    mycommand compile [--disable] <zone>... 
    mycommand launch [--disable] <zone>... 

Arguments: 
    <zone> zone name 

Options: 
    -h --help 
    --disable disable 

""" 
from docopt import docopt 

if __name__ == "__main__": 
    args = docopt(__doc__) 
    print args 

Poi chiamare da linea di comando:

aiuto di base (senza argomenti):

$ python mycommand 
Usage: 
    mycommand compile [--disable] <zone>... 
    mycommand launch [--disable] <zone>... 

Chiedendo un aiuto:

$ python mycommand -h 
Usage: 
    mycommand compile [--disable] <zone>... 
    mycommand launch [--disable] <zone>... 

Arguments: 
    <zone> zone name 

Options: 
    -h --help 
    --disable disable 

Utilizzando compilare sottocomando:

$ python mycommand compile zoneAlfa zoneBeta 
{'--disable': False, 
'<zone>': ['zoneAlfa', 'zoneBeta'], 
'compile': True, 
'launch': False} 

aggiungere bandiera --disable:

$ python mycommand compile --disable zoneAlfa zoneBeta 
{'--disable': True, 
'<zone>': ['zoneAlfa', 'zoneBeta'], 
'compile': True, 
'launch': False} 

lancio sottocomando lavora troppo:

$ python mycommand launch --disable zoneAlfa zoneBeta 
{'--disable': True, 
'<zone>': ['zoneAlfa', 'zoneBeta'], 
'compile': False, 
'launch': True} 

mio utilizzo dei parser argomento è iniziato con argparse troppo, e ho odiato la complessità del codice, che era necessario per fare qualcosa.

Più tardi ho trasformato in plac, che è un modo molto efficiente per trasformare una funzione python in un comando molto utile per la console.

Tuttavia, ero limitato da un insieme di regole da seguire e capire, che non mi erano molto chiare in molti casi. Con docopt apprezzo che posso scrivere prima la docstring, seguendo le regole standard di POSIX, e quindi usarla nel mio codice. Se ti interessa la convalida degli argomenti, ti indirizzerò ai campioni di questo fantastico pacchetto.

1

vedo due problemi nel tuo esempio:

1) l'uso di '--disable' sia nel parser e subparsers. Nested ArgumentParser si occupa di quella sovrapposizione dest.

2) Il set ripetuto di argomenti nei sottospecifiche. parents è sicuramente un modo per semplificarlo. Ma potresti facilmente scrivere il tuo codice:

parser = argparse.ArgumentParser() 
subparsers = parser.add_subparsers(dest='subparser_name') 

parser.add_argument('--disable', dest='main_disable') # This flag... 

for name in ['compile', 'launch']: 
    sp = subparsers.add_parser(name) 
    sp.add_argument('zones', nargs='*') 
    sp.add_argument('--disable', dest=name+'_disable')  # Is repeated... 
Problemi correlati