2012-06-05 11 views
6

argparse nie w kontaktach z sub-polecenia odbierania opcje globalne:Python: parser argument, który obsługuje globalne opcje do podkomendy prawidłowo

import argparse 
p = argparse.ArgumentParser() 
p.add_argument('--arg', action='store_true') 
s = p.add_subparsers() 
s.add_parser('test') 

będzie miał p.parse_args('--arg test'.split()) pracy,
ale nie na p.parse_args('test --arg'.split()).

Ktoś świadomy parsera argumentów python, który poprawnie obsługuje opcje globalne dla pod-poleceń?

+1

Co robić masz na myśli "nieudane"? Co chcesz się stać? Jakie opcje globalne? – alan

+0

zawodzi, ponieważ narzeka na nieznaną argumentację --arg – Ronny

Odpowiedz

6

Give docopt a try:

>>> from docopt import docopt 

>>> usage = """ 
... usage: prog.py command [--test] 
...  prog.py another [--test] 
... 
... --test Perform the test.""" 

>>> docopt(usage, argv='command --test') 
{'--test': True, 
'another': False, 
'command': True} 

>>> docopt(usage, argv='--test command') 
{'--test': True, 
'another': False, 
'command': True} 
+0

Wypróbowałem to, pojawiły się problemy – Ronny

+1

poszedł po nim po małym spree github wydania - dobra robota – Ronny

+0

@Roony, świetnie! i dzięki za opinie, więcej z tego jest zawsze mile widziane. – Halst

1

Oto brudne obejście -

import argparse 
p = argparse.ArgumentParser() 
p.add_argument('--arg', action='store_true') 
s = p.add_subparsers() 
s.add_parser('test') 

def my_parse_args(ss): 
    #parse the info the subparser knows about; don't issue an error on unknown stuff 
    namespace,leftover=p.parse_known_args(ss) 
    #reparse the unknown as global options and add it to the namespace. 
    if(leftover): 
     s.add_parser('null',add_help=False) 
     p.parse_args(leftover+['null'],namespace=namespace) 

    return namespace 

#print my_parse_args('-h'.split()) #This works too, but causes the script to stop. 
print my_parse_args('--arg test'.split()) 
print my_parse_args('test --arg'.split()) 

To działa - i można go modyfikować dość łatwo pracować z sys.argv (wystarczy zdjąć łańcuch split „ss”). Możesz nawet podklasować argparse.ArgumentParser i zamienić metodę parse_args na my_parse_args, a wtedy nigdy nie będziesz wiedział, jaka jest różnica - mimo że podklasowanie w celu zastąpienia jednej metody wydaje mi się przesadzone.

Sądzę jednak, że jest to nieco nietypowy sposób na użycie akapitów. Ogólnie rzecz biorąc, oczekuje się, że opcje globalne pojawią się przed opcjami dla trybów z akapitem, a nie później.

1

Parser ma określoną składnię: command <global options> subcommand <subcommand ptions>, próbujesz podać podkomendę za pomocą opcji, ale nie zdefiniowałeś jej.

+0

Myślę, że Ronny jest świadomy, dlaczego zawodzi (lub przynajmniej nie obchodzi) - szuka obejścia (używając argparse lub czegoś innego). – mgilson

3

W świecie Pythona dostępnych jest mnóstwo bibliotek służących do analizowania argumentów. Oto kilka, które widziałem, z których każdy powinien być w stanie obsłużyć rozwiązać problem, który próbujesz rozwiązać (na podstawie mojego rozmyte wspomnienie nich, kiedy grałem z nimi ostatni):

  • opster -Myślę, że to co rtęciowe zastosowania, IIRC
  • docopt -To jeden jest nowy, ale wykorzystuje ciekawe podejście
  • cliff -To jest stosunkowo nowy projekt Doug Hellmann (członek PSF, virtualenvwrapper autor, ogólną hakera nadzwyczajny) jest nieco więcej niż tylko analizatorem argumentów, ale jest zaprojektowany od podstaw, aby obsługiwać komendy wielopoziomowe
  • clint - Inny projekt, który ma być "analizą argumentów i więcej", ten autorstwa Kennetha Reitza (z Żądania sławy).
+0

Wow - nie zdawałem sobie sprawy, że jest tak wiele opcji - myślę, że to prawdopodobnie dlatego, że zawsze byłem w stanie przekonać Argarse'a do uległości. +1 dla znalezienia tych wszystkich ... – mgilson

+0

Zrobiłem szybkie dochodzenie, docopt i clint nie robią Klif to bardzo dziwna i złożona rzecz, której nie chcę rozumieć opster wygląda interesująco, próbując tego – Ronny

+0

opster nie zrobię jak dobrze – Ronny

3

Można łatwo dodać ten argument do obu analizatorów składni (parser i główny podpolecenia parser):

import argparse                 

main = argparse.ArgumentParser()              
subparser = main.add_subparsers().add_parser('test')           

for p in [main,subparser]:                 
    p.add_argument('--arg', action='store_true')         

print main.parse_args('--arg test'.split()).arg          
print main.parse_args('test --arg'.split()).arg 

Edit: Jak @hpaulj wskazał w komentarzu, istnieje również argument parents, który można przekazać do konstruktora ArgumentParser lub do metody add_parser. Możesz podać w tych parserach wartości, które są podstawą dla nowych.

import argparse 

base = argparse.ArgumentParser(add_help=False) 
base.add_argument('--arg', action='store_true') 

main = argparse.ArgumentParser(parents=[base]) 
subparser = main.add_subparsers().add_parser('test', parents=[base]) 

print main.parse_args('--arg test'.split()).arg 
print main.parse_args('test --arg'.split()).arg 

więcej przykładów/docs:

looking for best way of giving command line arguments in python, where some params are req for some option and some params are req for other options

Python argparse - Add argument to multiple subparsers (nie jestem pewien, czy to pytanie nie jest nakładające się z tego za dużo)

http://docs.python.org/dev/library/argparse.html#parents

+1

Jeśli było więcej opcji, można je zdefiniować w "macierzystym" parserze i dodać zarówno do "p" i "s" za pomocą parametru "parent". http://stackoverflow.com/a/18346152/901925 ma przykład dodawania tego samego zestawu opcji do wielu akapitów. – hpaulj

+0

@hpaulj - Myślę, że to jeszcze lepsza odpowiedź! Zamierzam zaktualizować moją odpowiedź ... – paluh