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.out
debug.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?
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
Creo que podrías manejar esto si subescribieras ArgumentParser sin demasiado esfuerzo ... – mgilson
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