我在Python 2.7中使用argparse来解析输入选项。我的选项之一是多项选择。我想在它的帮助文本中做一个列表。

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()

然而,argparse会删除所有换行和连续的空格。结果如下所示

~/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

如何在帮助文本中插入换行?


当前回答

尝试使用RawTextHelpFormatter来保存所有的格式:

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

它类似于RawDescriptionHelpFormatter,但不是只应用于描述和epilog, RawTextHelpFormatter也应用于所有帮助文本(包括参数)。

其他回答

我承认我发现这是一个非常令人沮丧的经历,因为我看到很多解决方案,我在网上看到很多次这样的问题。但我发现这些解决方案对我来说太复杂了,我想分享我最简单的解决方案。

下面是演示的脚本:

#!/usr/bin/python3
import textwrap
from argparse import ArgumentParser, HelpFormatter

class RawFormatter(HelpFormatter):
    def _fill_text(self, text, width, indent):
        return "\n".join([textwrap.fill(line, width) for line in textwrap.indent(textwrap.dedent(text), indent).splitlines()])

program_descripton = f'''
    FunkyTool v1.0

    Created by the Funky Guy on January 1 2020
    Copyright 2020. All rights reserved.

    Licensed under The Hippocratic License 2.1
    https://firstdonoharm.dev/

    Distributed on an "AS IS" basis without warranties
    or conditions of any kind, either express or implied.

    USAGE:
    '''

parser = ArgumentParser(description=program_descripton, formatter_class=RawFormatter)
args = parser.parse_args()

下面是它在test.py中的样子:

$ ./test.py --help
usage: test.py [-h]

FunkyTool v1.0

Created by the Funky Guy on January 1 2020
Copyright 2020. All rights reserved.

Licensed under The Hippocratic License 2.1
https://firstdonoharm.dev/

Distributed on an "AS IS" basis without warranties
or conditions of any kind, either express or implied.

USAGE:

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

所以,所有原始描述中的基本格式都被整齐地保留了下来,我们不得不,唉,使用一个自定义格式化器,但它是一个联机程序。它可以写得更清楚:

class RawFormatter(HelpFormatter):
    def _fill_text(self, text, width, indent):
        text = textwrap.dedent(text)          # Strip the indent from the original python definition that plagues most of us.
        text = textwrap.indent(text, indent)  # Apply any requested indent.
        text = text.splitlines()              # Make a list of lines
        text = [textwrap.fill(line, width) for line in text] # Wrap each line 
        text = "\n".join(text)                # Join the lines again
        return text

但我自己更喜欢写在一行上。

我想在描述文本中有手动换行符,并自动换行;但是这里没有一个建议对我有用-所以我最终修改了答案中给出的SmartFormatter类;尽管argparse方法名不是一个公共API,但这是我拥有的(作为一个名为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()

这是它在2.7和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

我也遇到过类似的问题(Python 2.7.6)。我尝试使用RawTextHelpFormatter将描述部分分解成几行:

parser = ArgumentParser(description="""First paragraph 

                                       Second paragraph

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

options = parser.parse_args()

和有:

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

First paragraph 

                        Second paragraph

                        Third paragraph

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

所以RawTextHelpFormatter不是一个解决方案。因为它打印源代码中出现的描述,保留所有空白字符(为了可读性,我想在源代码中保留额外的制表符,但我不想全部打印它们。此外,raw格式化器不会在行太长时换行,例如超过80个字符)。

感谢@Anton,他给了我正确的方向。但是为了格式化描述部分,该解决方案需要稍作修改。

无论如何,需要自定义格式化器。我扩展了现有的HelpFormatter类,并重写了_fill_text方法,如下所示:

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

与来自argparse模块的原始源代码比较:

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)

在原始代码中,整个描述都被包装了。在上面的自定义格式化器中,整个文本被分割成几个块,每个块都是独立格式化的。

借助自定义格式化器:

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

options = parser.parse_args()

输出结果为:

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

First paragraph

Second paragraph

Third paragraph

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

另一种简单的方法是包含文本包。

例如,

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 ... '''))

通过这种方式,我们可以避免在每个输出行前面有很长的空白。

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 ...

如果你只是想覆盖一个选项,你不应该使用RawTextHelpFormatter。相反,子类化HelpFormatter并为应该处理“原始”的选项提供一个特殊的介绍(我使用“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)

并使用它:

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()

对.add_argument()的任何其他调用,其中帮助不是以R|开始的,将被正常包装。

这是我对argparse改进的一部分。完整的SmartFormatter也支持添加 所有选项的默认值,以及实用程序描述的原始输入。完整版 有它自己的_split_lines方法,这样对版本字符串所做的任何格式化都会被保留:

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