2012-08-28 18 views
5

Me gustaría poder usar las opciones globales de un objeto argparse.ArgumentParser para anular/aumentar los valores predeterminados para un comando secundario.argparse aumenta los valores predeterminados del sub-comando a través de las opciones globales

Un ejemplo sería que la ayuda que se muestra refleja los cambios globales, es decir, para el siguiente ejemplo de juguete:

import argparse 
import os 
import sys 

# Global parser and options. 
parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter) 

parser.add_argument("--user", 
        dest="user", 
        default=os.environ.get("USER"), 
        help="Override the setting of the $USER variable.") 

# Sub-command parser and options. 
subparsers = parser.add_subparsers() 

command = subparsers.add_parser(
    "command", 
    formatter_class=argparse.ArgumentDefaultsHelpFormatter) 

command.add_argument("--config", 
        dest="config", 
        default="~%s/config" % os.environ.get("USER"), 
        help="The config file.") 

options = parser.parse_args() 

Lo ideal sería que cuando al ejecutar esto en modo de ayuda me gustaría conseguir,

> python example.py --user thing command --help 
usage: example.py command [-h] [--config CONFIG] 

optional arguments: 
    -h, --help  show this help message and exit 
    --config CONFIG The config file. (default: ~thing/config) 

es decir, la ruta del archivo de configuración es específica del usuario (cosa). Me doy cuenta de que podría cambiar la configuración predeterminada para que sea "~%(user)s/config" y luego resolver esto en tiempo de ejecución con el espacio de nombres de las opciones; sin embargo, me gustaría que la ayuda sea más explícita.

que se reúnen una solución alternativa sería la de tratar de analizar los argumentos de una vez para obtener las opciones globales, es decir,

if "--help" in sys.argv: 

    # Parse the command minus the help to obtain the global options. 
    args = sys.argv[1:] 
    args.remove("--help") 

    # Update the defaults with the global options. 
    options = parser.parse_args(args) 
    command.set_defaults(config="~%s/config" % options.user) 

    # Re-parse the options. 
    parser.parse_args() 

Aunque esto parece un poco hacky. ¿Hay un mejor enfoque/solución?

Respuesta

4

Después de definir las opciones globales, pero antes de definir los subcomandos, llame al parse_known_args para averiguar cuál es el valor de --user. Luego, termine de definir los comandos del subparser, usando el valor de --user para definir el valor predeterminado de --config, antes de llamando al parse_args para analizar todas las opciones en la línea de comando.

Esto es un poco diferente de su alternativa, pero mantiene todo el procesamiento de línea de comandos dentro del objeto argparse.

(I recortado su código un poco sólo para acortarlo para esta respuesta.)

import os 
import argparse 

# Global preparser and options. 
preparser = argparse.ArgumentParser(add_help=False) 
preparser.add_argument("--user", dest="user", default=os.environ.get("USER")) 

# ****** NEW ******* 
options, _ = preparser.parse_known_args() # Ignore what we haven't defined yet 
user = options.user      # Use this to define the default of --config 

parser = argparse.ArgumentParser(parents=[ preparser ], add_help=True, 
        formatter_class=argparse.ArgumentDefaultsHelpFormatter) 

# Sub-command parser and options. 
subparsers = parser.add_subparsers() 

command = subparsers.add_parser("command") 

# ****** MODIFIED ******* 
command.add_argument("--config", dest="config", default="~%s/config" % (user,)) 

options = parser.parse_args() 
+0

Gracias por la respuesta. Este enfoque es definitivamente mucho más limpio/simple que mi enfoque y más similar a un enfoque que estaba intentando sin argparse, es decir, analizar los argumentos dos veces. Puedo buscar en el código para ver cómo puedo obtener parse_known_args() para regresar con opciones (en lugar de salir) cuando se proporciona el argumento "--ayuda" en lugar de eliminarlo según mi ejemplo. – John

+0

Oh, me olvidé de eso. Puede capturar la excepción 'SystemExit' que' parse_known_args() 'lanzará cuando llame a 'ArgumentParser.exit' (que es un contenedor relativamente delgado alrededor de' sys.exit'). – chepner

+0

Oye, sí, lo he intentado, sin embargo, el problema con el try/except es que las opciones no están definidas. – John

0

he aquí una solución que modifica la línea de ayuda directa, en tiempo de ejecución. También podría modificar os.environ o algún otro modelo global, pero lo mantendré simple. La clave es asignar la acción creada por add_argument('--config'...) a una variable. help es solo un atributo de esa variable. Usted es libre de modificar eso.

class FooAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     config.help = 'new user (%s)'% values 
     setattr(namespace, self.dest, values) 

parser = argparse.ArgumentParser() 
parser.add_argument('--user',action=FooAction) 
sub = parser.add_subparsers() 
cmd = sub.add_parser('cmd') 
config = cmd.add_argument('--config',help='initial help') 
config.help = 'default help' # alt way of setting 'help' 
# print config # to see other attributes of the config action 
args = parser.parse_args() 
print args 

Se invoca con sólo cmd -h obtenemos la default help

$ python stack12167228.py cmd -h 
usage: stack12167228.py cmd [-h] [--config CONFIG] 

optional arguments: 
    -h, --help  show this help message and exit 
    --config CONFIG default help 

Se invoca con --user xxx cmd -h, la ayuda se personaliza

$ python stack12167228.py --user xxx cmd -h 
usage: stack12167228.py cmd [-h] [--config CONFIG] 

optional arguments: 
    -h, --help  show this help message and exit 
    --config CONFIG new user (xxx) 
Cuestiones relacionadas