2012-02-20 12 views
35

Mam ten kod, który jestem ogólnie zadowolony z:Python argparse: wiele opcji powoduje brzydkie pomoc wyjście

import argparse 

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer", 
      "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ] 

parser = argparse.ArgumentParser(description="A program to update components on servers.") 
group = parser.add_mutually_exclusive_group() 
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components') 
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components') 
parser.add_argument('-o', '--only', nargs='*', choices=servers, help='Space separated list of case sensitive server names to process') 
parser.add_argument('-s', '--skip', nargs='*', choices=servers, help='Space separated list of case sensitive server names to exclude from processing') 
args = parser.parse_args() 

Lubię że wybór = serwery sprawdza nazwy serwerów na wejściu dla mnie, więc że nie będę musiał. Jednakże, mając tak wiele ważnych wyborów sprawia, że ​​wyjście pomaga wyglądać straszne:

usage: args.py [-h] [-l | -u] 
       [-o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]] 
       [-s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]] 

A program to update components on servers. 

optional arguments: 
    -h, --help   show this help message and exit 
    -l, --list   list server components 
    -u, --updatepom  update server components 
    -o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --only [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]] 
         Space separated list of case sensitive server names to 
         process 
    -s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --skip [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]] 
         Space separated list of case sensitive server names to 
         exclude from processing 

Którędy polecacie jeśli chcę:

  • Nice (przeważnie) Auto-Tworzone wyjście pomóc
  • walidacji że wpisy podane do opcji -o lub -s są w servers.

Bonus:

  • byłoby możliwe, aby case dopasowywanie niewrażliwy ciągów znaków dla nazw serwerów?

Dołącz

Próbowałem za pomocą michaelfilms sugestię gdzie -o-s opcje są usuwane z powyższego wyjścia i ta część w brzmieniu:

server optional arguments: 
    Valid server names are: ApaServer, BananServer, GulServer, SolServer, 
    RymdServer, SkeppServer, HavsServer, PiratServer, SvartServer, 
    NattServer, SovServer 

myślę, że wygląda całkiem nieźle, ale Naprawdę muszę zapewnić pomoc dla opcji -o i -s, ponieważ użytkownik nie wiedziałby o nich inaczej. Tak więc nie jestem jeszcze na miejscu, używając tego podejścia.

Odpowiedz

3

Dlaczego nie użyć parametru parser.add_argument_group, aby utworzyć grupę dla opcji opartych na serwerze i podać opis argumentu wyświetlający listę możliwych wyborów? Następnie należy przekazać argparse.SUPPRESS do pomocy dla każdej z poszczególnych opcji. Wierzę, że da ci to, czego chcesz.

+0

Próbowałem go i wygląda całkiem nieźle. Ale nie jestem jeszcze tam, jak dotąd, stosując to podejście. Muszę pomóc użytkownikowi, informując go o opcjach '-o' i' -s'. – Deleted

4

Aby uzyskać oczekiwany wyjścia, musisz podklasy argparse.HelpFormatter i wdrożenie formatowanie, które potrzebujesz. W szczególności musisz zaimplementować własną metodę _metavar_formatter, która jest odpowiedzialna za łączenie wszystkich wyborów w jeden ciąg oddzielony przecinkami.

8

Mam ten sam problem i jako obejście wykorzystałem epilog do opisania każdego z dostępnych opcji. Musiałem użyć argparse.RawTextHelpFormatter, który pozwala określić, czy epilog jest wstępnie sformatowany.

def choicesDescriptions(): 
    return """ 
Choices supports the following: 
    choice1   - the FIRST option 
    choice2   - the SECOND option 
    ... 
    choiceN   - the Nth option 
""" 

def getChoices(): 
    return ["choice1", "choice2", ..., "choiceN"] 

parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, epilog=choicesDescriptions()) 
parser.add_argument(
    'choices', 
    choices=getChoices(), 
    help='Arg choice. See the choices options below' 
    ) 

args = parser.parse_args() 
print(args) 
20

Nie ma potrzeby, aby cokolwiek podklasować. Wystarczy podać argument o wartości metavar z łańcuchem, który ma się pojawić w wiadomości pomocy.

Zobacz argparse documentation szczegóły.

37

Powtarzam w zasadzie to, co powiedział Ernest - aby uniknąć brzydkiej długiej listy wyborów, ustaw metavar = '' dla argumentów opartych na wyborze (choć nie pozbywa się białych znaków między argumentem a przecinkiem (np. -o , zamiast -o,). Następnie możesz opisać dostępne opcje w ogólnym opisie (RawDescriptionHelpFormatter jest tutaj użyteczny, jeśli chcesz, aby były wymienione z oczywistym wcięciem).

Nie rozumiem, dlaczego odpowiedź Ernesta została odrzucona. Ten kod

import argparse 

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer", 
      "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ] 

parser = argparse.ArgumentParser(description="A program to update components on servers.") 
group = parser.add_mutually_exclusive_group() 
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components') 
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components') 
parser.add_argument('-o', '--only', choices=servers, help='Space separated list of case sensitive server names to process. Allowed values are '+', '.join(servers), metavar='') 
parser.add_argument('-s', '--skip', choices=servers, help='Space separated list of case sensitive server names to exclude from processing. Allowed values are '+', '.join(servers), metavar='') 
args = parser.parse_args() 

produkuje następujące dane wyjściowe pomocy

usage: run.py [-h] [-l | -u] [-o] [-s] 

A program to update components on servers. 

optional arguments: 
    -h, --help  show this help message and exit 
    -l, --list  list server components 
    -u, --updatepom update server components 
    -o , --only  Space separated list of case sensitive server names to 
        process. Allowed values are ApaServer, BananServer, 
        GulServer, SolServer, RymdServer, SkeppServer, HavsServer, 
        PiratServer, SvartServer, NattServer, SovServer 
    -s , --skip  Space separated list of case sensitive server names to 
        exclude from processing. Allowed values are ApaServer, 
        BananServer, GulServer, SolServer, RymdServer, SkeppServer, 
        HavsServer, PiratServer, SvartServer, NattServer, SovServer 

To jest nadzieją co oryginalny post został szukasz.

+0

Ustawienie metavar na '' lub None spowodowało wyświetlenie żadnej pomocy na all.move_parser.add_argument ('old_host', action = 'store', choices = distinct_host, help = "Stary host" + ','. Join (distinct_host), metavar = None) – user2601010

+0

'metavar = ''' wydaje się teraz powodować błąd. Ustawienie tego na coś innego niż pusty ciąg znaków (jak nazwa argumentu) naprawia to. –

1

http://bugs.python.org/issue16468argparse only supports iterable choices to problem błędów omawiających formatowanie wyborów. Lista opcji może pojawiać się w 3 miejscach: linii użycia, liniach pomocy i komunikatach o błędach.

Wszystko, o czym troszczy się parser, polega na wykonaniu testu in (__contains__). Jednak w przypadku formatowania długie listy, nieograniczone "listy" (np. Liczby całkowite> 100) i inne obiekty, które nie są iterowalne, powodują problemy. metavar to sposób, w jaki bieżący użytkownicy mogą obejść większość problemów związanych z formatowaniem (może to nie pomóc w przypadku komunikatów o błędach). Sprawdź problem, aby uzyskać pomysły na zmianę własnej wersji argparse.

3

To nie pomoże w sytuacjach, gdy lista opcji jest wyjątkowo długa, jak w pytaniu oryginalnym, ale dla osób, które podobnie jak ja natknęły się na to pytanie szukając sposobu na złamanie średnio długich łańcuchów opcji w dwie linie tu jest moje rozwiązanie:

import argparse 

class CustomFormatter(argparse.HelpFormatter): 
    """Custom formatter for setting argparse formatter_class. Identical to the 
    default formatter, except that very long option strings are split into two 
    lines. 
    """ 

    def _format_action_invocation(self, action): 
     if not action.option_strings: 
      metavar, = self._metavar_formatter(action, action.dest)(1) 
      return metavar 
     else: 
      parts = [] 
      # if the Optional doesn't take a value, format is: 
      # -s, --long 
      if action.nargs == 0: 
       parts.extend(action.option_strings) 
      # if the Optional takes a value, format is: 
      # -s ARGS, --long ARGS 
      else: 
       default = action.dest.upper() 
       args_string = self._format_args(action, default) 
       for option_string in action.option_strings: 
        parts.append('%s %s' % (option_string, args_string)) 
      if sum(len(s) for s in parts) < self._width - (len(parts) - 1) * 2: 
       return ', '.join(parts) 
      else: 
       return ',\n '.join(parts) 

Ten kod przesłania domyślny argparse.HelpFormatter metody _format_action_invocation i jest identyczny do realizacji domyślnej wyjątkiem ostatnich czterech liniach.

domyślne zachowanie formater:

parser = argparse.ArgumentParser(description="Argparse default formatter.") 
parser.add_argument('-a', '--argument', help='not too long') 
parser.add_argument('-u', '--ugly', choices=range(20), help='looks messy') 
parser.print_help() 

wyjścia:

usage: test.py [-h] [-a ARGUMENT] 
       [-u {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}] 

Argparse default formatter. 

optional arguments: 
    -h, --help   show this help message and exit 
    -a ARGUMENT, --argument ARGUMENT 
         not too long 
    -u {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}, --ugly {0,1,2,3,4,5,6, 
7,8,9,10,11,12,13,14,15,16,17,18,19} 
         looks messy 

niestandardowe zachowanie formater:

parser = argparse.ArgumentParser(description="Argparse custom formatter.", 
           formatter_class=CustomFormatter) 
parser.add_argument('-a', '--argument', help='not too long') 
parser.add_argument('-l', '--less-ugly', choices=range(20), help='less messy') 

wyjścia:

usage: test.py [-h] [-a ARGUMENT] 
       [-l {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}] 

Argparse custom formatter. 

optional arguments: 
    -h, --help   show this help message and exit 
    -a ARGUMENT, --argument ARGUMENT 
         not too long 
    -l {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}, 
    --less-ugly {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19} 
         less messy 
Powiązane problemy