Mam następujący kod (używając Python 2.7):

# shared command line options, like --version or --verbose
parser_shared = argparse.ArgumentParser(add_help=False)
parser_shared.add_argument('--version', action='store_true')

# the main parser, inherits from `parser_shared`
parser = argparse.ArgumentParser(description='main', parents=[parser_shared])

# several subcommands, which can't inherit from the main parser, since
# it would expect subcommands ad infinitum
subparsers = parser.add_subparsers('db', parents=[parser_shared])

...

args = parser.parse_args()

Teraz chciałbym zadzwonić do tego programu np. z --version dołączony do normalnego programu lub niektórych podkomenda:

$ prog --version
0.1

$ prog db --version
0.1

Zasadniczo muszę zadeklarować opcjonalne podszary. Jestem świadomy, że nie jest to naprawdę wspierany, ale czy są jakieś obejście lub alternatywy?

Edytuj: Komunikat o błędzie Dostaję:

$ prog db --version
# works fine

$ prog --version
usage: ....
prog: error: too few arguments
19
miku 15 grudzień 2011, 18:27

7 odpowiedzi

Najlepsza odpowiedź

Zgodnie z dokumentacją, --version za pomocą action='version' (a nie z action='store_true') Wydrukuje automatycznie numer wersji:

parser.add_argument('--version', action='version', version='%(prog)s 2.0')
18
eumiro 15 grudzień 2011, 14:29

FWIW, wpadłem w to również, i skończyłem się "rozwiązywać", nie używając podszarowych (już miałem swój własny system do drukowania pomocy, więc nic tam nie straciłeś).

Zamiast tego robię:

parser.add_argument("command", nargs="?",
                    help="name of command to execute")

args, subcommand_args = parser.parse_known_args()

... A potem podkomenda tworzy swój własny parser (podobny do subparder), który działa tylko na subcommand_args.

8
Matthew 26 luty 2014, 22:04

Wydaje się, że wdraża podstawową ideę opcjonalnego podszyciarza. Parse standardowe argumenty mają zastosowanie do wszystkich podkomendy. Następnie, jeśli coś pozostanie, wywołujemy parser na odpoczynek. Podstawowymi argumentami są rodzicem podkomisji, aby -H pojawia się poprawnie. Planuję wejść do interaktywnego monitu, jeśli nie występują podstępami.

import argparse

p1 = argparse.ArgumentParser( add_help = False )    
p1.add_argument( ‘–flag1′ )

p2 = argparse.ArgumentParser( parents = [ p1 ] )
s = p2.add_subparsers()
p = s.add_parser( ‘group’ )
p.set_defaults( group=True )

( init_ns, remaining ) = p1.parse_known_args( )

if remaining:
    p2.parse_args( args = remaining, namespace=init_ns )
else:
    print( ‘Enter interactive loop’ )

print( init_ns )
5
mikeporterinmd 4 kwiecień 2015, 01:15

Jak omówiono w http://bugs. pytahon.org/issue9253 (argparat: opcjonalne podszary), jak Python 3.3, podszary są teraz opcjonalne. Było to niezamierzone wynikiem zmiany sposobu, w jaki parse_args sprawdzono pod kątem wymaganych argumentów.

Znalazłem fudge, który przywraca poprzednie (wymagane subbarserzy) zachowanie, jawnie ustawiając atrybut required akcji {X1}}.

parser = ArgumentParser(prog='test')
subparsers = parser.add_subparsers()
subparsers.required = True   # the fudge
subparsers.dest = 'command'
subparser = subparsers.add_parser("foo", help="run foo")
parser.parse_args()

Zobacz ten problem, aby uzyskać więcej informacji. Spodziewam się, że jeśli i gdy ten problem zostanie odpowiednio załatany, subbarsery będą wymagane domyślnie, z pewnym opcją, aby ustawić atrybut {x0}} do False. Ale istnieje duże zaległości argparse poprawek.

4
hpaulj 27 luty 2014, 06:04

Tak, właśnie sprawdziłem svn, który jest używany jako przykład obiektu w Dokumentacja add_subparsers() i obsługuje tylko "--version" na głównym poleceniu:

python zacharyyoung$ svn log --version
Subcommand 'log' doesn't accept option '--version'
Type 'svn help log' for usage.

Nadal:

# create common parser
parent_parser = argparse.ArgumentParser('parent', add_help=False)
parent_parser.add_argument('--version', action='version', version='%(prog)s 2.0')

# create the top-level parser
parser = argparse.ArgumentParser(parents=[parent_parser])
subparsers = parser.add_subparsers()

# create the parser for the "foo" command
parser_foo = subparsers.add_parser('foo', parents=[parent_parser])

Co daje:

python zacharyyoung$ ./arg-test.py --version
arg-test.py 2.0
python zacharyyoung$ ./arg-test.py foo --version
arg-test.py foo 2.0
2
Zach Young 15 grudzień 2011, 17:15

Podczas gdy my Poczekaj na dostarczenie tej funkcji, możemy użyć takiego kodu:

# Make sure that main is the default sub-parser
if '-h' not in sys.argv and '--help' not in sys.argv:
    if len(sys.argv) < 2:
        sys.argv.append('main')
    if sys.argv[1] not in ('main', 'test'):
        sys.argv = [sys.argv[0], 'main'] + sys.argv[1:]
1
Joshua Richardson 13 listopad 2012, 23:21

Chociaż @ eumiro jest adresem odpowiedzi --version, może to zrobić tylko dlatego, że jest to specjalny przypadek dla Optparse. Aby umożliwić ogólne inwokacje:

 prog
 prog --verbose
 prog --verbose main
 prog --verbose db 

I mają prog --version Pracuj tak samo jak prog --verbose main (i prog main --verbose) Możesz dodać metodę do argumentuR i zadzwoń do nazwy domyślnej podstarze, tuż przed wywołaniem parse_args() :

import argparse
import sys

def set_default_subparser(self, name, args=None):
    """default subparser selection. Call after setup, just before parse_args()
    name: is the name of the subparser to call by default
    args: if set is the argument list handed to parse_args()

    , tested with 2.7, 3.2, 3.3, 3.4
    it works with 2.6 assuming argparse is installed
    """
    subparser_found = False
    for arg in sys.argv[1:]:
        if arg in ['-h', '--help']:  # global help if no subparser
            break
    else:
        for x in self._subparsers._actions:
            if not isinstance(x, argparse._SubParsersAction):
                continue
            for sp_name in x._name_parser_map.keys():
                if sp_name in sys.argv[1:]:
                    subparser_found = True
        if not subparser_found:
            # insert default in first position, this implies no
            # global options without a sub_parsers specified
            if args is None:
                sys.argv.insert(1, name)
            else:
                args.insert(0, name)

argparse.ArgumentParser.set_default_subparser = set_default_subparser

def do_main(args):
    print 'main verbose', args.verbose

def do_db(args):
    print 'db verbose:', args.verbose

parser = argparse.ArgumentParser()
parser.add_argument('--verbose', action='store_true')
parser.add_argument('--version', action='version', version='%(prog)s 2.0')
subparsers = parser.add_subparsers()
sp = subparsers.add_parser('main')
sp.set_defaults(func=do_main)
sp.add_argument('--verbose', action='store_true')
sp = subparsers.add_parser('db')
sp.set_defaults(func=do_db)

parser.set_default_subparser('main')
args = parser.parse_args()

if hasattr(args, 'func'):
    args.func(args)

Metoda set_default_subparser() jest częścią Ruamel.std.argparse pakiet.

0
Anthon 15 październik 2014, 09:43