2010-10-04 26 views
222

Estoy usando argparse in Python 2.7 para analizar las opciones de entrada. Una de mis opciones es una opción múltiple. Quiero hacer una lista en su texto de ayuda, p.Python argparse: ¿Cómo insertar nueva línea en el texto de ayuda?

from argparse import ArgumentParser 

parser = ArgumentParser(description='test') 

parser.add_argument('-g', choices=['a', 'b', 'g', 'd', 'e'], default='a', 
    help="Some option, where\n" 
     " a = alpha\n" 
     " b = beta\n" 
     " g = gamma\n" 
     " d = delta\n" 
     " e = epsilon") 

parser.parse_args() 

Sin embargo, argparse tiras de cada nueva línea y espacios consecutivos. El resultado se ve como

 
~/Downloads:52$ python2.7 x.py -h 
usage: x.py [-h] [-g {a,b,g,d,e}] 

test 

optional arguments: 
    -h, --help  show this help message and exit 
    -g {a,b,g,d,e} Some option, where a = alpha b = beta g = gamma d = delta e 
        = epsilon 

¿Cómo insertar nuevas líneas en el texto de ayuda?

+0

No tengo Python 2.7 conmigo para poder probar mis ideas. ¿Qué hay de usar el texto de ayuda entre comillas triples ("" "" ""). ¿Las nuevas líneas sobreviven usando esto? – pyfunc

+3

@pyfunc: No. El stripping se realiza en tiempo de ejecución mediante 'argparse', no el intérprete, por lo que cambiar a' "" "..." "" 'no ayudará. – kennytm

Respuesta

255

Intente utilizar RawTextHelpFormatter:

from argparse import RawTextHelpFormatter 
parser = ArgumentParser(description='test', formatter_class=RawTextHelpFormatter) 
+4

Bien, gracias. ¿Es posible aplicarlo solo para 1 opción? – kennytm

+4

Creo que no es así. Podría crear una subclase, pero lamentablemente 'Solo el nombre de esta clase se considera una API pública. Todos los métodos proporcionados por la clase se consideran un detalle de implementación. Probablemente no sea una gran idea, aunque podría no importar, ya que 2.7 está destinado a ser el último 2.x python y se espera que refactorice muchas cosas para 3.x de todos modos. De hecho, estoy ejecutando 2.6 con 'argparse' instalado a través de' easy_install' para que la documentación no esté actualizada. – intuited

+3

Algunos enlaces: para [python 2.7] (http://docs.python.org/library/argparse.html#formatter-class), y [python 3. *] (http://docs.python.org/dev /library/argparse.html#formatter-class). El paquete 2.6 debería, de acuerdo con [su wiki] (http://code.google.com/p/argparse/), cumplir con el 2.7 oficial. Desde el documento: "Al pasar RawDescriptionHelpFormatter como formatter_class = indica que la descripción y epilog ya están formateados correctamente y no deben estar en línea" – Stefano

53

Si lo que desea es anular la opción, no se debe utilizar RawTextHelpFormatter. En lugar subclase el HelpFormatter y proporcionar una introducción especial para las opciones que deben ser manejadas "en bruto" (utilizo "R|rest of help"):

import argparse 

class SmartFormatter(argparse.HelpFormatter): 

    def _split_lines(self, text, width): 
     if text.startswith('R|'): 
      return text[2:].splitlines() 
     # this is the RawTextHelpFormatter._split_lines 
     return argparse.HelpFormatter._split_lines(self, text, width) 

y utilizarla:

from argparse import ArgumentParser 

parser = ArgumentParser(description='test', formatter_class=SmartFormatter) 

parser.add_argument('-g', choices=['a', 'b', 'g', 'd', 'e'], default='a', 
    help="R|Some option, where\n" 
     " a = alpha\n" 
     " b = beta\n" 
     " g = gamma\n" 
     " d = delta\n" 
     " e = epsilon") 

parser.parse_args() 

otras llamadas a .add_argument() donde el la ayuda no comienza con R| se envolverá de forma normal.

Esto es parte de my improvements on argparse. El SmartFormatter completo también admite agregar los valores predeterminados a todas las opciones y la entrada sin formato de la descripción de las utilidades. La versión completa tiene su propio método _split_lines, por lo que cualquier formateo realizado, p. cadenas de versión se conserva:

parser.add_argument('--version', '-v', action="version", 
        version="version...\n 42!") 
+0

Quiero hacer esto para un mensaje de versión, pero este SmartFormatter solo parece funcionar con texto de ayuda, no el texto de la versión especial. 'parser.add_argument ('- v', '--version', action = 'version', version = get_version_str())' ¿Es posible extenderlo a ese caso? –

+0

@mc_electron la versión completa de SmartFormatter también tiene su propia '_split_lines' y conserva los saltos de línea (no es necesario especificar" R | "al principio, si desea esa opción, aplique el método' _VersionAction .__ call__' – Anthon

+0

I ' No estoy completando la primera parte de tu comentario, aunque puedo ver en '_VersionAction .__ call__' que probablemente solo quiero' 'parser.exit (message = version)' en lugar de usar la versión formateada. ¿Hay alguna manera? para hacer eso sin liberar una copia parcheada de argparse, aunque? –

7

He enfrentado un problema similar (Python 2.7.6). He tratado de romper Descripción sección en varias líneas utilizando RawTextHelpFormatter:

parser = ArgumentParser(description="""First paragraph 

             Second paragraph 

             Third paragraph""", 
             usage='%(prog)s [OPTIONS]', 
             formatter_class=RawTextHelpFormatter) 

options = parser.parse_args() 

Y tengo:

 
usage: play-with-argparse.py [OPTIONS] 

First paragraph 

         Second paragraph 

         Third paragraph 

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

Así RawTextHelpFormatter no es una solución. Porque imprime la descripción tal como aparece en el código fuente, preservando todos los espacios en blanco (quiero mantener pestañas adicionales en mi código fuente para poder leerlas, pero no quiero imprimirlas todas. También el formateador sin procesar no ajusta la línea cuando está demasiado largo, más de 80 caracteres por ejemplo).

Gracias a @Anton que inspiró la dirección correcta above. Pero esa solución necesita una ligera modificación para formatear la sección descripción.

De todos modos, se necesita un formateador personalizado. Extendí HelpFormatter clase existente y hizo caso omiso de _fill_text método como este:

import textwrap as _textwrap 
class MultilineFormatter(argparse.HelpFormatter): 
    def _fill_text(self, text, width, indent): 
     text = self._whitespace_matcher.sub(' ', text).strip() 
     paragraphs = text.split('|n ') 
     multiline_text = '' 
     for paragraph in paragraphs: 
      formatted_paragraph = _textwrap.fill(paragraph, width, initial_indent=indent, subsequent_indent=indent) + '\n\n' 
      multiline_text = multiline_text + formatted_paragraph 
     return multiline_text 

Comparar con el código fuente original procedente de argparse módulo:

def _fill_text(self, text, width, indent): 
    text = self._whitespace_matcher.sub(' ', text).strip() 
    return _textwrap.fill(text, width, initial_indent=indent, 
             subsequent_indent=indent) 

En el código original toda la descripción se está envuelto.En el formateador personalizado anterior, el texto completo se divide en varios fragmentos y cada uno de ellos se formatea de forma independiente.

Así que con la ayuda de formateador personalizado:

parser = ArgumentParser(description= """First paragraph 
             |n        
             Second paragraph 
             |n 
             Third paragraph""", 
       usage='%(prog)s [OPTIONS]', 
       formatter_class=MultilineFormatter) 

options = parser.parse_args() 

la salida es:

 
usage: play-with-argparse.py [OPTIONS] 

First paragraph 

Second paragraph 

Third paragraph 

optional arguments: 
    -h, --help show this help message and exit 
+1

Esto es maravilloso, sucedió después de casi rendirme y contemplar simplemente volver a implementar el argumento de la ayuda por completo ... me salvó una buena cantidad de molestias. –

+2

subclases 'HelpFormatter' es problemático ya que los desarrolladores de argparse solo garantizan que el nombre de clase sobrevivirá en futuras versiones de argparse. Básicamente se han escrito un cheque en blanco para que puedan cambiar los nombres de los métodos si les conviene hacerlo. Encuentro esto frustrante; lo menos que pudieron haber hecho es exponer algunos métodos en la API. – MrMas

20

Otra forma fácil de hacerlo es incluir textwrap.

Por ejemplo,

import argparse, textwrap 
parser = argparse.ArgumentParser(description='some information', 
     usage='use "python %(prog)s --help" for more information', 
     formatter_class=argparse.RawTextHelpFormatter) 

parser.add_argument('--argument', default=somedefault, type=sometype, 
     help= textwrap.dedent('''\ 
     First line 
     Second line 
     More lines ... ''')) 

De esta manera, podemos evitar el espacio vacío larga delante de cada línea de salida.

usage: use "python your_python_program.py --help" for more information 

Prepare input file 

optional arguments: 
-h, --help   show this help message and exit 
--argument ARGUMENT 
         First line 
         Second line 
         More lines ... 
-1

Quería tener tanto saltos de línea manuales en el texto de la descripción, como el ajuste automático de la misma; pero ninguna de las sugerencias aquí funcionó para mí, así que terminé modificando la clase SmartFormatter que figura en las respuestas aquí; los problemas con los nombres de los métodos argparse no ser una API pública pesar de ello, aquí es lo que tengo (como un archivo llamado test.py):

import argparse 
from argparse import RawDescriptionHelpFormatter 

# call with: python test.py -h 

class SmartDescriptionFormatter(argparse.RawDescriptionHelpFormatter): 
    #def _split_lines(self, text, width): # RawTextHelpFormatter, although function name might change depending on Python 
    def _fill_text(self, text, width, indent): # RawDescriptionHelpFormatter, although function name might change depending on Python 
    #print("splot",text) 
    if text.startswith('R|'): 
     paragraphs = text[2:].splitlines() 
     rebroken = [argparse._textwrap.wrap(tpar, width) for tpar in paragraphs] 
     #print(rebroken) 
     rebrokenstr = [] 
     for tlinearr in rebroken: 
     if (len(tlinearr) == 0): 
      rebrokenstr.append("") 
     else: 
      for tlinepiece in tlinearr: 
      rebrokenstr.append(tlinepiece) 
     #print(rebrokenstr) 
     return '\n'.join(rebrokenstr) #(argparse._textwrap.wrap(text[2:], width)) 
    # this is the RawTextHelpFormatter._split_lines 
    #return argparse.HelpFormatter._split_lines(self, text, width) 
    return argparse.RawDescriptionHelpFormatter._fill_text(self, text, width, indent) 

parser = argparse.ArgumentParser(formatter_class=SmartDescriptionFormatter, description="""R|Blahbla bla blah blahh/blahbla (bla blah-blabla) a blahblah bl a blaha-blah .blah blah 

Blah blah bla blahblah, bla blahblah blah blah bl blblah bl blahb; blah bl blah bl bl a blah, bla blahb bl: 

    blah blahblah blah bl blah blahblah""") 

options = parser.parse_args() 

Esto es cómo funciona en 2.7 y 3.4:

$ python test.py -h 
usage: test.py [-h] 

Blahbla bla blah blahh/blahbla (bla blah-blabla) a blahblah bl a blaha-blah 
.blah blah 

Blah blah bla blahblah, bla blahblah blah blah bl blblah bl blahb; blah bl 
blah bl bl a blah, bla blahb bl: 

    blah blahblah blah bl blah blahblah 

optional arguments: 
    -h, --help show this help message and exit 
Cuestiones relacionadas