Nuestros mejores investigadores han agotado sus depósitos de café, investigando todo el tiempo por la respuesta, hasta que Pablo halló la solución en GitLab y ahora la comparte aquí.
Solución:
Puedes resolver esto usando un personalizado argparse.Action
eso abre el archivo, analiza el contenido del archivo y luego agrega los argumentos.
Por ejemplo, esta sería una acción muy simple:
class LoadFromFile (argparse.Action):
def __call__ (self, parser, namespace, values, option_string = None):
with values as f:
# parse arguments in the file and store them in the target namespace
parser.parse_args(f.read().split(), namespace)
Que puedes usar así:
parser = argparse.ArgumentParser()
# other arguments
parser.add_argument('--file', type=open, action=LoadFromFile)
args = parser.parse_args()
El espacio de nombres resultante en args
también contendrá cualquier configuración que también se cargó desde el archivo donde el archivo contenía argumentos en la misma sintaxis que en la línea de comando (por ejemplo, --foo 1 --bar 2
).
Si necesita un análisis más sofisticado, también puede analizar la configuración en el archivo por separado primero y luego elegir selectivamente qué valores se deben tomar. Por ejemplo, dado que los argumentos se evalúan en el orden en que se especifican, podría tener sentido evitar que las configuraciones en el archivo sobrescriban los valores que se especificaron explícitamente en la línea de comando. Esto permitiría usar el archivo de configuración por defecto:
def __call__ (self, parser, namespace, values, option_string=None):
with values as f:
contents = f.read()
# parse arguments in the file and store them in a blank namespace
data = parser.parse_args(contents.split(), namespace=None)
for k, v in vars(data).items():
# set arguments in the target namespace if they haven’t been set yet
if getattr(namespace, k, None) is not None:
setattr(namespace, k, v)
Por supuesto, también puede hacer que la lectura del archivo sea un poco más complicada, por ejemplo, leer primero desde JSON.
tu comentaste eso
Necesito poder escribir mi propia función para leer ese archivo y devolver los argumentos (no está en un formato de un argumento por línea) –
Hay una disposición en el actual prefix-controlador de archivos para cambiar la forma en que se lee el archivo. El archivo es leído por un método ‘privado’, parser._read_args_from_files
pero llama a un método público simple que convierte una línea en cadenas, acción predeterminada de un argumento por línea:
def convert_arg_line_to_args(self, arg_line):
return [arg_line]
Fue escrito de esta manera para que puedas personalizarlo fácilmente. https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.convert_arg_line_to_args
Una anulación útil de este método es la que trata cada palabra separada por espacios como un argumento:
def convert_arg_line_to_args(self, arg_line):
for arg in arg_line.split():
if not arg.strip():
continue
yield arg
En el test_argparse.py
unittesting archivo hay un caso de prueba para esta alternativa.
Pero si aún desea activar esta lectura con una opción de argumento, en lugar de una prefix personaje, entonces el enfoque de acción personalizado es bueno.
Sin embargo, podría escribir su propia función que procesa argv
antes de pasar a la parser
. Se podría modelar en parser._read_args_from_files
.
Así que podrías escribir una función como:
def read_my_file(argv):
# if there is a '-A' string in argv, replace it, and the following filename
# with the contents of the file (as strings)
# you can adapt code from _read_args_from_files
new_argv = []
for a in argv:
....
# details left to user
return new_argv
Luego invoque su analizador con:
parser.parse_args(read_my_file(sys.argv[1:]))
Y sí, esto podría estar envuelto en un ArgumentParser
subclase.