2012-08-28 20 views
5

Chciałbym móc korzystać z opcji globalnych z obiektu argparse.ArgumentParser w celu przesłonięcia/zwiększenia wartości domyślnych dla pod-polecenia.Domyślne ustawienia podskładne argparse augment za pomocą opcji globalnych

Przykładem może być to, że wyświetlane pomoc odzwierciedla światowe nowości, czyli na poniższym przykładzie zabawki:

import argparse 
import os 
import sys 

# Global parser and options. 
parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter) 

parser.add_argument("--user", 
        dest="user", 
        default=os.environ.get("USER"), 
        help="Override the setting of the $USER variable.") 

# Sub-command parser and options. 
subparsers = parser.add_subparsers() 

command = subparsers.add_parser(
    "command", 
    formatter_class=argparse.ArgumentDefaultsHelpFormatter) 

command.add_argument("--config", 
        dest="config", 
        default="~%s/config" % os.environ.get("USER"), 
        help="The config file.") 

options = parser.parse_args() 

Idealnie podczas gdy uruchomię to w trybie pomocy byłoby uzyskać,

> python example.py --user thing command --help 
usage: example.py command [-h] [--config CONFIG] 

optional arguments: 
    -h, --help  show this help message and exit 
    --config CONFIG The config file. (default: ~thing/config) 

tj. Ścieżka do pliku konfiguracyjnego jest specyficzna dla użytkownika (rzecz). Zdaję sobie sprawę, że mogę zmienić domyślną konfigurację na "~%(user)s/config", a następnie rozwiązać ten problem w czasie wykonywania w przestrzeni nazw opcji, jednak chciałbym, aby pomoc była bardziej wyraźna.

wnoszę alternatywnym rozwiązaniem byłoby, aby spróbować przeanalizować argumenty raz, aby uzyskać opcje globalne, tzn

if "--help" in sys.argv: 

    # Parse the command minus the help to obtain the global options. 
    args = sys.argv[1:] 
    args.remove("--help") 

    # Update the defaults with the global options. 
    options = parser.parse_args(args) 
    command.set_defaults(config="~%s/config" % options.user) 

    # Re-parse the options. 
    parser.parse_args() 

Choć wydaje się to nieco hacky. Czy istnieje lepsze podejście/rozwiązanie?

Odpowiedz

4

Po zdefiniowaniu opcji globalnych, ale przed zdefiniowaniem podpoleceń należy zadzwonić pod numer parse_known_args, aby dowiedzieć się, jaka jest wartość --user. Następnie zakończ definiując polecenia akapert, używając wartości --user, aby zdefiniować wartość domyślną --config, przed wywoływaniem parse_args w celu przeanalizowania wszystkich opcji w wierszu poleceń.

To trochę różni się od twojej alternatywy, ale zachowuje wszystkie przetwarzanie linii poleceń wewnątrz obiektu argparse.

(I okrojone kod trochę tylko do skrócenia go do tej odpowiedzi.)

import os 
import argparse 

# Global preparser and options. 
preparser = argparse.ArgumentParser(add_help=False) 
preparser.add_argument("--user", dest="user", default=os.environ.get("USER")) 

# ****** NEW ******* 
options, _ = preparser.parse_known_args() # Ignore what we haven't defined yet 
user = options.user      # Use this to define the default of --config 

parser = argparse.ArgumentParser(parents=[ preparser ], add_help=True, 
        formatter_class=argparse.ArgumentDefaultsHelpFormatter) 

# Sub-command parser and options. 
subparsers = parser.add_subparsers() 

command = subparsers.add_parser("command") 

# ****** MODIFIED ******* 
command.add_argument("--config", dest="config", default="~%s/config" % (user,)) 

options = parser.parse_args() 
+0

Dzięki za odpowiedź. To podejście jest zdecydowanie znacznie czystsze/prostsze niż moje podejście i bardziej podobne do podejścia, które próbowałem bez argparse, tj. Dwukrotnie analizowałem argumenty. Mogę zajrzeć do kodu, aby zobaczyć, jak mogę uzyskać parse_known_args(), aby zwrócić z opcjami (w przeciwieństwie do wychodzenia), gdy podano argument "--help" zamiast usuwania go na mój przykład. – John

+0

Och, zapomniałem o tym. Możesz złapać wyjątek 'SystemExit', który' parse_known_args() 'wyrzuci, gdy wywoła' ArgumentParser.exit' (co jest względnie cienkim opakowaniem wokół 'sys.exit'). – chepner

+0

Hej, tak, próbowałem tego, jednak problem z try/except jest taki, że opcje są niezdefiniowane. – John

0

Oto rozwiązanie, które modyfikuje linię pomocy bezpośrednio, w czasie wykonywania. Mógłby również zmodyfikować os.environ lub inne globalne, ale utrzymam to prosto. Kluczem jest przypisanie akcji utworzonej przez add_argument('--config'...) do zmiennej. help to tylko atrybut tej zmiennej. Możesz to zmienić.

class FooAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     config.help = 'new user (%s)'% values 
     setattr(namespace, self.dest, values) 

parser = argparse.ArgumentParser() 
parser.add_argument('--user',action=FooAction) 
sub = parser.add_subparsers() 
cmd = sub.add_parser('cmd') 
config = cmd.add_argument('--config',help='initial help') 
config.help = 'default help' # alt way of setting 'help' 
# print config # to see other attributes of the config action 
args = parser.parse_args() 
print args 

Wykonano tylko z cmd -h otrzymujemy default help

$ python stack12167228.py cmd -h 
usage: stack12167228.py cmd [-h] [--config CONFIG] 

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

wywołany z --user xxx cmd -h, pomoc jest dostosowany

$ python stack12167228.py --user xxx cmd -h 
usage: stack12167228.py cmd [-h] [--config CONFIG] 

optional arguments: 
    -h, --help  show this help message and exit 
    --config CONFIG new user (xxx)