¿Cómo se obtiene argparse para elegir un subparser predeterminado?

Tengo el siguiente código en script.py :

 import argparse parser = argparse.ArgumentParser() sp = parser.add_subparsers(dest='command') sp.default = 'a' a_parser = sp.add_parser('a') b_parser = sp.add_parser('b') a_parser.add_argument('--thing', default='thing') b_parser.add_argument('--nothing', default='nothing') args = parser.parse_args() print(args) 

Puedo llamar a esto de tres maneras diferentes:

 $ python3 script.py Namespace(command='a') $ python3 script.py a Namespace(command='a', thing='thing') $ python3 script.py b Namespace(command='b', nothing='nothing') 

Solo hay un problema con esto: lo que quiero es que si proporciono cero argumentos en la línea de comandos es que a_parser sea ​​el que termine de analizar y hacer cosas. Claramente no lo es, el sp.default simplemente está configurando command='a' , no lo que espero, es decir, “Oh, sí, el usuario no proporcionó ningún argumento en la línea de comandos, pero sé que esto debería puede ser procesado por a_parser . Aquí está el Namespace(command='a', thing='thing') de Namespace(command='a', thing='thing') ! ”

¿Hay alguna manera de que pueda hacer esto con argparse? He buscado algunas opciones diferentes, pero ninguna de ellas parece proporcionar lo que estoy buscando. Supongo que podría hacer un poco de jiggery haciendo 3 ArgumentParsers distintos y luego pasar los argumentos a cada uno de ellos, aunque eso suena un poco grave.

¿Alguna opción mejor?

Primero, una nota histórica: los subparsers no eran opcionales y aún no están en Python2. El hecho de que sean opcionales en Py3 es una especie de error que se introdujo hace varios años. Hubo un cambio en la prueba para los argumentos requeridos, y los subparsers (una especie de posición) cayeron por las grietas. Si se hace correctamente, debería haber establecido explícitamente los subparsers como no necesarios.

Los subparadores no se comportan como otros argumentos no requeridos, los que tienen nargs='?' o marcado sin el parámetro required .

En cualquier caso, su sp.default define el valor que se pondrá en el command dest, pero no a_parser el uso de a_parser . Ese command='a' nunca se ‘evalúa’.

El uso de parse_known_args podría permitirle devaluar las cadenas restantes con a_parser .

Sin ningún argumento, podemos hacer:

 In [159]: args, extras = parser.parse_known_args([]) In [160]: args Out[160]: Namespace(command='a') In [161]: extras Out[161]: [] 

Luego ejecute condicionalmente a_parser (si el comando es ‘a’ pero no ‘cosa’)

 In [163]: a_parser.parse_args(extras,namespace=args) Out[163]: Namespace(command='a', thing='thing') 

Pero si bash incluir un valor --thing :

 In [164]: args, extras = parser.parse_known_args('--thing ouch'.split()) usage: ipython3 [-h] {a,b} ... ipython3: error: argument command: invalid choice: 'ouch' (choose from 'a', 'b') 

Intenta analizar el ‘ouch’ como nombre del subparser. El analizador principal no sabe nada sobre el argumento --thing .

Como expliqué en la otra pregunta argparse hoy, el analizador de alto nivel analiza las entradas hasta que encuentra algo que se ajuste al comando ‘subparsers’ (o en este ejemplo genera un error). Luego pasa el análisis al subparser. No se reanuda el análisis después.

Agregar argumentos argparse de nivel superior después de subparsers args

Cómo establecer un subparser predeterminado usando el módulo Argparse con Python 2.7

Mi respuesta a esta solicitud Py2 podría funcionar para usted. Primero ejecuto un parse_known_args con un analizador que no tiene subparsers, y condicionalmente ejecuto un segundo analizador que maneja los subparsers.

 In [165]: firstp = argparse.ArgumentParser() In [166]: args, extras = firstp.parse_known_args('--thing ouch'.split()) In [167]: args Out[167]: Namespace() 

Si los extras no tienen ‘a’ o ‘b’, llame a_parser (o simplemente mire a sys.argv[1:] directamente):

 In [168]: extras Out[168]: ['--thing', 'ouch'] In [169]: a_parser.parse_args(extras) Out[169]: Namespace(thing='ouch') 

O modifique los extras para incluir el comando de subparser faltante:

 In [170]: extras = ['a']+extras In [171]: parser.parse_args(extras) Out[171]: Namespace(command='a', thing='ouch') 

En cualquier caso, los subparsers optional no están bien desarrollados en argparse . Es el efecto secundario de un cambio realizado hace un tiempo, en lugar de una característica bien pensada.