2012-02-20 2 views
35

Ho questo codice che io sono generalmente soddisfatto:Python argparse: un sacco di scelte risultati in brutta aiuto uscita

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

mi piace che la scelta = server convalida i nomi dei server in ingresso per me, così che non dovrò. Tuttavia, avendo così tante scelte valide rende l'uscita aiuto aspetto terribile:

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 

Da che parte mi consiglia se voglio:

  • Nizza (per lo più) auto-generata aiuto uscita
  • convalida che le voci date alle opzioni -o o -s sono in servers.

Bonus:

  • Sarebbe possibile avere caso stringa insensitive di corrispondenza per i nomi dei server?

Append

Ho provato ad utilizzare michaelfilms suggerimento dove i -o-s opzioni vengono rimossi dal uscita di cui sopra e si aggiunge questa parte:

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

penso che sembra piuttosto buono, ma Ho davvero bisogno di fornire aiuto per le opzioni -o e -s in quanto l'utente non sarebbe a conoscenza di loro altrimenti. Quindi non sono ancora lì usando ancora questo approccio.

risposta

3

Perché non utilizzare parser.add_argument_group per creare un gruppo per le opzioni basate sul server e fornire un argomento di descrizione per visualizzare l'elenco delle possibili scelte? Quindi passare argparse.SUPPRESS nell'aiuto per ciascuna delle singole opzioni. Credo che ti darà quello che vuoi.

+0

Ho provato e sembra abbastanza buono. Ma non sono ancora arrivato là usando ancora questo approccio. Devo aiutare l'utente a fargli conoscere le opzioni '-o' e' -s'. – Deleted

4

Per ottenere l'output previsto, è necessario creare una sottoclasse di argparse.HelpFormatter e implementare la formattazione necessaria. In particolare, è necessario implementare il proprio metodo _metavar_formatter, che è l'unico incaricato di unire tutte le scelte in una singola stringa separata da virgole.

8

Ho lo stesso problema e, per ovviare al problema, ho utilizzato l'epilog per descrivere ciascuna delle opzioni disponibili. Ho dovuto usare argparse.RawTextHelpFormatter, che ti permette di specificare che l'epilog è pre-formattato.

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

Non è necessario sottoclasse nulla. È sufficiente passare un argomento metavar con la stringa che si desidera visualizzare nel messaggio di guida.

Vedere il argparse documentation per dettagli.

37

Fondamentalmente sto ripetendo quello che ha detto Ernest - per evitare la brutta lunga lista di scelte, imposta metavar = '' per gli argomenti basati sulla scelta (anche se non eliminerà lo spazio tra l'argomento e la virgola (ad es. -o , anziché -o,). È quindi possibile descrivere le scelte disponibili in dettaglio nella descrizione generale (RawDescriptionHelpFormatter è utile qui se si desidera che vengano elencate con indentazione evidente).

Non capisco perché la risposta di Ernest sia stata respinta. Questo codice

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

produce il seguente output aiuto

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 

Questo è ciò che si spera post originale stava cercando.

+0

L'impostazione metavar su "" o Nessuna porta a non visualizzare alcun aiuto su.move_parser.add_argument ('old_host', action = 'store', choices = distinct_host, help = "Vecchio host" + ','. Join (distinct_host), metavar = Nessuno) – user2601010

+0

'metavar = ''' sembra causare un errore ora. Impostandolo su qualsiasi cosa diversa da una stringa vuota (come il nome dell'argomento) lo corregge. –

1

http://bugs.python.org/issue16468argparse only supports iterable choices è il problema relativo agli errori relativi alla formattazione delle scelte. L'elenco delle scelte può essere visualizzato in 3 posizioni: la riga di utilizzo, le linee guida e i messaggi di errore.

Tutto ciò di cui si preoccupa il parser è eseguire un test (__contains__). Ma per la formattazione, lunghi elenchi, 'liste' illimitate (ad esempio interi> 100) e altri oggetti che non sono iterabili danno problemi. metavar è il modo in cui gli utenti attuali possono aggirare la maggior parte dei problemi di formattazione (potrebbe non essere d'aiuto con i messaggi di errore). Guarda il problema per avere idee su come cambiare la tua versione di argparse.

3

Questo non aiuta in situazioni in cui l'elenco delle opzioni è estremamente lungo, come nella domanda originale, ma per quelle persone che, come me, hanno trovato questa domanda alla ricerca di un modo per spezzare le stringhe di opzioni moderatamente lunghe in due righe , qui è la mia soluzione:

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) 

Questo codice sovrascrive il metodo argparse.HelpFormatter _format_action_invocation di default, ed è identico alla implementazione di default se non nelle ultime quattro righe.

predefinito comportamento formattatore:

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

uscite:

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 

personalizzato comportamento formattatore:

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

uscite:

0.123.516,410617 millions
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