Argparse: ¿Cómo incluir valores predeterminados en '--help'?

307

Supongamos que tengo el siguiente fragmento de argumento:

diags.cmdln_parser.add_argument( '--scan-time',
                     action  = 'store',
                     nargs   = '?',
                     type    = int,
                     default = 5,
                     help    = "Wait SCAN-TIME seconds between status checks.")

Actualmente, --helpdevuelve:

usage: connection_check.py [-h]
                             [--version] [--scan-time [SCAN_TIME]]

          Test the reliability/uptime of a connection.



optional arguments:
-h, --help            show this help message and exit
--version             show program's version number and exit
--scan-time [SCAN_TIME]
                    Wait SCAN-TIME seconds between status checks.

Preferiría algo como:

--scan-time [SCAN_TIME]
                    Wait SCAN-TIME seconds between status checks.
                    (Default = 5)

Echar un vistazo al código del formateador de ayuda reveló opciones limitadas. ¿Hay una manera inteligente argparsede imprimir el valor predeterminado de --scan-timemanera similar, o debería subclasificar el helpformateador?

JS.
fuente
55
Te puede interesar docopt . Nunca he vuelto a mirar argparse.
Paulo Scardine
14
@PauloScardine: estar integrado en el lenguaje es un beneficio importante para argparse.
jordanm
1
@PauloScardine: Extraer una biblioteca no estándar en mi proyecto actual será realmente un dolor, pero seguro que me gusta el aspecto de la salida de docopt. ¡Gracias por el consejo!
JS.
@JS. dices "Extraer una biblioteca no estándar en mi proyecto actual será realmente un dolor" ¿En serio? Hay muchas bibliotecas muy útiles en pypi. En mi contexto, es fácil extraer una biblioteca no estándar. Es triste, si es difícil en su contexto.
guettli
3
@guettli: Ese proyecto fue para un proyecto comercial integrado. Tienes razón la instalación fue fácil. Obtener la aprobación de los abogados corporativos fue una pesadilla.
JS.

Respuestas:

447

Usa el argparse.ArgumentDefaultsHelpFormatterformateador :

parser = argparse.ArgumentParser(
    # ... other options ...
    formatter_class=argparse.ArgumentDefaultsHelpFormatter)

Para citar la documentación:

La otra clase de formateador disponible, ArgumentDefaultsHelpFormatteragregará información sobre el valor predeterminado de cada uno de los argumentos.

Tenga en cuenta que esto solo se aplica a los argumentos que tienen texto de ayuda definido ; sin helpvalor para un argumento, no hay ningún mensaje de ayuda para agregar información sobre el valor por defecto a .

La salida exacta para su opción de tiempo de escaneo se convierte en:

  --scan-time [SCAN_TIME]
                        Wait SCAN-TIME seconds between status checks.
                        (default: 5)
Martijn Pieters
fuente
12
¿Puedo controlar que solo los argumentos con un valor explícito default=muestren el valor predeterminado? Como no me gustan los textos 'por defecto: ninguno'.
ziyuang
14
Puede establecer el defaultque SUPPRESS: default=argparse.SUPPRESS. Tenga en cuenta que en ese caso no se agregará ningún atributo al resultado del espacio de nombres si se omitió ese argumento, consulte la defaultdocumentación .
Martijn Pieters
44
Tenga en cuenta que también debe especificar esto para cada subparser creado.
KomodoDave
1
Luego cree una nueva pregunta, que incluya un ejemplo reproducible mínimo que demuestre el problema. Como dije, me funciona con Python 2.7.
Martijn Pieters
3
@David Estaba teniendo el mismo problema. Agregue el helpargumento add_argumenty eso debería funcionar.
Pablo Díaz Ogni
190

Agregue '%(default)s'al parámetro de ayuda para controlar lo que se muestra.

parser.add_argument("--type", default="toto", choices=["toto","titi"],
                              help = "type (default: %(default)s)")
polux.moon
fuente
99
Me gusta esta opción porque ya había usado format_class = argparse.RawTestHelpFormatter y no tenía ganas de tirarme un pedo con OOP.
mqsoh
23
No olvide incluir la variable 'tipo' en su cadena de formato; por ejemplo, '% (predeterminado) s' para una cadena o '% (predeterminado) d' para un dígito.
strongMA
1
Esta solución me gusta mucho más, es mucho más simple y no tengo que manejar explícitamente argumentos sin valores predeterminados.
void.pointer
@mqsoh la herencia múltiple en realidad solo funcionó, pero desafortunadamente no es API pública: stackoverflow.com/a/52025430/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
Me gusta esto porque cambiar la clase de formateador agrega un montón de "(predeterminado: Ninguno)" en todas partes, lo que está abarrotando la ayuda.
6005
11

Clase de envoltura

Este es el enfoque SECO más confiable que he encontrado hasta ahora para mostrar los valores predeterminados y utilizar otro formateador, como argparse.RawTextHelpFormatteral mismo tiempo:

#!/usr/bin/env python3

import argparse

class ArgumentParserWithDefaults(argparse.ArgumentParser):
    def add_argument(self, *args, help=None, default=None, **kwargs):
        if help is not None:
            kwargs['help'] = help
        if default is not None and args[0] != '-h':
            kwargs['default'] = default
            if help is not None:
                kwargs['help'] += ' Default: {}'.format(default)
        super().add_argument(*args, **kwargs)

parser = ArgumentParserWithDefaults(
    formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument('-a', default=13, help='''my help
for a''')
parser.add_argument('-b', default=42, help='''my help
for b''')
parser.add_argument('--no-default', help='''my help
for no-default''')
parser.add_argument('--no-help', default=101)

parser.print_help()
print()
print(parser.parse_args())

Salida:

usage: main.py [-h] [-a A] [-b B] [--no-default NO_DEFAULT]
               [--no-help NO_HELP]

optional arguments:
  -h, --help            show this help message and exit
  -a A                  my help
                        for a Default: 13
  -b B                  my help
                        for b Default: 42
  --no-default NO_DEFAULT
                        my help
                        for no-default
  --no-help NO_HELP

Namespace(a=13, b=42, no_default=None, no_help=101)

ArgumentDefaultsHelpFormatter+ RawTextHelpFormatterherencia múltiple

La herencia múltiple simplemente funciona, pero no parece ser una API pública:

#!/usr/bin/env python3

import argparse

class RawTextArgumentDefaultsHelpFormatter(
        argparse.ArgumentDefaultsHelpFormatter,
        argparse.RawTextHelpFormatter
    ):
        pass

parser = argparse.ArgumentParser(
    formatter_class=RawTextArgumentDefaultsHelpFormatter
)
parser.add_argument('-a', default=13, help='''my help
for a''')
parser.add_argument('-b', default=42, help='''my help
for b''')
parser.print_help()

Salida:

usage: a.py [-h] [-a A] [-b B]

optional arguments:
  -h, --help  show this help message and exit
  -a A        my help
              for a (default: 13)
  -b B        my help
              for b (default: 42)

Simplemente funciona, porque como podemos ver trivialmente de las fuentes https://github.com/python/cpython/blob/v3.6.5/Lib/argparse.py#L648 que:

  • RawTextHelpFormatter implementos _split_lines
  • ArgumentDefaultsHelpFormatter implementos _get_help_string

así que podemos adivinar que funcionarán bien juntos.

Sin embargo, esto no parece ser una API pública, y tampoco lo son los métodos formatter_class, por lo que no creo que haya una forma de API pública para hacerlo actualmente. argparsedocstring dice:

Todas las otras clases en este módulo se consideran detalles de implementación. (Tenga en cuenta también que HelpFormatter y RawDescriptionHelpFormatter solo se consideran públicos como nombres de objeto; la API de los objetos del formateador todavía se considera un detalle de implementación).

Consulte también: Personalizar el mensaje de ayuda de argparse

Probado en Python 3.6.5.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1
Excelente ! Finalmente, imprima tanto la cadena de documentación formateada como los argumentos predeterminados. Gracias
Sylvain