Argparse: sposób na dołączenie wartości domyślnych do '--help'?

307

Załóżmy, że mam następujący fragment kodu:

diags.cmdln_parser.add_argument( '--scan-time',
                     action  = 'store',
                     nargs   = '?',
                     type    = int,
                     default = 5,
                     help    = "Wait SCAN-TIME seconds between status checks.")

Obecnie --helpzwraca:

usage: connection_check.py [-h]
                             [--version] [--scan-time [SCAN_TIME]]

          Test the reliability/uptime of a connection.



optional arguments:
-h, --help            show this help message and exit
--version             show program's version number and exit
--scan-time [SCAN_TIME]
                    Wait SCAN-TIME seconds between status checks.

Wolałbym coś takiego:

--scan-time [SCAN_TIME]
                    Wait SCAN-TIME seconds between status checks.
                    (Default = 5)

Zerknięcie na kod formatera pomocy ujawnił ograniczone opcje. Czy istnieje sprytny sposób na argparsewydrukowanie domyślnej wartości --scan-timew podobny sposób, czy też powinienem po prostu podklasować helpformatyzator?

JS.
źródło
5
Możesz być zainteresowany docopt . Nigdy więcej nie spojrzałem na argparse.
Paulo Scardine
14
@PauloScardine - Wbudowanie się w język jest główną korzyścią dla argparse.
jordanm
1
@PauloScardine: Wciągnięcie niestandardowej biblioteki do mojego obecnego projektu rzeczywiście będzie uciążliwe, ale z pewnością podoba mi się wygląd wyników docopt. Dzięki za wskazówkę!
JS.
@JS. mówisz „Wciągnięcie niestandardowej biblioteki do mojego obecnego projektu rzeczywiście będzie bolesne” Naprawdę? W pypi znajduje się wiele bardzo przydatnych bibliotek. W moim kontekście łatwo jest pobrać niestandardową bibliotekę. To smutne, jeśli jest trudne w twoim kontekście.
guettli
3
@guettli: Ten projekt dotyczył komercyjnego projektu osadzonego. Masz rację, instalacja była łatwa. Uzyskanie zgody od działu prawnego było koszmarem.
JS.

Odpowiedzi:

446

Użyj argparse.ArgumentDefaultsHelpFormatterformatyzatora :

parser = argparse.ArgumentParser(
    # ... other options ...
    formatter_class=argparse.ArgumentDefaultsHelpFormatter)

Aby zacytować dokumentację:

Druga dostępna klasa formatera ArgumentDefaultsHelpFormatterdoda informacje o domyślnej wartości każdego z argumentów.

Zauważ, że dotyczy to tylko argumentów ze zdefiniowanym tekstem pomocy ; bez helpwartości dla argumentu, nie ma komunikatu o pomoc, aby dodać informacje o wartości domyślnej do .

Dokładne wyjście dla opcji czasu skanowania staje się wtedy:

  --scan-time [SCAN_TIME]
                        Wait SCAN-TIME seconds between status checks.
                        (default: 5)
Martijn Pieters
źródło
12
Czy mogę kontrolować, czy tylko argumenty zawierające jawne wartości default=wskazują wartość domyślną? Ponieważ nie podoba mi się tekst „default: None”.
ziyuang
14
Można ustawić defaultdo SUPPRESS: default=argparse.SUPPRESS. Należy zauważyć, że w tym przypadku nie ma atrybutu zostanie dodana do wyniku przestrzeni nazw, jeśli ten argument został pominięty, zobaczyć się defaultz dokumentacją .
Martijn Pieters
4
Pamiętaj, że musisz to określić również dla każdego utworzonego akapitu.
KomodoDave,
1
Następnie utwórz nowe pytanie, w tym minimalny, powtarzalny przykład, który pokazuje problem. Jak powiedziałem, działa dla mnie z Python 2.7.
Martijn Pieters
3
@David Miałem ten sam problem. Dodaj helpargument do add_argumenti to powinno działać.
Pablo Díaz Ogni
190

Dodaj '%(default)s'do parametru pomocy, aby kontrolować wyświetlane informacje.

parser.add_argument("--type", default="toto", choices=["toto","titi"],
                              help = "type (default: %(default)s)")
polux.moon
źródło
9
Podoba mi się ta opcja, ponieważ już użyłem format_class = argparse.RawTestHelpFormatter i nie miałem ochoty pierdzić z OOP.
mqsoh
23
Nie zapomnij dołączyć zmiennej „typ” do łańcucha formatującego - np. „% (Domyślnie) s” dla łańcucha lub „% (domyślnie) d” dla cyfry.
strongMA
1
Lubię to rozwiązanie znacznie lepiej, jest znacznie prostsze i nie muszę jawnie obsługiwać argumentów bez wartości domyślnych.
void.pointer
@mqsoh wielokrotne dziedziczenie faktycznie po prostu działało, ale niestety nie jest publicznym API: stackoverflow.com/a/52025430/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
Podoba mi się to, ponieważ zmiana klasy formatyzatora dodaje wszędzie „(domyślnie: Brak)”, co zaśmieca pomoc.
6005,
11

Klasa opakowania

Jest to najbardziej niezawodne i SUCHE podejście, jakie do tej pory znalazłem, aby zarówno wyświetlać wartości domyślne, jak i używać innego formatyzatora, takiego jak argparse.RawTextHelpFormatter:

#!/usr/bin/env python3

import argparse

class ArgumentParserWithDefaults(argparse.ArgumentParser):
    def add_argument(self, *args, help=None, default=None, **kwargs):
        if help is not None:
            kwargs['help'] = help
        if default is not None and args[0] != '-h':
            kwargs['default'] = default
            if help is not None:
                kwargs['help'] += ' Default: {}'.format(default)
        super().add_argument(*args, **kwargs)

parser = ArgumentParserWithDefaults(
    formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument('-a', default=13, help='''my help
for a''')
parser.add_argument('-b', default=42, help='''my help
for b''')
parser.add_argument('--no-default', help='''my help
for no-default''')
parser.add_argument('--no-help', default=101)

parser.print_help()
print()
print(parser.parse_args())

Wynik:

usage: main.py [-h] [-a A] [-b B] [--no-default NO_DEFAULT]
               [--no-help NO_HELP]

optional arguments:
  -h, --help            show this help message and exit
  -a A                  my help
                        for a Default: 13
  -b B                  my help
                        for b Default: 42
  --no-default NO_DEFAULT
                        my help
                        for no-default
  --no-help NO_HELP

Namespace(a=13, b=42, no_default=None, no_help=101)

ArgumentDefaultsHelpFormatter+ RawTextHelpFormatterwielokrotne dziedziczenie

Wielokrotne dziedziczenie po prostu działa, ale nie wydaje się publicznym API:

#!/usr/bin/env python3

import argparse

class RawTextArgumentDefaultsHelpFormatter(
        argparse.ArgumentDefaultsHelpFormatter,
        argparse.RawTextHelpFormatter
    ):
        pass

parser = argparse.ArgumentParser(
    formatter_class=RawTextArgumentDefaultsHelpFormatter
)
parser.add_argument('-a', default=13, help='''my help
for a''')
parser.add_argument('-b', default=42, help='''my help
for b''')
parser.print_help()

Wynik:

usage: a.py [-h] [-a A] [-b B]

optional arguments:
  -h, --help  show this help message and exit
  -a A        my help
              for a (default: 13)
  -b B        my help
              for b (default: 42)

To po prostu działa, ponieważ, jak możemy łatwo zobaczyć ze źródeł https://github.com/python/cpython/blob/v3.6.5/Lib/argparse.py#L648, że:

  • RawTextHelpFormatter narzędzia _split_lines
  • ArgumentDefaultsHelpFormatter narzędzia _get_help_string

więc możemy zgadywać, że będą ze sobą dobrze współpracować.

Nie wydaje się to jednak publicznym interfejsem API, formatter_classpodobnie jak metody , więc nie sądzę, że istnieje obecnie publiczny sposób interfejsu API. argparsedocstring mówi:

Wszystkie pozostałe klasy w tym module są uważane za szczegóły implementacji. (Należy również pamiętać, że HelpFormatter i RawDescriptionHelpFormatter są uważane za publiczne tylko jako nazwy obiektów - API obiektów formaterów jest nadal uważane za szczegół implementacji).

Zobacz także: Dostosuj komunikat pomocy argparse

Testowane na Pythonie 3.6.5.

Ciro Santilli
źródło
1
Wspaniały ! Na koniec wydrukuj oba sformatowane dokumenty i domyślne argumenty. Dzięki
Sylvain,