the parser following example error argumentparser argument arg_parser are add_argument python parsing command-line-arguments argparse subcommand

python - parser - Subcomando predeterminado, o manejo de subcomandos con argparse



python arguments example (7)

¿Cómo puedo tener un sub-command predeterminado, o manejar el caso donde no se da un argparse usando argparse ?

import argparse a = argparse.ArgumentParser() b = a.add_subparsers() b.add_parser(''hi'') a.parse_args()

Aquí me gustaría que se seleccionara un comando, o que los argumentos se manejaran solo en función del siguiente nivel de analizador (en este caso, el analizador de nivel superior).

joiner@X:~/src> python3 default_subcommand.py usage: default_subcommand.py [-h] {hi} ... default_subcommand.py: error: too few arguments


Aquí hay una manera más agradable de agregar un método set_default_subparser :

if cmd in [None, ''hi'']: print(''command "hi"'')


En Python 2.7, puedes anular el comportamiento de error en una subclase (una pena que no haya una forma mejor de diferenciar el error):

import argparse class ExceptionArgParser(argparse.ArgumentParser): def error(self, message): if "invalid choice" in message: # throw exception (of your choice) to catch raise RuntimeError(message) else: # restore normal behaviour super(ExceptionArgParser, self).error(message) parser = ExceptionArgParser() subparsers = parser.add_subparsers(title=''Modes'', dest=''mode'') default_parser = subparsers.add_parser(''default'') default_parser.add_argument(''a'', nargs="+") other_parser = subparsers.add_parser(''other'') other_parser.add_argument(''b'', nargs="+") try: args = parser.parse_args() except RuntimeError: args = default_parser.parse_args() # force the mode into namespace setattr(args, ''mode'', ''default'') print args


En Python 3.2 (y 2.7) obtendrá ese error, pero no en 3.3 y 3.4 (sin respuesta). Por lo tanto, en 3.3 / 3.4 puede probar que parsed_args sea ​​un Namespace vacío.

Una solución más general es agregar un método set_default_subparser() (tomado del paquete ruamel.std.argparse ) y llamar a ese método justo antes de parse_args() :

import argparse import sys def set_default_subparser(self, name, args=None, positional_args=0): """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 last position before global positional # arguments, this implies no global options are specified after # first positional argument if args is None: sys.argv.insert(len(sys.argv) - positional_args, name) else: args.insert(len(args) - positional_args, name) argparse.ArgumentParser.set_default_subparser = set_default_subparser def do_hi(): print(''inside hi'') a = argparse.ArgumentParser() b = a.add_subparsers() sp = b.add_parser(''hi'') sp.set_defaults(func=do_hi) a.set_default_subparser(''hi'') parsed_args = a.parse_args() if hasattr(parsed_args, ''func''): parsed_args.func()

Esto funcionará con 2.6 (si argparse está instalado desde PyPI), 2.7, 3.2, 3.3, 3.4. Y te permite hacer ambas cosas.

python3 default_subcommand.py

y

python3 default_subcommand.py hi

Con el mismo efecto.

Permitiendo elegir un nuevo subparser por defecto, en lugar de uno de los existentes.

La primera versión del código permite configurar uno de los subparsers definidos previamente como predeterminado. La siguiente modificación permite agregar un nuevo subparser predeterminado, que luego podría usarse para procesar específicamente el caso cuando el usuario no seleccionó un subparser (diferentes líneas marcadas en el código)

def set_default_subparser(self, name, args=None, positional_args=0): """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 existing_default = False # check if default parser previously defined 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 sp_name == name: # check existance of default parser existing_default = True if not subparser_found: # If the default subparser is not among the existing ones, # create a new parser. # As this is called just before ''parse_args'', the default # parser created here will not pollute the help output. if not existing_default: for x in self._subparsers._actions: if not isinstance(x, argparse._SubParsersAction): continue x.add_parser(name) break # this works OK, but should I check further? # insert default in last position before global positional # arguments, this implies no global options are specified after # first positional argument if args is None: sys.argv.insert(len(sys.argv) - positional_args, name) else: args.insert(len(args) - positional_args, name) argparse.ArgumentParser.set_default_subparser = set_default_subparser a = argparse.ArgumentParser() b = a.add_subparsers(dest =''cmd'') sp = b.add_parser(''hi'') sp2 = b.add_parser(''hai'') a.set_default_subparser(''hey'') parsed_args = a.parse_args() print(parsed_args)

La opción "predeterminada" aún no aparecerá en la ayuda:

python test_parser.py -h usage: test_parser.py [-h] {hi,hai} ... positional arguments: {hi,hai} optional arguments: -h, --help show this help message and exit

Sin embargo, ahora es posible diferenciar y manejar por separado llamando a uno de los subparsers proporcionados, y llamando al subparser predeterminado cuando no se proporcionó ningún argumento:

$ python test_parser.py hi Namespace(cmd=''hi'') $ python test_parser.py Namespace(cmd=''hey'')


Para referencia posterior:

... b = a.add_subparsers(dest=''cmd'') b.set_defaults(cmd=''hey'') # <-- this makes hey as default b.add_parser(''hi'')

así, estos dos serán iguales:

  • python main.py hey
  • python main.py

Parece que he encontrado la solución al final yo mismo.

Si el comando es opcional, esto hace que el comando sea una opción . En mi configuración de analizador original, tenía un comando de package que podía tomar una serie de pasos posibles, o realizaría todos los pasos si no se daba ninguno. Esto hace que el paso sea una elección:

parser = argparse.ArgumentParser() command_parser = subparsers.add_parser(''command'') command_parser.add_argument(''--step'', choices=[''prepare'', ''configure'', ''compile'', ''stage'', ''package'']) ...other command parsers parsed_args = parser.parse_args() if parsed_args.step is None: do all the steps...


Puede agregar un argumento con un valor predeterminado que se usará cuando no se establezca nada, creo.

Vea esto: http://docs.python.org/dev/library/argparse.html#default

Editar:

Lo siento, leí tu pregunta un poco rápido.

No creo que tengas una forma directa de hacer lo que quieres a través de argparse. Pero puede verificar la longitud de sys.argv y, si su longitud es 1 (solo el nombre del script), podría pasar manualmente los parámetros predeterminados para el análisis, haciendo algo como esto:

import argparse a = argparse.ArgumentParser() b = a.add_subparsers() b.add_parser(''hi'') if len(sys.argv) == 1: a.parse_args([''hi'']) else: a.parse_args()

Creo que debería hacer lo que quieras, pero estoy de acuerdo en que sería bueno tener esto fuera de la caja.


Tal vez lo que estás buscando es el argumento dest de add_subparsers :

( Advertencia: funciona en Python 3.4, pero no en 2.7 )

class DefaultSubcommandArgParse(argparse.ArgumentParser): __default_subparser = None def set_default_subparser(self, name): self.__default_subparser = name def _parse_known_args(self, arg_strings, *args, **kwargs): in_args = set(arg_strings) d_sp = self.__default_subparser if d_sp is not None and not {''-h'', ''--help''}.intersection(in_args): for x in self._subparsers._actions: subparser_found = ( isinstance(x, argparse._SubParsersAction) and in_args.intersection(x._name_parser_map.keys()) ) if subparser_found: break else: # insert default in first position, this implies no # global options without a sub_parsers specified arg_strings = [d_sp] + arg_strings return super(DefaultSubcommandArgParse, self)._parse_known_args( arg_strings, *args, **kwargs )

Ahora solo puedes usar el valor de cmd :

import argparse parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest=''cmd'') parser_hi = subparsers.add_parser(''hi'') parser.parse_args([]) # Namespace(cmd=None)