Python argparse: Yardım metnine yeni satır nasıl eklenir?


341

Giriş seçeneklerini ayrıştırmak için argparsePython 2.7'de kullanıyorum . Seçeneklerimden biri çoktan seçmeli. Yardım metninde bir liste yapmak istiyorum, örn.

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

Ancak, argparsetüm yeni satırları ve ardışık boşlukları kaldırır. Sonuç şuna benziyor

~ / İndirilenler: 52 $ python2.7 x.py -h
kullanım: x.py [-h] [-g {a, b, g, d, e}]

Ölçek

isteğe bağlı argümanlar:
  -h, --help bu yardım mesajını göster ve çık
  -g {a, b, g, d, e} Bazı seçenekler; burada a = alfa b = beta g = gama d = delta e
                  = epsilon

Yardım metnine yeni satırlar nasıl eklenir?


Yanımda python 2.7 yok, bu yüzden fikirlerimi test edebilirim. Üçlü tırnak içinde yardım metni kullanmaya ne dersiniz ("" "" ""). Yeni hatlar bunu kullanarak hayatta kalıyor mu?
pyfunc

4
@pyfunc: Hayır. Sıyırma işlemi argparse, yorumlayıcı tarafından değil çalışma zamanında yapılır , bu nedenle geçiş yapmak """..."""yardımcı olmaz.
kennytm

Bu benim için çalıştı
kakule

Yanıtlar:


394

Kullanmayı deneyin RawTextHelpFormatter:

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

6
Bence değil. Bunu alt sınıflara ayırabilirsiniz, ancak ne yazık ki Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. büyük olasılıkla harika bir fikir değil, önemli olmasa da, 2.7 son 2.x python olması gerekiyor ve yine de 3.x için birçok şeyi yeniden düzenlemeniz bekleniyor. Aslında 2.6 ile argparseyüklü üzerinden çalıştırmak easy_installböylece belgelerin kendisi güncel olmayabilir.
intuited

3
Bazı bağlantılar: python 2.7 ve python 3 için * . 2.6 paketi , wikisine göre resmi 2.7 pakete uymalıdır. Dokümandan: "RawDescriptionHelpFormatter'ı formatter_class = olarak geçirme = açıklamanın ve epilogun zaten doğru şekilde biçimlendirildiğini ve satırlara sarılmaması gerektiğini gösterir"
Stefano

83
Bunun RawDescriptionHelpFormatteryerine, yardım metni yerine yalnızca açıklama ve epilog üzerinde çalışan formatter_class = öğesini deneyin .
MarkHu

3
RawTextHelpFormatterÖnde ve takip eden yeni satırların bile kaldırıldığını fark ettim . Bu sorunu çözmek için, art arda iki veya daha fazla yeni satır ekleyebilirsiniz; bir satırsonu hariç hepsi hayatta kalacak.
BayMas

11
Biçimlendiricileri de birleştirebilirsiniz, örn. class Formatter( argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): passVe sonra formatter_class=Formatter.
Terry Brown

79

Sadece bir seçeneği geçersiz kılmak istiyorsanız, kullanmamalısınız RawTextHelpFormatter. Bunun yerine alt sınıf HelpFormatterve "raw" (kullanıyorum "R|rest of help") ele alınması gereken seçenekler için özel bir giriş sağlar :

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)

Ve kullanın:

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()Yardımın başlamadığı diğer tüm çağrılar R|normal şekilde sarılacaktır.

Bu argparse ile ilgili geliştirmelerimin bir parçası . Tam SmartFormatter ayrıca tüm seçeneklere varsayılanların eklenmesini ve yardımcı program açıklamasının ham girdisini de destekler. Tam sürümün kendi _split_linesyöntemi vardır, böylece örneğin sürüm dizelerine yapılan herhangi bir biçimlendirme korunur:

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

Bunu bir sürüm mesajı için yapmak istiyorum, ancak bu SmartFormatter özel sürüm metni ile değil, sadece yardım metni ile çalışıyor gibi görünüyor. parser.add_argument('-v', '--version', action='version',version=get_version_str()) Bu davaya genişletmek mümkün mü?
mc_electron

@mc_electron SmartFormatter'ın tam sürümünün de kendi _split_linessatırları vardır ve satır sonlarını korur (başlangıçta "R |" belirtmeye gerek yoktur, bu seçeneği istiyorsanız, _VersionAction.__call__yöntemi
yamalayın

Ben _VersionAction.__call__muhtemelen sadece parser.exit(message=version)biçimlendirilmiş sürümü kullanmak yerine bunu isteyeyim ki görebiliyorum, yorumunuzun ilk kısmını grokking değilim . Yine de argparse'nin yamalı bir kopyasını yayınlamadan bunu yapmanın bir yolu var mı?
mc_electron

@mc_electron Bitbucket'te yayınladığım geliştirmelere atıfta bulunuyorum (yanıttaki argparse ile ilgili geliştirmelerimin bağlantısına göre). Ama aynı zamanda yama olabilir __call__de _VersionActionyaparak argparse._VersionAction.__call__ = smart_versiontanımlayan sonradef smart_version(self, parser, namespace, values, option_string=None): ...
Anthon

İyi fikir. Epilog ve açıklama _split_lines üzerinden geçmedi gibi bana yardımcı olmadı :(
Pod

31

Bunu yapmanın bir başka kolay yolu da textwrap eklemektir .

Örneğin,

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

Bu şekilde, her çıktı hattının önündeki uzun boş alandan kaçınabiliriz.

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

Benzer bir sorunla karşılaştım (Python 2.7.6). Ben kullanarak açıklama satırını birkaç satırlara ayırmaya çalıştım RawTextHelpFormatter:

parser = ArgumentParser(description="""First paragraph 

                                       Second paragraph

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

options = parser.parse_args()

Ve var:

kullanım: play-with-argparse.py [SEÇENEKLER]

İlk paragraf 

                        İkinci paragraf

                        Üçüncü paragraf

isteğe bağlı argümanlar:
  -h, --help bu yardım mesajını göster ve çık

Öyleyse RawTextHelpFormatterbir çözüm değil. Tüm beyaz boşluk karakterlerini koruyarak açıklamayı kaynak kodunda göründüğü için (okunabilirlik için kaynak kodumda fazladan sekme tutmak istiyorum, ancak hepsini yazdırmak istemiyorum. Ayrıca ham biçimlendirici, çok uzun, örneğin 80 karakterden fazla).

Yukarıdaki doğru yöne ilham veren @Anton'a teşekkürler . Ancak bu çözümün açıklama bölümünü biçimlendirmek için küçük bir modifikasyona ihtiyacı vardır .

Her neyse, özel biçimlendirici gerekiyor. Varolan HelpFormattersınıfı genişletti ve _fill_textböyle yöntem overrode :

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 modülünden gelen orijinal kaynak koduyla karşılaştırın :

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)

Orijinal kodda tüm açıklama kaydırılır. Yukarıdaki özel biçimlendiricide metnin tamamı birkaç parçaya bölünür ve her biri bağımsız olarak biçimlendirilir.

Özel biçimlendirici yardımıyla:

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

options = parser.parse_args()

çıktı:

kullanım: play-with-argparse.py [SEÇENEKLER]

İlk paragraf

İkinci paragraf

Üçüncü paragraf

isteğe bağlı argümanlar:
  -h, --help bu yardım mesajını göster ve çık

1
Bu harika --- neredeyse vazgeçtikten ve yardım argümanını tamamen yeniden uygulamayı düşündükten sonra bunun üzerinde oldu ... bana çok fazla güçlük kazandırdı.
Paul Gowder

2
alt sınıflandırma HelpFormattersorunludur çünkü argparse geliştiricileri sadece sınıf adının argparse'nin gelecekteki versiyonlarında hayatta kalacağını garanti eder. Temel olarak kendilerine boş bir çek yazmışlar, böylece eğer uygunsa yöntem adlarını değiştirebilirler. Bunu sinir bozucu buluyorum; yapabildikleri en az şey API'da birkaç yöntemle karşılaşır.
BayMas

OP'nin tam olarak ne istediğini değil, tam olarak ne istediğimi, teşekkürler!
Huw Walters

2

Ben açıklama metninde hem manuel satır sonları, hem de otomatik sarma istedim; ama buradaki önerilerin hiçbiri benim için işe yaramadı - bu yüzden burada verilen cevaplarda verilen SmartFormatter sınıfını değiştirdim; argparse yöntem adları ile ortak bir API olmamasına rağmen sorunları rağmen, burada ne var (adlı bir dosya olarak 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 ve 3.4'te şu şekilde çalışır:

$ 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

Yukarıda açıklanan SmartFomatter'dan başlayarak, bu çözüme son verdim:

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

Üst düzey ayrıştırıcıya geçirilen formatter_class argümanının garip bir şekilde alt_parserler tarafından miras alınmadığını, oluşturulan her alt_parser için bir tanesinin tekrar geçirilmesi gerektiğini unutmayın.


0

önsöz

Bu soru argparse.RawTextHelpFormatteriçin bana yardımcı olur.

Şimdi, nasıl kullanacağımı paylaşmak istiyorum argparse.

Soruyla ilgili olmayabileceğini biliyorum,

ama bu sorular bir süredir beni rahatsız ediyor.

Bu yüzden deneyimlerimi paylaşmak istiyorum, umarım bu birisi için faydalı olacaktır.

İşte başlıyoruz.

3. Taraf Modülleri

Colorama : Metin rengini değiştirmek için:pip install colorama

MS Windows altında ANSI çıkış karakter dizilerinin (renkli terminal metni ve imleç konumlandırması üretmek için) çalışmasını sağlar

Misal

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)

Sınıfı FormatTextaşağıdaki

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

resim açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.