Python argparse: как вставить новую строку в текст справки?

Я использую argparse в Python 2.7 для анализа параметров ввода. Один из моих вариантов - это множественный выбор. Я хочу сделать список в его тексте справки, например.

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

Как вставить строки в текст справки?

Ответ 1

Попробуйте использовать RawTextHelpFormatter:

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

Ответ 2

Если вы просто хотите переопределить один параметр, вы не должны использовать RawTextHelpFormatter. Вместо этого подкласс HelpFormatter и предоставить специальное введение для параметров, которые должны обрабатываться "raw" (я использую "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!")

Ответ 3

Другой простой способ сделать это - включить textwrap.

Например,

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

Ответ 4

Я столкнулся с подобной проблемой (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 formatter не переносит строки, когда это слишком длинный, более 80 символов например).

Спасибо @Anton, который вдохновил правильное направление выше. Но это решение нуждается в небольшой модификации, чтобы отформатировать раздел описания.

В любом случае, нужен специальный форматер. Я расширил существующий класс HelpFormatter и _fill_text метод _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

Ответ 5

Я хотел иметь как ручные разрывы строк в тексте описания, так и автоматическую его упаковку; но ни одно из предложений здесь не работало для меня - поэтому я закончил модификацию класса 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

Ответ 6

Начиная с описанного выше 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_parsers, его необходимо передавать снова для каждого созданного sub_parser.