Argparse: ¿Argumentos requeridos enumerados en “argumentos opcionales”?

9 minutos de lectura

avatar de usuario
muerte

Uso el siguiente código simple para analizar algunos argumentos; tenga en cuenta que uno de ellos es obligatorio. Desafortunadamente, cuando el usuario ejecuta el script sin proporcionar el argumento, el texto de uso/ayuda que se muestra no indica que haya un argumento no opcional, lo que me parece muy confuso. ¿Cómo puedo hacer que Python indique que un argumento no es opcional?

Aquí está el código:

import argparse
if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Foo')
    parser.add_argument('-i','--input', help='Input file name', required=True)
    parser.add_argument('-o','--output', help='Output file name', default="stdout")
    args = parser.parse_args()
    print ("Input file: %s" % args.input )
    print ("Output file: %s" % args.output )

Cuando ejecuto el código anterior sin proporcionar el argumento requerido, obtengo el siguiente resultado:

usage: foo.py [-h] -i INPUT [-o OUTPUT]

Foo

optional arguments:
    -h, --help            show this help message and exit
    -i INPUT, --input INPUT
                          Input file name
    -o OUTPUT, --output OUTPUT
                          Output file name

  • En la línea de uso, el -i INPUT parte no está rodeada por corchetes, lo que sutilmente indica que, de hecho, es obligatorio. Además, puede explicar manualmente que a través de la help parámetro

    – Jaime Rodríguez-Guerra

    12 de junio de 2014 a las 9:15

  • @JaimeRGP Sí, pero eso no es suficiente, por supuesto, y también es menos que prominente. El nombre del grupo asignado optional arguments para los argumentos requeridos sigue siendo engañoso.

    – Asclepio

    5 de octubre de 2016 a las 6:13


avatar de usuario
dar un toque

Parámetros que comienzan con - o -- generalmente se consideran opcionales. Todos los demás parámetros son parámetros posicionales y como tales requeridos por diseño (como argumentos de funciones posicionales). Es posible requerir argumentos opcionales, pero esto va un poco en contra de su diseño. Dado que siguen siendo parte de los argumentos no posicionales, seguirán apareciendo bajo el encabezado confuso “argumentos opcionales”, incluso si son obligatorios. Sin embargo, los corchetes que faltan en la parte de uso muestran que, de hecho, son necesarios.

Véase también el documentación:

En general, el módulo argparse asume que indicadores como -f y –bar indican argumentos opcionales, que siempre se pueden omitir en la línea de comandos.

Nota: Las opciones requeridas generalmente se consideran de mala forma porque los usuarios esperan que las opciones sean opcionales y, por lo tanto, deben evitarse cuando sea posible.

Dicho esto, los encabezados “argumentos posicionales” y “argumentos opcionales” en la ayuda son generados por dos grupos de argumentos en los que los argumentos se separan automáticamente. Ahora, podría “hackearlo” y cambiar el nombre de los opcionales, pero una solución mucho más elegante sería crear otro grupo para “argumentos con nombre requeridos” (o como quiera llamarlos):

parser = argparse.ArgumentParser(description='Foo')
parser.add_argument('-o', '--output', help='Output file name', default="stdout")
requiredNamed = parser.add_argument_group('required named arguments')
requiredNamed.add_argument('-i', '--input', help='Input file name', required=True)
parser.parse_args(['-h'])
usage: [-h] [-o OUTPUT] -i INPUT

Foo

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        Output file name

required named arguments:
  -i INPUT, --input INPUT
                        Input file name

  • He estado teniendo el mismo problema. Probé tu solución. Agrega los argumentos al nuevo grupo, pero mi código no parece funcionar después de eso. Cualquier solución sería apreciada. Enlace a mi código – pastebin.com/PvC2aujz

    – Zarar Mahmud

    13 abr 2019 a las 19:45

  • @ZararMahmud: está pasando argumentos vacíos en la línea 24 de su código: parser.parse_args([]) En su lugar, utilice parser.parse_args() sin argumentos para capturar el contenido de sys.argv. Por análisis de argumentos

    – Devin

    12 jun 2019 a las 13:50

  • @poke: ¡Buena solución! Pero esto no ayuda en caso de que necesite grupos exclusivos mutuos, ¿o me estoy perdiendo algo?

    – Juez

    16 de enero de 2020 a las 15:40

  • @Judge recomendaría leer esto pymotw.com/3/argparse/#mutually-exclusive-options

    – Pedro Moore

    23 de abril de 2020 a las 13:16

  • @mrgloom Las opciones generalmente se definen con -- y un nombre preciso para explicar para qué sirven. un solo - luego se puede usar para definir un alias corto para las opciones de uso común. En mi ejemplo, podrías usar ambos --output out.txt o -o out.txt por exactamente lo mismo. los -o es solo un alias corto para --output. Algunas herramientas de línea de comandos también le permiten encadenar estos alias cortos. P.ej tar -xf archive.tar es corto para tar --extract --file=archive.tar.

    – dar un toque

    9 de julio de 2020 a las 22:39


avatar de usuario
karl rosaen

Dado que prefiero enumerar los argumentos obligatorios antes que los opcionales, los sorteo a través de:

parser = argparse.ArgumentParser()
parser._action_groups.pop()
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')
return parser.parse_args()

y esto da como resultado:

usage: main.py [-h] --required_arg REQUIRED_ARG [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  --optional_arg OPTIONAL_ARG

puedo vivir sin -h, --help apareciendo en el grupo de argumentos opcionales.

  • ¿Esto realmente obliga a argparse a tratar cualquiera de los argumentos según sea necesario?

    – Antonio

    12/10/2017 a las 19:41

  • Creo que el argumento ‘requerido’ aún debe establecerse al agregar un argumento.

    – Karl Rosaen

    13 oct 2017 a las 15:13

  • Eso es muy agradable.

    – Paul Cezanne

    12 de enero de 2018 a las 21:26

  • @Anthony: no, necesita el ‘requerido = Verdadero’ en add_argument para eso. La respuesta anterior solo ilustra la agrupación de argumentos.

    – usuario2275693

    28 de febrero de 2018 a las 16:10

  • Ahora, en la primera línea, parece que el argumento sería opcional, por lo contrario de lo que muestra OP. ¿Hay alguna posibilidad de que los argumentos requeridos se muestren antes que los opcionales Y no tengan corchetes?

    – isi

    30 sep 2020 a las 10:20

avatar de usuario
RalphyZ

Construyendo a partir de @Karl Rosaen

parser = argparse.ArgumentParser()
optional = parser._action_groups.pop() # Edited this line
required = parser.add_argument_group('required arguments')
# remove this line: optional = parser...
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')
parser._action_groups.append(optional) # added this line
return parser.parse_args()

y esto da como resultado:

usage: main.py [-h] [--required_arg REQUIRED_ARG]
           [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  -h, --help                    show this help message and exit
  --optional_arg OPTIONAL_ARG

  • Por cierto, ¿hay alguna manera (métodos) de cómo obtener acceso a _action_group sin acceder al miembro protegido? En mi caso, necesito agregar algún argumento al grupo ya existente (personalizado).

    – máquina

    22 de abril de 2017 a las 9:21


  • Esto es genial. Resuelve el elemento –help que aparece en una segunda lista opcional.

    – Jeremy

    18 de noviembre de 2017 a las 19:17

  • Nota: esta respuesta rompe la API expuesta, verifique la respuesta de Bryan_D a continuación.

    – jajaja

    2 de mayo de 2020 a las 13:26


Una vez más, construyendo a partir de @RalphyZ

Este no rompe la API expuesta.

from argparse import ArgumentParser, SUPPRESS
# Disable default help
parser = ArgumentParser(add_help=False)
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')

# Add back help 
optional.add_argument(
    '-h',
    '--help',
    action='help',
    default=SUPPRESS,
    help='show this help message and exit'
)
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')

Que mostrará lo mismo que arriba y debería sobrevivir a futuras versiones:

usage: main.py [-h] [--required_arg REQUIRED_ARG]
           [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  -h, --help                    show this help message and exit
  --optional_arg OPTIONAL_ARG

avatar de usuario
atti

NB: según Christophe Vu-Brugier, el siguiente código no funciona a partir de la versión 3.10 de Python.

por defecto hay 2 grupos de argumentos en parser._action_groups: argumentos posicionales y argumentos con nombre (titulados ‘argumentos opcionales’). puede agregar sus argumentos opcionales con nombre al grupo existente de ‘argumentos opcionales’ y los argumentos con nombre requeridos a un nuevo grupo de ‘argumentos requeridos’. Después de eso, puede reordenar los grupos:

import argparse

parser = argparse.ArgumentParser(description='Foo')

required = parser.add_argument_group('required arguments')

required.add_argument('-i','--input', help='Input file name', required=True)
parser.add_argument('-o','--output', help='Output file name', default="stdout")

groups_order = {
    'positional arguments': 0,
    'required arguments': 1,
    'optional arguments': 2
}
parser._action_groups.sort(key=lambda g: groups_order[g.title])

parser.parse_args(['-h'])

producción:

usage: argparse_argument_groups.py [-h] -i INPUT [-o OUTPUT]

Foo

required arguments:
  -i INPUT, --input INPUT
                        Input file name

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        Output file name

  • Usé su fragmento durante mucho tiempo y me sirvió bien. Gracias por eso. Sin embargo, observo que ya no funciona con Python 3.10 porque se cambió el nombre de “argumentos opcionales” a “opciones”. La ejecución de este código desencadena una excepción “KeyError: options” en Python 3.10. En consecuencia, decidí que era más seguro para mi proyecto eliminar la distinción entre argumentos “requeridos” y “opcionales”.

    – Christophe Vu-Brugier

    28 de junio a las 9:33

No es necesario anular el grupo opcional.

Solo haz:

parser = argparse.ArgumentParser()
required = parser.add_argument_group('required arguments')
required.add_argument('--required_arg', required=True)
# All arguments set via parser directly will automatically go to the optional group
parser.add_argument('--optional_arg')
parser.print_help()

imprimirá

usage: [-h] --required_arg REQUIRED_ARG [--optional_arg OPTIONAL_ARG]

optional arguments:
  -h, --help            show this help message and exit
  --optional_arg OPTIONAL_ARG

required arguments:
  --required_arg REQUIRED_ARG

Si desea tener argumentos obligatorios antes de opcionales, puede hacer lo siguiente:

parser = argparse.ArgumentParser()
optional = parser._action_groups.pop()
required = parser.add_argument_group('required arguments')
parser._action_groups.append(optional)
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')
parser.print_help()

que imprimirá grupos en el orden correcto:

usage: [-h] --required_arg REQUIRED_ARG [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  -h, --help            show this help message and exit
  --optional_arg OPTIONAL_ARG

  • Usé su fragmento durante mucho tiempo y me sirvió bien. Gracias por eso. Sin embargo, observo que ya no funciona con Python 3.10 porque se cambió el nombre de “argumentos opcionales” a “opciones”. La ejecución de este código desencadena una excepción “KeyError: opciones” en Python 3.10. En consecuencia, decidí que era más seguro para mi proyecto eliminar la distinción entre argumentos “requeridos” y “opcionales”.

    – Christophe Vu-Brugier

    28 de junio a las 9:33

avatar de usuario
Zheng

Los argumentos requeridos suelen ser argumentos “posicionales” cuando se usa argparse.

    parser = argparse.ArgumentParser(description="Foo")
    parser.add_argument("username")
    parser.add_argument("password")

Esto agregará dos argumentos posicionales que son necesarios.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad