Python Argparse : 도움말 텍스트에 줄 바꿈을 삽입하는 방법은 무엇입니까?


341

입력 옵션을 구문 분석하기 위해 argparsePython 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모든 줄 바꿈과 연속 공백을 제거합니다. 결과는 다음과 같습니다

~ / 다운로드 : 52 $ python2.7 x.py -h
사용법 : x.py [-h] [-g {a, b, g, d, e}]

테스트

선택적 인수 :
  -h, --help이 도움말 메시지를 표시하고 종료
  -g {a, b, g, d, e} a = 알파 b = 베타 g = 감마 d = 델타 e
                  = 엡실론

도움말 텍스트에 줄 바꿈을 삽입하는 방법은 무엇입니까?


나와 함께 python 2.7이 없으므로 아이디어를 테스트 할 수 있습니다. 큰 따옴표 ( "" "" "")로 도움말 텍스트를 사용하는 것은 어떻습니까. 새로운 라인이 이것을 사용하여 살아남습니까?
pyfunc

4
@pyfunc : 아니요. 스트리핑은 런타임에 argparse인터프리터가 아닌에 의해 수행 되므로로 전환 """..."""해도 도움이되지 않습니다.
kennytm

이것은 나를 위해 일했다
cardamom

답변:


394

사용해보십시오 RawTextHelpFormatter:

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

6
나는 그렇지 않다고 생각합니다. 당신은 불행하게도을 서브 클래 싱 할 수도 있지만 Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. 2.7은 지난 2.x는 파이썬으로 의미 당신은 어쨌든 3.x를위한 일의 리팩토링 많이 예상 될 것이기 때문에, 비록 그것이 문제가 아니라 수도, 그래서 아마 좋은 생각. 실제로 문서를 오래 사용할 수 있도록 argparsevia 를 설치하여 2.6을 실행 easy_install하고 있습니다.
초대

3
일부 링크 : python 2.7python 3. * 용 . 2.6 패키지는 위키 에 따라 공식 2.7 패키지를 준수해야합니다. 문서에서 : "rawDescriptionHelpFormatter를 formatter_class =로 전달하면 설명과 에필로그가 이미 올바르게 형식화되어 있고 줄 바꿈되어서는 안됨을 나타냅니다"
Stefano

83
대신 RawDescriptionHelpFormatter도움말 텍스트가 아닌 설명 및 에필로그에서만 작동하는 formatter_class = 를 사용해보십시오 .
MarkHu

3
와 함께 RawTextHelpFormatter, 선행 및 후행 줄 바꿈이 제거 되었음을 알았 습니다. 이 문제를 해결하려면 두 개 이상의 연속 개행을 추가하면됩니다. 하나의 개행을 제외하고는 모두 살아남을 것입니다.
MrMas

11
당신은 예, 너무 포매터를 결합 할 수 있습니다 class Formatter( argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): pass다음과 formatter_class=Formatter.
Terry Brown

79

하나의 옵션 만 무시하려면을 사용하지 않아야합니다 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!")

버전 메시지에 대해이 작업을 수행하려고하지만이 SmartFormatter는 특수 버전 텍스트가 아닌 도움말 텍스트에서만 작동하는 것 같습니다. parser.add_argument('-v', '--version', action='version',version=get_version_str()) 이 경우로 확장 할 수 있습니까?
mc_electron

@mc_electron SmartFormatter의 정식 버전에도 고유 한 _split_lines줄 바꿈이 있습니다 (처음에 "R |"을 지정할 필요가 없습니다.이 옵션을 원하면 _VersionAction.__call__방법을 패치하십시오.
Anthon

형식화 된 버전을 사용하는 대신 주석을 _VersionAction.__call__원한다는 것을 알 수는 있지만 주석의 첫 번째 부분을 완전히 구사하지는 않습니다 parser.exit(message=version). 패치 된 argparse 사본을 공개하지 않고 그렇게 할 수있는 방법이 있습니까?
mc_electron

@ mc_electron 나는 bitbucket에 게시 된 개선 사항을 언급하고 있습니다 (응답의 argparse에 대한 개선 사항 링크). 그러나 당신은 또한 패치 수 __call___VersionAction수행하여 argparse._VersionAction.__call__ = smart_version정의한 후def smart_version(self, parser, namespace, values, option_string=None): ...
안톤

좋은 생각이야 에필로그와 설명이 _split_lines를 통해 실행되지 않는 것처럼 나를 도와주지 않았다 :(
Pod

31

또 다른 쉬운 방법은 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 ...

11

비슷한 문제에 직면했습니다 (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()

그리고 얻었다 :

사용법 : play-with-argparse.py [옵션]

첫 번째 단락 

                        두 번째 단락

                        세 번째 단락

선택적 인수 :
  -h, --help이 도움말 메시지를 표시하고 종료

따라서 RawTextHelpFormatter해결책이 아닙니다. 모든 공백 문자를 유지하면서 소스 코드에 나타나는 설명을 인쇄하기 때문에 (가독성을 위해 소스 코드에 추가 탭을 유지하고 싶지만 모두 인쇄하고 싶지 않습니다. 또한 원시 포맷터는 줄 바꿈을하지 않습니다. 예를 들어 너무 길면 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()

출력은 다음과 같습니다

사용법 : play-with-argparse.py [옵션]

첫 번째 단락

두 번째 단락

세 번째 단락

선택적 인수 :
  -h, --help이 도움말 메시지를 표시하고 종료

1
이것은 도움이되는 주장을 완전히 다시 구현하는 것을 거의 포기하고 고민 한 후에 일어난 일입니다.
Paul Gowder

2
HelpFormatterargparse 개발자는 클래스 이름이 이후 버전의 argparse에서도 유지 될 것만 보장하므로 서브 클래 싱 은 문제가됩니다. 기본적으로 백지 수표를 작성했기 때문에 메소드 이름이 편리한 경우 메소드 이름을 변경할 수 있습니다. 나는이 좌절감을 느낀다. 그들이 할 수있는 최소한은 API에 몇 가지 방법이 노출되어 있습니다.
MrMas

OP가 요구하는 것이 아니라 정확히 내가 원하는 것, 감사합니다!
Huw Walters

2

설명 텍스트에 수동 줄 바꿈과 자동 줄 바꿈을 모두 원했습니다. 그러나 여기에 제안 된 어느 것도 나를 위해 일하지 않았습니다. 그래서 나는 여기 답변에 주어진 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

1

위에서 설명한 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에 대해 다시 전달해야합니다.


0

머리말

이 질문에 argparse.RawTextHelpFormatter도움이됩니다.

이제을 사용하는 방법을 공유하고 싶습니다 argparse.

질문과 관련이 없을 수도 있음을 알고 있습니다.

그러나 이러한 질문들은 잠시 동안 귀찮게되었습니다.

내 경험을 공유하고 싶습니다. 누군가에게 도움이되기를 바랍니다.

여기 있습니다

타사 모듈

colorama : 텍스트 색상 변경 :pip install colorama

MS Windows에서 ANSI 이스케이프 문자 시퀀스 (컬러 터미널 텍스트 및 커서 위치 지정)가 작동하도록합니다.

import colorama
from colorama import Fore, Back
from pathlib import Path
from os import startfile, system

SCRIPT_DIR = Path(__file__).resolve().parent
TEMPLATE_DIR = SCRIPT_DIR.joinpath('.')


def main(args):
    ...


if __name__ == '__main__':
    colorama.init(autoreset=True)

    from argparse import ArgumentParser, RawTextHelpFormatter

    format_text = FormatText([(20, '<'), (60, '<')])
    yellow_dc = format_text.new_dc(fore_color=Fore.YELLOW)
    green_dc = format_text.new_dc(fore_color=Fore.GREEN)
    red_dc = format_text.new_dc(fore_color=Fore.RED, back_color=Back.LIGHTYELLOW_EX)

    script_description = \
        '\n'.join([desc for desc in
                   [f'\n{green_dc(f"python {Path(__file__).name} [REFERENCE TEMPLATE] [OUTPUT FILE NAME]")} to create template.',
                    f'{green_dc(f"python {Path(__file__).name} -l *")} to get all available template',
                    f'{green_dc(f"python {Path(__file__).name} -o open")} open template directory so that you can put your template file there.',
                    # <- add your own description
                    ]])
    arg_parser = ArgumentParser(description=yellow_dc('CREATE TEMPLATE TOOL'),
                                # conflict_handler='resolve',
                                usage=script_description, formatter_class=RawTextHelpFormatter)

    arg_parser.add_argument("ref", help="reference template", nargs='?')
    arg_parser.add_argument("outfile", help="output file name", nargs='?')
    arg_parser.add_argument("action_number", help="action number", nargs='?', type=int)
    arg_parser.add_argument('--list', "-l", dest='list',
                            help=f"example: {green_dc('-l *')} \n"
                                 "description: list current available template. (accept regex)")

    arg_parser.add_argument('--option', "-o", dest='option',
                            help='\n'.join([format_text(msg_data_list) for msg_data_list in [
                                ['example', 'description'],
                                [green_dc('-o open'), 'open template directory so that you can put your template file there.'],
                                [green_dc('-o run'), '...'],
                                [green_dc('-o ...'), '...'],
                                # <- add your own description
                            ]]))

    g_args = arg_parser.parse_args()
    task_run_list = [[False, lambda: startfile('.')] if g_args.option == 'open' else None,
                     [False, lambda: [print(template_file_path.stem) for template_file_path in TEMPLATE_DIR.glob(f'{g_args.list}.py')]] if g_args.list else None,
                     # <- add your own function
                     ]
    for leave_flag, func in [task_list for task_list in task_run_list if task_list]:
        func()
        if leave_flag:
            exit(0)

    # CHECK POSITIONAL ARGUMENTS
    for attr_name, value in vars(g_args).items():
        if attr_name.startswith('-') or value is not None:
            continue
        system('cls')
        print(f'error required values of {red_dc(attr_name)} is None')
        print(f"if you need help, please use help command to help you: {red_dc(f'python {__file__} -h')}")
        exit(-1)
    main(g_args)

의 클래스 FormatText는 다음과 같습니다

class FormatText:
    __slots__ = ['align_list']

    def __init__(self, align_list: list, autoreset=True):
        """
        USAGE::

            format_text = FormatText([(20, '<'), (60, '<')])
            red_dc = format_text.new_dc(fore_color=Fore.RED)
            print(red_dc(['column 1', 'column 2']))
            print(red_dc('good morning'))
        :param align_list:
        :param autoreset:
        """
        self.align_list = align_list
        colorama.init(autoreset=autoreset)

    def __call__(self, text_list: list):
        if len(text_list) != len(self.align_list):
            if isinstance(text_list, str):
                return text_list
            raise AttributeError
        return ' '.join(f'{txt:{flag}{int_align}}' for txt, (int_align, flag) in zip(text_list, self.align_list))

    def new_dc(self, fore_color: Fore = Fore.GREEN, back_color: Back = ""):  # DECORATOR
        """create a device context"""
        def wrap(msgs):
            return back_color + fore_color + self(msgs) + Fore.RESET
        return wrap

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.