我在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

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


当前回答

迟到了12年,但我也需要这个。

OP要求在帮助(不是描述)中的新行,因此这里的解决方案实际上并不完全有效,因为如果一行比屏幕宽度长,那么它在包装时失去缩进(被包装到列1而不是保留帮助文本的缩进),这看起来真的很难看,或空行被吞噬,这是我不想要的,因为我有时需要长帮助文本中的空行。

工作方案如下:

import textwrap

class CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter):
    def _split_lines(self, text, width):
        wrapper = textwrap.TextWrapper(width=width)
        lines = []
        for line in text.splitlines():
            if len(line) > width:
                lines.extend(wrapper.wrap(line))
            else:
                lines.append(line)
        return lines

parser = argparse.ArgumentParser(formatter_class=CustomArgumentFormatter)

其他回答

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

下面是演示的脚本:

#!/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

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

我来这里寻找方法来获得ArgumentDefaultsHelpFormatter的行为,但与换行符和制表符荣誉。特洛伊的代码让我接近了,但最终的结果更简单一些:

class CustomArgumentFormatter(argparse.ArgumentDefaultsHelpFormatter):
    """
    Formats help text to honor newlines and tabs (and show default values).
    """

    # Match multiples of regular spaces only.
    _SPACE_MATCHER = re.compile(r' +', re.ASCII)

    def _split_lines(self, text, width):
        new_text = []
        for line in text.splitlines():
          # For each newline in the help message, replace any multiples of
          # whitespaces (due to indentation in source code) with one space.
          line = self._SPACE_MATCHER.sub(' ', line).rstrip()
          # Fit the line length to the console width
          new_text.extend(textwrap.wrap(line, width))
        return new_text

然后换行符和制表符将按预期出现:

parser = argparse.ArgumentParser(formatter_class=CustomArgumentFormatter)

parser.add_argument(
    '--ethernet_config', type=str, required=False, default=None,
    help='Path to a text file that specifies Ethernet network IP settings \
      to use on the board. For example: \
      \n\t ip=192.0.2.100 \
      \n\t subnet_mask=255.255.255.0 \
      \n\t gateway=192.0.2.1')

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

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

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

如果你只是想覆盖一个选项,你不应该使用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!")

从上面描述的SmartFomatter开始,我最终得到了这个解决方案:

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

注意,奇怪的是,传递给顶级解析器的formatter_class参数并没有被sub_parser继承,必须为每个创建的sub_parser再次传递它。