2012-09-13 16 views
7

argparse de Python 2.7 le ofrece dos puntos de extensión donde puede controlar cómo se analizan sus argumentos de línea de comandos: funciones de tipo y clases de acción.Al usar argparse, ¿debería ocurrir la validación y la inicialización en tipos o acciones personalizados?

Al pasar de los tipos y acciones incorporados, la mejor práctica parece ser que las funciones de tipo deben contener el código de validación/inicialización y las acciones deben estar relacionadas con el almacenamiento de valores en el espacio de nombres. El problema con este enfoque es cuando tiene un código de verificación de tipo que tiene efectos secundarios. Considere este ejemplo sencillo:

from argparse import ArgumentParser, FileType 
argp = ArgumentParser() 
argp.add_argument('-o', type=FileType('w'), default='myprog.out') 
argp.parse_args(['-o', 'debug.out']) 

Si ejecuta este, se encuentra pitón se abrirá dos archivos en el sistema, y ​​myprog.outdebug.out. Tendría más sentido abrir solo debug.out cuando el usuario no proporciona el argumento -o.

Meneando un poco, parece que argparse solo llamará a su función de tipo sobre argumentos pasados ​​o argumentos predeterminados de tipo str. Esto es desafortunado si su verificador de tipos tiene efectos secundarios, ya que se llamará por defecto incluso si se pasó un valor. Entonces, para la inicialización con efectos secundarios, tal vez sería mejor hacerlo en la acción. ¡El problema con esto es que no se llamará a la acción si proporciona un valor predeterminado!

Considere el siguiente código:

from argparse import ArgumentParser, Action 

def mytype(arg): 
    print 'checking type for ' + repr(arg) 
    return arg 

class OutputFileAction(Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     print 'running action for ' + repr(values) 
     try: 
      outstream = open(values, 'w') 
     except IOError as e: 
      raise ArgumentError('error opening file ' + values) 
     setattr(namespace, self.dest, outstream) 

argp = ArgumentParser() 
argp.add_argument('-o', type=mytype, action=OutputFileAction, default='myprog.out') 

Ahora trato de usarlo:

>>> argp.parse_args([]) 
checking type for 'myprog.out' 
Namespace(o='myprog.out') 
>>> argp.parse_args(['-o', 'debug.out']) 
checking type for 'myprog.out' 
checking type for 'debug.out' 
running action for 'debug.out' 
Namespace(o=<open file 'debug.out', mode 'w' at 0x2b7fced07300>) 

¿Quién ordenó que comportamiento? ¿Hay una manera sensata de que los valores predeterminados se comporten exactamente como si el usuario los hubiera pasado? ¿O no registrar los valores predeterminados cuando se suministran los valores?

Respuesta

2

Por lo que yo sé, no hay una forma "sensata" de hacerlo. Por supuesto, es trivial para salir de la conversión type apagar y postproceso la Namespace regresaron de parse_args:

args = argp.parse_args() 
args.o = open(args.o,'w') 

pero supongo que eso no es lo que estás buscando.

+1

Sí, por supuesto, esto funciona. La razón por la que quiero hacerlo en validador de tipo o clase de acción es que estoy escribiendo un conjunto de herramientas de línea de comandos que tienen argumentos similares y quiero factorizar el código que analiza, valida y crea una instancia de esos argumentos en un común conjunto de funciones/clases. El código es más claro cuando el argumento que maneja la rutina se especifica dentro de la definición del argumento. – Nick

+0

Creo que podrías manejar esto si subescribieras ArgumentParser sin demasiado esfuerzo ... – mgilson

+0

Me pregunto cuántos programas se romperían si el comportamiento fuera simplemente llamar al verificador de tipos y actuar solo sobre el argumento aplicado (ya sea cadena pasada). Con la excepción de que el paso de verificación de tipo se omitirá si el valor predeterminado no es de tipo basetring. – Nick

Cuestiones relacionadas