usage parser mutually metavar float exclusive example argumentparser argument add_argument python command-line-interface argparse subcommand

mutually - parser python example



argparse subparser opcional(para--version) (7)

Aunque la respuesta de @ eumiro aborda la opción --version , solo puede hacerlo porque es un caso especial para optparse. Para permitir invocaciones generales de:

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

y hacer que prog --version funcione de la misma manera que prog --verbose main (y prog main --verbose ) puede agregar un método a Argumentparser y llamar a ese con el nombre del subparser predeterminado, justo antes de invocar a 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)

El método set_default_subparser() es parte del paquete ruamel.std.argparse .

Tengo el siguiente código (usando 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()

Ahora me gustaría poder llamar a este programa, por ejemplo, con la --version adjunta al programa normal o algún subcomando:

$ prog --version 0.1 $ prog db --version 0.1

Básicamente, necesito declarar subparsers opcionales. Soy consciente de que esto no es realmente compatible , pero ¿hay soluciones alternativas o alternativas?

Editar: El mensaje de error que estoy recibiendo:

$ prog db --version # works fine $ prog --version usage: .... prog: error: too few arguments


Como se explica en http://bugs.python.org/issue9253 (argparse: subparsers opcionales), a partir de Python 3.3, los subparsers ahora son opcionales. Este fue un resultado no deseado de un cambio en la forma en que parse_args verificó los argumentos requeridos.

Encontré un fudge que restaura el comportamiento anterior (subparsers requeridos), estableciendo explícitamente el atributo required de la acción subparsers .

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

Ver ese tema para más detalles. Espero que si y cuando este problema se soluciona correctamente, se requerirán subparsers por defecto, con algún tipo de opción para establecer su atributo required en False . Pero hay un gran atraso de parches argparse .


De acuerdo con la documentación, --version with action=''version'' (y no con action=''store_true'' ) imprime automáticamente el número de versión:

parser.add_argument(''--version'', action=''version'', version=''%(prog)s 2.0'')


Esto parece implementar la idea básica de un subparser opcional. Analizamos los argumentos estándar que se aplican a todos los subcomandos. Entonces, si queda algo, invocamos el analizador sobre el resto. Los argumentos principales son un elemento principal del subcomando, por lo que la -h aparece correctamente. Planeo ingresar una solicitud interactiva si no hay subcomandos presentes.

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 )


FWIW, también me encontré con esto y terminé "resolviéndolo" al no usar subparsers (ya tenía mi propio sistema para imprimir ayuda, así que no perdí nada allí).

En su lugar, hago esto:

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

... y luego el subcomando crea su propio analizador (similar a un subparser) que opera solo en subcommand_args .


Mientras esperamos que se entregue esta función , podemos usar un código como este:

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


Sí, acabo de marcar svn , que se usa como un ejemplo de objeto en la documentación add_subparsers() , y solo admite ''--version'' en el comando principal:

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

Todavía:

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

Cuyos rendimientos:

python zacharyyoung$ ./arg-test.py --version arg-test.py 2.0 python zacharyyoung$ ./arg-test.py foo --version arg-test.py foo 2.0