parser example argumentparser arg_parser add_argument python argparse

example - python argparse argumentparser()



tipos de ruta de directorio con argparse (3)

Mi script python necesita leer archivos de un directorio pasado en la línea de comando. He definido un tipo de archivo legible como se muestra a continuación para usarlo con argparse para validar que el directorio pasado en la línea de comando es existente y legible. Además, también se ha especificado un valor predeterminado (/ tmp / non_existent_dir en el ejemplo a continuación) para el argumento del directorio. El problema aquí es que argparse invoca a readable_dir () en el valor predeterminado incluso en una situación donde un argumento de directorio se pasa explícitamente en la línea de comando. Esto provoca que la secuencia de comandos falle ya que la ruta predeterminada / tmp / non_existent_dir no existe en un contexto donde un directorio se pasa explícitamente en la línea de comando. Podría evitar esto al no especificar un valor predeterminado y hacer que este argumento sea obligatorio, o posponiendo la validación hasta más adelante en el guión, pero ¿es una solución más elegante que todos conocen?

#!/usr/bin/python import argparse import os def readable_dir(prospective_dir): if not os.path.isdir(prospective_dir): raise Exception("readable_dir:{0} is not a valid path".format(prospective_dir)) if os.access(prospective_dir, os.R_OK): return prospective_dir else: raise Exception("readable_dir:{0} is not a readable dir".format(prospective_dir)) parser = argparse.ArgumentParser(description=''test'', fromfile_prefix_chars="@") parser.add_argument(''-l'', ''--launch_directory'', type=readable_dir, default=''/tmp/non_existent_dir'') args = parser.parse_args()


Envié un parche para "argumentos de ruta" a la lista de correo de la biblioteca estándar de Python hace unos meses.

Con esta clase PathType , puede simplemente especificar el siguiente tipo de argumento para hacer coincidir solo un directorio existente; cualquier otra cosa dará un mensaje de error:

type = PathType(exists=True, type=''dir'')

Aquí está el código, que podría modificarse fácilmente para requerir permisos específicos de archivos / directorios:

from argparse import ArgumentTypeError as err import os class PathType(object): def __init__(self, exists=True, type=''file'', dash_ok=True): ''''''exists: True: a path that does exist False: a path that does not exist, in a valid parent directory None: don''t care type: file, dir, symlink, None, or a function returning True for valid paths None: don''t care dash_ok: whether to allow "-" as stdin/stdout'''''' assert exists in (True, False, None) assert type in (''file'',''dir'',''symlink'',None) or hasattr(type,''__call__'') self._exists = exists self._type = type self._dash_ok = dash_ok def __call__(self, string): if string==''-'': # the special argument "-" means sys.std{in,out} if self._type == ''dir'': raise err(''standard input/output (-) not allowed as directory path'') elif self._type == ''symlink'': raise err(''standard input/output (-) not allowed as symlink path'') elif not self._dash_ok: raise err(''standard input/output (-) not allowed'') else: e = os.path.exists(string) if self._exists==True: if not e: raise err("path does not exist: ''%s''" % string) if self._type is None: pass elif self._type==''file'': if not os.path.isfile(string): raise err("path is not a file: ''%s''" % string) elif self._type==''symlink'': if not os.path.symlink(string): raise err("path is not a symlink: ''%s''" % string) elif self._type==''dir'': if not os.path.isdir(string): raise err("path is not a directory: ''%s''" % string) elif not self._type(string): raise err("path not valid: ''%s''" % string) else: if self._exists==False and e: raise err("path exists: ''%s''" % string) p = os.path.dirname(os.path.normpath(string)) or ''.'' if not os.path.isdir(p): raise err("parent path is not a directory: ''%s''" % p) elif not os.path.exists(p): raise err("parent directory does not exist: ''%s''" % p) return string


Puede crear una acción personalizada en lugar de un tipo:

import argparse import os import tempfile import shutil import atexit class readable_dir(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): prospective_dir=values if not os.path.isdir(prospective_dir): raise argparse.ArgumentTypeError("readable_dir:{0} is not a valid path".format(prospective_dir)) if os.access(prospective_dir, os.R_OK): setattr(namespace,self.dest,prospective_dir) else: raise argparse.ArgumentTypeError("readable_dir:{0} is not a readable dir".format(prospective_dir)) ldir = tempfile.mkdtemp() atexit.register(lambda dir=ldir: shutil.rmtree(ldir)) parser = argparse.ArgumentParser(description=''test'', fromfile_prefix_chars="@") parser.add_argument(''-l'', ''--launch_directory'', action=readable_dir, default=ldir) args = parser.parse_args() print (args)

Pero esto me parece un poco sospechoso: si no se proporciona un directorio, pasa un directorio no legible que parece frustrar el propósito de verificar si el directorio es accesible en primer lugar.

Tenga en cuenta que, como se señaló en los comentarios, podría ser mejor
raise argparse.ArgumentError(self, ...) lugar de argparse.ArgumentTypeError .

EDITAR

Por lo que sé, no hay forma de validar el argumento predeterminado. Supongo que los desarrolladores de argparse simplemente asumieron que si está proporcionando un valor predeterminado, entonces debería ser válido. Lo más rápido y más fácil de hacer aquí es simplemente validar los argumentos inmediatamente después de analizarlos. Parece que solo intentas obtener un directorio temporal para hacer algo de trabajo. Si ese es el caso, puede usar el módulo tempfile para que tempfile un nuevo directorio. tempfile mi respuesta anterior para reflejar esto. Creo un directorio temporal, utilizo eso como el argumento predeterminado ( tempfile ya garantiza que el directorio que crea será grabable) y luego lo registro para que se elimine cuando se cierre el programa.


Si su script no puede funcionar sin un launch_directory válido, entonces se debe convertir en un argumento obligatorio:

parser.add_argument(''launch_directory'', type=readable_dir)

Por cierto, debería usar argparse.ArgumentTypeError lugar de Exception en argparse.ArgumentTypeError readable_dir() .