Python argparse: ¿Cómo insertar nueva línea en el texto de ayuda?
Pregunta
Estoy usando argparse
en 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, por ejemplo.
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 parece a
~/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 saltos de línea en el texto de ayuda?
Solución
Trate de usar RawTextHelpFormatter
:
from argparse import RawTextHelpFormatter
parser = ArgumentParser(description='test', formatter_class=RawTextHelpFormatter)
Otros consejos
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 utilizarlo:
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()
Las demás llamadas a .add_argument()
donde la ayuda no se inicia con R|
será envuelto con normalidad.
Esto es parte de mis mejoras en argparse . El SmartFormatter completo también admite la adición
los valores por defecto a todas las opciones, y la entrada bruta de la descripción utilidades. La versión completa
tiene su propio método _split_lines
, de modo que ningún formato hacer para, por ejemplo, cadenas de versión se conserva:
parser.add_argument('--version', '-v', action="version",
version="version...\n 42!")
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, se puede evitar el largo espacio vacío 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 ...
He enfrentado problema similar (Python 2.7.6). He tratado de romper descripció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 tiene:
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. Debido a que imprime la descripción tal como aparece en el código fuente, la preservación de espacios en blanco (que quiero mantener las pestañas adicionales en mi código fuente para facilitar la lectura, pero no quiero imprimir a todos. También formateador prima no se ajusta lineal cuando se demasiado largo, más de 80 caracteres, por ejemplo).
Gracias a @Anton que inspiraron la dirección anterior . Pero esa solución debe ligera modificación con vistas a formato descripción .
En cualquier caso, se necesita formateador personalizado. Me extendió la clase existente y el método HelpFormatter
_fill_text
overrode como esto:
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 está siendo envuelto toda la descripción. En formateador personalizado por encima de todo el texto se divide en varias partes, y cada uno de ellos tiene el formato 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
Yo quería tener dos saltos de línea manuales en el texto de la descripción y de envolver automática de la misma; pero ninguna de las sugerencias aquí trabajado para mí - así que acabé modificando la clase SmartFormatter dada 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
A partir de SmartFomatter descrito anteriormente, terminé a esa solución:
class SmartFormatter(argparse.HelpFormatter):
'''
Custom Help Formatter used to split help text when '\n' was
inserted in it.
'''
def _split_lines(self, text, width):
r = []
for t in text.splitlines(): r.extend(argparse.HelpFormatter._split_lines(self, t, width))
return r
Tenga en cuenta que el argumento extrañamente formatter_class pasado al analizador de nivel superior no se inheritated por sub_parsers, uno debe pasar de nuevo por cada sub_parser creado.