Python argparse:ヘルプテキストに改行を挿入する方法?


340

入力オプションの解析argparseにPython 2.7を使用しています。私の選択肢の1つは複数選択です。ヘルプテキストにリストを作りたい、例えば

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

これは私にとってはうまくいった
カルダモン

回答:


393

使用してみてください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 pythonであり、とにかく3.xのために多くのものをリファクタリングすることが期待されるので、重要ではないかもしれませんが、おそらく素晴らしいアイデアではありません。私は実際にargparseインストールされた2.6を実行しているeasy_installので、ドキュメント自体が古くなる可能性があります。
2010年

3
一部のリンク:python 2.7およびpython 3. *用。2.6パッケージは、そのwikiによれば、公式の2.7 パッケージに準拠しているはずです。ドキュメントから:「RawDescriptionHelpFormatterをformatter_class =として渡すと、説明とエピローグが既に正しくフォーマットされており、行を折り返すべきではないことを示しています」
Stefano

83
代わりにformatter_class = RawDescriptionHelpFormatterを試してください。これは、ヘルプテキストではなく、説明とエピローグでのみ機能します。
MarkHu 2014年

3
でもRawTextHelpFormatter、先頭と末尾の改行が削除されていることに気づきました。これを回避するには、2つ以上の連続した改行を追加するだけです。1つの改行以外はすべて存続します。
MrMas 2016年

11
あなたは、例えば、あまりにもフォーマッタを組み合わせることができclass Formatter( argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): pass、その後とformatter_class=Formatter
Terry Brown

78

1つのオプションを上書きするだけの場合は、を使用しないでください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!")

バージョンメッセージに対してこれを実行したいのですが、この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 2014

@mc_electron私がbitbucketで公開した改善について言及しています(回答のargparseに関する私の改善へのリンクに従って)。しかし、あなたはまた、パッチを適用することができます__call___VersionAction行うことによってargparse._VersionAction.__call__ = smart_version定義した後def smart_version(self, parser, namespace, values, option_string=None): ...
Anthonの

いい案。エピローグと説明が_split_lines :(
Pod

31

これを行うもう1つの簡単な方法は、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 2015年

2
HelpFormatterargparseの開発者は、クラス名がargparseの将来のバージョンでも存続することを保証するだけなので、サブクラス化には問題があります。彼らは基本的に空白のチェックを書き込んでいるので、都合のよいときにメソッド名を変更できます。これはイライラします。APIでいくつかのメソッドが公開されている可能性があります。
MrMas 2016年

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.