2014-04-25 10 views
9

Używam argparse zbudować polecenie z podpoleceń:Pozwolenie argparse globalnych flag po podrzędnego

MyCommand [global flagi] komenda [flagi]

Chciałbym globalnych flag do pracy, czy są one przed lub po podkomende. Czy jest to czysty sposób, który nie wymaga powtarzania kodu?

Na przykład:

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

Chcę to zrobić dla wielu flag, więc powtarzam w kółko wydaje ... unpythonic.

Odpowiedz

10

Jest to idealny przypadek użycia dla parents argparse cecha:

Czasami kilka Parsery dzielić wspólny zbiór argumentów. Raczej niż powtarzanie definicji tych argumentów, można użyć pojedynczego analizatora składni ze wszystkimi wspólnymi argumentami i przekazanymi do parent = argument do ArgumentParser.

Zdefiniuj rodzica bazowego ArgumentParser, dodaj argumenty, które będą udostępniane w przekątnych. Następnie dodać subparsers i ustawić swoją bazę parser jako rodzic, dostarczając argumentu parents słów kluczowych:

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 

Zauważ, że add_help=False tutaj pomaga uniknąć problemów z conflicting help argument.

Zobacz również: Python argparse - Add argument to multiple subparsers.

+3

"Chciałbym, aby flagi globalne działały * niezależnie od tego, czy są przed, czy po podkomende. *" Wierzę, że to rozwiązanie nie działa, gdy flagi są dostarczane przed podkomenda w wierszu polecenia. Próbowałem * również * stosowania base_subparser jako nadrzędny do parser najwyższego poziomu, ale to zachowuje się źle dla wymaganych = True argumentów. – Weeble

1

Pytasz do sporządzania roztworu argparse, ale również zadzwonić na rozwiązanie pythonic będę celowo zaproponować alternatywę za pomocą pakietu docopt:

dostać go:

$ pip install docopt 

Wpisz swój kod do mycommand plik:

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

następnie wywołać ją z linii poleceń:

podstawowa pomoc (bez argumentów):

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

Prośba o pomoc:

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

Arguments: 
    <zone> zone name 

Options: 
    -h --help 
    --disable disable 

Korzystanie kompilacji komendy:

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

dodać flagę --disable:

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

launch Komenda działa zbyt:

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

Moja wykorzystanie parserami argumentów zaczęło się argparse też, i nienawidziłem złożoność kodu, który był potrzebny do zrobienia czegoś.

Później zmieniłem się w plac, co jest bardzo skutecznym sposobem przekształcenia funkcji Pythona w bardzo użyteczne polecenie dla konsoli.

Nadal byłem ograniczony zestawem zasad do naśladowania i zrozumienia, które nie były dla mnie zbyt jasne w wielu przypadkach. Dzięki docopt doceniam, że najpierw mogę napisać docstring, stosując standardowe reguły POSIX, a następnie użyć go w moim kodzie. Jeśli zależy ci na sprawdzeniu argumentów, skieruję cię do próbek tego wspaniałego pakietu.

1

Widzę dwa problemy w swoim przykładzie:

1) stosowanie '--disable' zarówno parsera i subparsers. Nested ArgumentParser dotyczy tego nakładającego się dest.

2) Powtórzenie zestawu argumentów w trybach. parents jest z pewnością jednym ze sposobów na uproszczenie tego. Ale możesz łatwo napisać swój własny kod:

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... 
Powiązane problemy