2015-05-13 9 views
5

hier ist, was ich tun möchte: Ein Befehl, der aussieht wie git Befehl Verhalten. Sie erhalten nicht die gleichen Optionen, egal ob Sie git commit oder git checkout eingegeben haben. Aber in meinem Fall mag ich auf einem Argument-Wert (ein Dateiname) wie dies anhand verschiedene Argumente bieten:Python argparse, bieten verschiedene Argumente basierend auf Parent-Argument-Wert

>cmd file.a -h 
usage: cmd filename [-opt1] [-opt2] 
positional arguments: 
filename   file to process 
optional arguments: 
-opt1   do something on files of type 'a' 
-opt2   do something else on files of type 'a' 

>cmd file.b -h 
usage: cmd filename [-opt3] [-opt4] 
positional arguments: 
filename   file to process 
optional arguments: 
-opt3   do something on files of type 'b' 
-opt4   do something else on files of type 'b' 

Ist es möglich, diese Art der Sache zu tun mit Python und argparse?
Was ich bisher versucht habe, ist:

parser = argparse.Argument_parser(prog='cmd') 
subparsers = parser.add_subparsers() 

parser.add_argument('filename', 
        help="file or sequence to process") 

args = parser.parse_args(args=argv[1:]) 

sub_parser = subparsers.add_parser(args.filename, help="job type") 
base, ext = os.path.splitext(args.filename) 
if ext == 'a': 
    sub_parser.add_argument("-opt1", action='store_true') 
    sub_parser.add_argument("-opt2", action='store_true') 
elif ext == 'b': 
    sub_parser.add_argument("-opt3", action='store_true') 
    sub_parser.add_argument("-opt4", action='store_true') 

args = parser.parse_args(args=argv[1:]) 

Ich weiß nicht, ob ich subparsers oder Kind Parser oder Gruppen verwenden soll, bin ich irgendwie in all den Möglichkeiten, die argparse

+0

Basierend auf dem Verhalten, das Sie sehen möchten, klingt es so, als wäre ein Subparser der Weg, den Sie gehen möchten. –

Antwort

14
vorgesehen verloren

Wenn Sie einen Blick auf parse_args() implementation werfen, werden Sie feststellen, dass es alle Argumente auf einmal analysiert (es verwendet yield nicht, um kontinuierlich den Status zu generieren), also müssen Sie Ihre Struktur vor und nicht nach der Hälfte der Argumente vorbereiten.

Taking from official example in the docs Sie sollten subparser (n), wie dies vor Start-Parsing hinzufügen:

import argparse 

parser = argparse.ArgumentParser(prog='PROG') 
subparsers = parser.add_subparsers(help='sub-command help') 

# create the parser for the "a" command 
parser_a = subparsers.add_parser('a', help='a help') 
parser_a.add_argument("--opt1", action='store_true') 
parser_a.add_argument("--opt2", action='store_true') 

# create the parser for the "b" command 
parser_b = subparsers.add_parser('b', help='b help') 
parser_b.add_argument("--opt3", action='store_true') 
parser_b.add_argument("--opt4", action='store_true') 

# parse some argument lists 
print(parser.parse_args()) 

Und die Ausgabe (in der Befehlszeile), wird Hilfe schön gedruckt:

D:\tmp>s.py -h 
usage: PROG [-h] {a,b} ... 

positional arguments: 
    {a,b}  sub-command help 
    a   a help 
    b   b help 

optional arguments: 
    -h, --help show this help message and exit 

A Argumente werden geparst

B Argumente werden

analysiert
D:\tmp>s.py b 
Namespace(opt3=False, opt4=False) 

Auch mit args:

D:\tmp>s.py b --opt3 
Namespace(opt3=True, opt4=False) 

A Argumente in B Lauf Fehler verursacht:

D:\tmp>s.py b --opt2 
usage: PROG [-h] {a,b} ... 
PROG: error: unrecognized arguments: --opt2 

Auch wenn Sie identify which subparser benötigen verwendet wurde Sie dest=name zu parser.add_subparsers() Anruf hinzufügen kann (die ich denke, ist nicht gestresst richtig in der Dokumentation):

subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name') 

Mit dem Ergebnis:

D:\tmp>s.py b --opt3 
Namespace(opt3=True, opt4=False, subparser_name='b') 

Wenn Sie benötigt parse_args() prüft nur nachlauf aruments

Sometimes a script may only parse a few of the command-line arguments, passing the remaining arguments on to another script or program. In these cases, the parse_known_args() method can be useful. It works much like parse_args() except that it does not produce an error when extra arguments are present. Instead, it returns a two item tuple containing the populated namespace and the list of remaining argument strings.

Immerhin:

def parse_args(self, args=None, namespace=None): 
    args, argv = self.parse_known_args(args, namespace) 
    if argv: 
     msg = _('unrecognized arguments: %s') 
     self.error(msg % ' '.join(argv)) 
    return args 

Und dann können Sie wirklich Argumente dynamisch zu erstellen (zum Beispiel von teuer Ressource einige Argument Optionen laden) Sie parse_known_args() verwenden könnte Erneut einen Parser auf argv ausführen, aber ich kann mir vorstellen, einige Probleme, die damit gehen können und ich würde es nicht empfehlen, bis wirklich notwendig ist.

+0

Super Antwort. Viel Spaß mit deinem neuen Abzeichen. –

+0

Erneut einlesen. Noch einige Ebenen der Großartigkeit, um hier aufzudecken :) –

Verwandte Themen