2010-10-04 4 views
222

Używam argparse in Python 2.7 do analizowania opcji wprowadzania. Jedną z moich opcji jest wybór wielokrotny. Chcę utworzyć listę w tekście pomocy, np.Python argparse: Jak wstawić znak nowej linii w tekście pomocy?

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

Jednakże argparse usuwa wszystkie linie oraz kolejnych obszarów. Wynik wygląda tak, jak wstawić znaki nowej linii w tekście pomocy?

+0

Nie mam Pythona 2.7 ze mną, więc mogę przetestować moje pomysły. Co powiesz na temat używania tekstu pomocy w potrójnych cudzysłowach ("" "" ""). Czy nowe linie przetrwają z tego powodu? – pyfunc

+3

@pyfunc: Nie. Rozbijanie odbywa się w czasie pracy przez 'argparse', nie tłumacza, więc przełączenie na' "" "..." "" nie pomoże. – kennytm

Odpowiedz

255

Spróbuj użyć RawTextHelpFormatter:

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

Fajnie, dzięki. Czy możliwe jest zastosowanie go tylko w opcji 1? – kennytm

+4

Myślę, że nie jest. Można ją podklasować, ale niestety 'Tylko nazwa tej klasy jest uważana za publiczny interfejs API. Wszystkie metody dostarczone przez klasę są uważane za szczegóły implementacji. "Prawdopodobnie nie jest to świetny pomysł, chociaż może nie mieć znaczenia, ponieważ 2.7 ma być ostatnim pytonem w wersji 2.x, a będziesz wymagać od niego wielu rzeczy dla 3.x. W rzeczywistości uruchamiam 2.6 z 'argparse' zainstalowanym przez' easy_install', więc sama dokumentacja może być nieaktualna. – intuited

+3

Niektóre linki: dla [python 2.7] (http://docs.python.org/library/argparse.html#formatter-class) i [python 3. *] (http://docs.python.org/dev /library/argparse.html#formatter-class). Pakiet 2.6 powinien, zgodnie z [jego wiki] (http://code.google.com/p/argparse/), być zgodny z oficjalnym pakietem 2.7. Z dokumentu: "Przekazywanie RawDescriptionHelpFormatter jako formatter_class = wskazuje, że opis i epilog są już poprawnie sformatowane i nie powinny być zawijane liniami" – Stefano

53

Jeśli chcesz po prostu zastąpić jedną opcję, nie należy używać RawTextHelpFormatter. Zamiast podklasy HelpFormatter i zapewnić specjalny wstęp do opcji, które powinny być traktowane jako „raw” (używam "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) 

i używać go:

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

Wszelkie inne połączenia do .add_argument() gdzie na pomoc nie zaczyna się od R| zostanie zapakowana jak zwykle. To jest częścią my improvements on argparse. Pełna funkcja SmartFormatter obsługuje także dodawanie ustawień domyślnych do wszystkich opcji oraz nieprzetworzone dane wejściowe opisu narzędzi. Pełna wersja ma własną metodę _split_lines, więc wszelkie formatowanie wykonane na przykład do ciągi wersji są zachowywane:

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

Chcę to zrobić dla wiadomości wersji, ale ten SmartFormatter wydaje się działać tylko z tekstem pomocy, a nie tekstem specjalnej wersji. 'parser.add_argument ('- v', '--version', action = 'version', version = get_version_str())' Czy można go rozszerzyć na tę sprawę? –

+0

@mc_electron pełna wersja SmartFormatter ma również własną '_split_lines' i zachowuje podział linii (nie trzeba określać" R | "na początku, jeśli chcesz tę opcję, załatuj metodę' _VersionAction .__ call__' – Anthon

+0

. nie jestem całkowicie grokowanie pierwszej części twojego komentarza, chociaż widzę w "_VersionAction .__ call__", że prawdopodobnie chciałbym, aby to było tylko "parser.exit (message = version)' zamiast używania sformatowanej wersji. Czy jest jakiś sposób aby to zrobić bez wydawania poprawionej kopii argparse? –

7

Napotkałem podobny problem (Python 2.7.6). Próbowałem rozbić opis rozdział na kilka linii z wykorzystaniem RawTextHelpFormatter:

parser = ArgumentParser(description="""First paragraph 

             Second paragraph 

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

options = parser.parse_args() 

I got:

 
usage: play-with-argparse.py [OPTIONS] 

First paragraph 

         Second paragraph 

         Third paragraph 

optional arguments: 
    -h, --help show this help message and exit 

Więc RawTextHelpFormatter nie jest rozwiązaniem. Ponieważ wypisuje opis w postaci kodu źródłowego, zachowując wszystkie znaki odstępu (chcę zachować dodatkowe zakładki w moim kodzie źródłowym, aby móc je odczytać, ale nie chcę ich wszystkich drukować.) Również formatowanie raw nie owija wiersza, gdy jest on zbyt długi, na przykład ponad 80 znaków).

Dzięki @Anton, który zainspirował właściwy kierunek above. Ale to rozwiązanie wymaga niewielkich modyfikacji w celu sformatowania sekcji opis.

W każdym razie potrzebny jest niestandardowy formater. I przedłużony istniejących HelpFormatter klasę i overrode _fill_text metody takie jak to:

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 

porównać z oryginalnym kodem źródłowym pochodzące z modułu 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) 

W oryginalnym kodzie cały opis jest zawinięty.W edytorze niestandardowym powyżej cały tekst jest podzielony na kilka części, a każdy z nich jest formatowany niezależnie.

Więc z pomocą niestandardowego formater:

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

options = parser.parse_args() 

wyjście jest:

 
usage: play-with-argparse.py [OPTIONS] 

First paragraph 

Second paragraph 

Third paragraph 

optional arguments: 
    -h, --help show this help message and exit 
+1

To jest cudowne - wydarzyło się to po tym, jak prawie się poddałem i rozważając jedynie ponowne wprowadzenie argumentu pomocy ... uratowało mi sporo kłopotów. –

+2

Podklasowanie 'HelpFormatter' jest problematyczne, ponieważ programiści argparse gwarantują tylko, że nazwa klasy przetrwa w przyszłych wersjach argparse. Zasadniczo napisali sobie pustą próbę, aby mogli zmienić nazwy metod, jeśli byłoby to dla nich wygodne. Uważam to za frustrujące; co najmniej mogliby zrobić, to ujawnić kilka metod w API. – MrMas

20

Innym sposobem, aby to zrobić jest włączenie textwrap.

Na przykład

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

W ten sposób możemy uniknąć długiej pustej przestrzeni z przodu każdej linii wyjściowej.

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

Chciałam mieć zarówno ręczne podziały wierszy w tekście opisu, jak i jego automatyczne zawijanie; ale żadna z sugestii tutaj nie działała dla mnie - więc skończyłem na modyfikacji klasy SmartFormatter podanej w odpowiedziach tutaj; że problemy z argparse nazwy metod nie będący publicznym API pomimo, tutaj jest to, co mam (jako plik o nazwie 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() 

Jak to działa w 2.7 i 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