2011-03-10 25 views
9

Me gustaría tener un argumento para mi programa que tenga algunos parámetros requeridos junto con algunos parámetros opcionales. Algo como esto:Python argparse subargumentos opcionales

[--print text [color [size]] 

por lo que podría pasar alguna de estas:

mycommand --print hello 
mycommand --print hello blue 
mycommand --print hello red 12 

Podría ser múltiplo de éstos así que tiene que ser una sola add_argument. Por ejemplo:

[--print text [color]] [--output filename [overwrite]] 

puedo lograr argumentos que están cerca de lo que quiero:

>>> parser = argparse.ArgumentParser() 
>>> act = parser.add_argument('--foo', nargs=3, metavar=('x','y','z')) 
>>> act = parser.add_argument('--bar', nargs='?') 
>>> act = parser.add_argument('--baz', nargs='*') 
>>> parser.print_help() 
usage: [-h] [--foo x y z] [--bar [BAR]] [--baz [BAZ [BAZ ...]]] 

optional arguments: 
    -h, --help   show this help message and exit 
    --foo x y z 
    --bar [BAR] 
    --baz [BAZ [BAZ ...]] 

pero no del todo. ¿Hay alguna manera de hacer esto con argparse? Sé que podría hacerlos todos nargs="*" pero luego --help no enumeraría los nombres de los argumentos opcionales. Si paso nargs="*" y una tupla para metavar, argparse lanza una excepción.

Respuesta

5

Leyendo el source code (empiece en take_action), creo que lo que quiere es imposible. Todo argumento que se analiza y se pasa a acciones se hace en base a nargs, y nargs es un número, OPTIONAL ("?"), ZERO_OR_MORE ("*"), ONE_OR_MORE ("+"), PARSER o REMAINDER. Esto debe determinarse antes de que el objeto Action (que maneja la entrada) incluso vea lo que está obteniendo, por lo que no puede determinar dinámicamente nargs.

Creo que tendrá que vivir con una solución alternativa. Tal vez tenga --foo-x x, --foo-y y y --foo-z z, y quizás también --foo x y z.

+1

Gracias. Eso es muy malo. – jterrace

7

¿Qué tal

def printText(args): 
    print args 

parser = argparse.ArgumentParser() 
subparser = parser.add_subparsers() 
printer = subparser.add_parser('print') 
printer.add_argument('text') 
printer.add_argument('color', nargs='?') 
printer.add_argument('size', type=int, nargs='?') 
printer.set_defaults(func=printText) 

cmd = parser.parse_args() 
cmd.func(cmd) 

Entonces usted consigue algo como esto:

$ ./test.py -h 
usage: test.py [-h] {print} ... 

positional arguments: 
    {print} 

$ ./test.py print -h 
usage: test.py print [-h] text [color] [size] 

positional arguments: 
    text 
    color 
    size 

$ ./test.py print text 
Namespace(color=None, func=<function printText at 0x2a96150b90>, size=None, text='text') 

$ ./test.py print text red 
Namespace(color='red', func=<function printText at 0x2a96150b90>, size=None, text='text') 

$ ./test.py print text red 12 
Namespace(color='red', func=<function printText at 0x2a96150b90>, size=12, text='text') 
+0

Pero solo puede especificar un subparser a la vez, esto no funcionará para múltiples. – jterrace

+0

¿Puedes dar un ejemplo para múltiples? Mi ejemplo se reduce a argumentos posicionales opcionales, ambos nargs = '?', Por lo que dependiendo de lo que necesite hacer, podría lograrse sin los subparsers. o múltiples subparsers .. :) – CNeo

+0

Tengo el ejemplo en mi OP: mycommand --print hello red 12 --output nombre de archivo sobrescribir – jterrace

0

que funcione para un solo arg:

parser.add_argument('--write_google', nargs='?', const='Yes', 
        choices=['force', 'Yes'], 
        help="write local changes to google") 
1

Según la respuesta de Devin Jeanpierre, parece que usar '+' (uno o más) en lugar de '*' haría lo que estás tratando de lograr. (PD: Acabo de comentar en su respuesta si tuviera suficientes puntos)

+0

No lo olvides-- él tampoco quiere tener más de tres argumentos.Si incorpora esto en su publicación, puede publicar una respuesta independiente (si usted, por supuesto, acredita a Jean Pierre por su idea). – Alex

Cuestiones relacionadas