Saltar al contenido

cómo hacer que argparse lea argumentos de un archivo con una opción en lugar de prefix

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_filespero 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.

Te mostramos reseñas y calificaciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *