2015-05-13 16 views
5

ecco cosa vorrei fare: Un comando che assomiglia al comportamento del comando git. Non ottieni le stesse opzioni se hai digitato git commit o git checkout. Ma nel mio caso voglio fornire argomenti diversi sulla base di un valore di argomento (un nome di file) come questo:Python argparse, fornire argomenti diversi basati sul valore dell'argomento genitore

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

E 'possibile fare questo genere di cose utilizzando Python e argparse?
quello che ho provato finora è:

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:]) 

non so se devo usare subparsers o parser bambino o gruppi, Sono un po 'perso in tutte le possibilità offerte dal argparse

+0

In base al comportamento che si desidera vedere, sembra che una subparser sia il modo in cui si desidera andare. –

risposta

14

Quando dai un'occhiata a parse_args() implementation noterai che analizza tutti gli argomenti contemporaneamente (non usa yield per generare continuamente lo stato) quindi devi preparare la struttura prima e non dopo che la metà degli argomenti sarebbe stata analizzata.

Taking from official example in the docs si dovrebbe aggiungere subparser (s) prima di iniziare l'analisi di come questo:

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()) 

E l'uscita (in linea di comando), l'aiuto è ben stampato:

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 

Vengono analizzati gli argomenti

D:\tmp>s.py a --opt1 
Namespace(opt1=True, opt2=False) 

B argomenti vengono verificati

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

Anche con args:

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

esecuzione A argomenti in B causa di errore:

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

anche se è necessario identify which subparser è stato utilizzato è possibile aggiungere dest=name a parser.add_subparsers() chiamata (che credo non sia adeguatamente sottolineato nella documentazione):

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

Con il risultato di:

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

Se avete bisogno per creare realmente gli argomenti in modo dinamico (ad esempio caricare alcune opzioni argomento dalla costoso risorse) si potrebbe utilizzare parse_known_args():

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.

Dopo tutto, parse_args() solo controlla aruments finali:

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 

e poi si può ri-eseguire un altro parser su argv, ma posso immaginare alcuni problemi che possono andare con questo e non lo consiglierei fino a davvero necessario.

+0

Risposta stupenda. Goditi il ​​tuo nuovo badge. –

+0

Rileggilo di nuovo. Ancora strati di meraviglia da scoprire qui :) –

Problemi correlati