2014-11-18 18 views
10

Sto provando a scrivere una funzione con cui è possibile analizzare una quantità variabile di argomenti tramite argparse: so che posso farlo tramite nargs="+". Purtroppo, il modo in cui l'aiuto di argparse funziona (e il modo in cui le persone generalmente scrivono argomenti nella CLI) mette gli argomenti posizionali alla fine. Questo porta al mio argomento posizionale che viene catturato come parte degli argomenti opzionali.Argparse - non catturare argomenti posizionali con `nargs`.

#!/usr/bin/python 
import argparse 

parser = argparse.ArgumentParser() 
parser.add_argument("positional", help="my positional arg", type=int) 
parser.add_argument("-o", "--optional", help="my optional arg", nargs='+', type=float) 
args = parser.parse_args() 
print args.positional, args.optional 

l'esecuzione di questo come ./test.py -h mostra la seguente istruzione di uso:

usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] positional 

ma se corro ./test.py -o 0.21 0.11 0.33 0.13 100 mi dà

test.py: error: too few arguments 

per ottenere una corretta analisi di argomenti, devo correre ./test.py 100 -o 0.21 0.11 0.33 0.13

Quindi, come:

  • fanno argparse riformattare l'output utilizzo in modo che sia meno fuorviante, o, meglio ancora:

  • dire argparse di non prendere l'ultimo elemento per l'argomento opzionale -o se è l'ultimo in list

?

risposta

12

C'è un bug report su questo: http://bugs.python.org/issue9338

optional argparse con nargs =, '*' o '+' non può essere seguito da positionals

Un semplice '?' (utente) correzione è di usare -- per separare postionals da optionals:

./test.py -o 0.21 0.11 0.33 0.13 -- 100 

ho scritto una patch che riserva alcuni degli argomenti per l'utilizzo da parte del positio nale. Ma non è banale.

Per quanto riguarda la modifica della linea di utilizzo - la cosa più semplice è quello di scrivere il proprio, per es .:

usage: test.py [-h] positional [-o OPTIONAL [OPTIONAL ...]] 
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] -- positional 

non mi consiglia di aggiungere la logica per il formattatore utilizzo per rendere questo tipo di cambiamento. Penso che diventerebbe troppo complesso.

Un'altra soluzione rapida consiste nel trasformare questo posizionale in un (obbligatorio) facoltativo. Dà all'utente completa libertà per quanto riguarda il loro ordine e potrebbe ridurre la confusione. Se non si vuole confondere un 'optional richiesto' basta dargli un valore logico predefinito.

usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] -p POSITIONAL 
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] [-p POS_WITH_DEFAULT] 

Una semplice modifica al Help_Formatter è quello di elencare semplicemente gli argomenti nell'ordine in cui sono definite.Il modo normale di modificare il comportamento del formattatore consiste nel suddividerlo in sottoclasse e cambiare uno o due metodi. Molti di questi metodi sono "privati" (prefisso _), quindi lo fai con la consapevolezza che il codice futuro potrebbe cambiare (lentamente).

In questo metodo, actions è l'elenco di argomenti, nell'ordine in cui sono stati definiti. Il comportamento predefinito è dividere gli "optionals" da "positionals" e riassemblare l'elenco con positionals alla fine. C'è un codice aggiuntivo che gestisce le lunghe code che necessitano di un wrapping. Normalmente mette posizionali su una linea separata. L'ho omesso.

class Formatter(argparse.HelpFormatter): 
    # use defined argument order to display usage 
    def _format_usage(self, usage, actions, groups, prefix): 
     if prefix is None: 
      prefix = 'usage: ' 

     # if usage is specified, use that 
     if usage is not None: 
      usage = usage % dict(prog=self._prog) 

     # if no optionals or positionals are available, usage is just prog 
     elif usage is None and not actions: 
      usage = '%(prog)s' % dict(prog=self._prog) 
     elif usage is None: 
      prog = '%(prog)s' % dict(prog=self._prog) 
      # build full usage string 
      action_usage = self._format_actions_usage(actions, groups) # NEW 
      usage = ' '.join([s for s in [prog, action_usage] if s]) 
      # omit the long line wrapping code 
     # prefix with 'usage:' 
     return '%s%s\n\n' % (prefix, usage) 

parser = argparse.ArgumentParser(formatter_class=Formatter) 

che produce una linea di utilizzo come:

usage: stack26985650.py [-h] positional [-o OPTIONAL [OPTIONAL ...]] 
6

Invece di utilizzare nargs="+", considerare l'utilizzo di action="append". Ciò richiede il passaggio di -o di fronte a ciascun numero, ma non consumerà argomenti a meno che non lo si voglia effettivamente.