2014-04-25 3 views
9

ich argparse bin mit einem Befehl mit Subbefehle bauen:Permit argparse globale Flags nach subcommand

mycommand [GLOBAL FLAGS] subcommand [FLAGS]

ich die globalen Flags arbeiten möchten, ob sie vor oder nach dem Unterbefehl. Gibt es eine saubere Möglichkeit, dies zu tun, ohne Code zu wiederholen?

Zum Beispiel:

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... 

Ich möchte dies für viele Fahnen tun, also mich immer und immer scheint ... unpythonic.

Antwort

10

Dies ist ein perfekter Anwendungsfall für parents argparse Merkmal:

Manchmal mehrere Parser eine gemeinsame Reihe von Argumenten. Eher als Wiederholung der Definitionen dieser Argumente, ein einzelner Parser mit allen gemeinsamen Argumente und an parents übergeben als Argument an ArgumentParser kann verwendet werden.

Definieren Sie ein Basisparent ArgumentParser, fügen Sie Argumente hinzu, die über Subparser gemeinsam genutzt werden. Dann fügen Sie subparsers und stellen Sie Ihre Basis-Parser als Eltern, indem sie parents Stichwort Argument:

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 

Beachten Sie, dass add_help=False hier hilft bei conflicting help argument Probleme zu vermeiden.

Siehe auch: Python argparse - Add argument to multiple subparsers.

+3

„Ich würde die globalen Flags mögen * arbeiten, ob sie vor oder nach dem subcommand *.“ Ich glaube, diese Lösung funktioniert nicht, wenn die Fahnen vor dem vorgesehen sind Unterbefehl in der Befehlszeile. Ich habe * auch * versucht, das base_subparser als Elternteil zum obersten Parser anzuwenden, aber dieses verhält sich schlecht für erforderlich = zutreffende Argumente. – Weeble

1

Sie sind für argparse Lösung gefragt, aber wie Sie auch für eine Lösung Pythonic nennen, ich werde eine alternative Verwendung Paket docopt absichtlich schlagen:

bekommen es:

$ pip install docopt 

Code schreiben in mycommand Datei:

""" 
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 

es dann von der Kommandozeile aufrufen:

grundlegende Hilfe (keine Argumente):

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

für eine Hilfe Vorstellung:

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

Arguments: 
    <zone> zone name 

Options: 
    -h --help 
    --disable disable 

Mit subcommand kompilieren:

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

Flagge hinzufügen disable:

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

Start subcommand funktioniert auch:

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

Meine Verwendung des Argument Parser begann mit argparse auch, und ich hasste die Komplexität des Codes, die für etwas zu tun brauchen.

Später verwandelte ich mich in plac, die sehr effiziente Möglichkeit ist, eine Python-Funktion in sehr brauchbare Befehle für die Konsole zu verwandeln.

Dennoch war ich durch Regeln begrenzt zu folgen und zu verstehen, die mir in vielen Fällen nicht sehr klar waren. Mit docopt schätze ich, dass ich den Docstring zuerst schreiben kann, den Standardregeln von POSIX folgend, und es dann in meinem Code verwende. Wenn Sie sich für die Validierung von Argumenten interessieren, werde ich Sie zu Beispielen dieses großartigen Pakets führen.

1

Ich sehe zwei Probleme in Ihrem Beispiel:

1) Die Verwendung von 'disable' sowohl in dem Parser und subparsers. Nested ArgumentParser befasst sich mit dieser überlappenden dest.

2) Der wiederholte Satz von Argumenten in den Subparsern. parents ist sicherlich eine Möglichkeit, das zu vereinfachen. Aber Sie könnten leicht Ihren eigenen Code schreiben:

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...