¿Es esta la forma correcta de importar scripts de Python que residen en carpetas arbitrarias?

Este fragmento es de una respuesta anterior aquí en SO. Tiene alrededor de un año (y la respuesta no fue aceptada). Soy nuevo en Python y encuentro que la ruta del sistema es un verdadero problema. Tengo algunas funciones escritas en scripts en diferentes directorios, y me gustaría poder importarlas en nuevos proyectos sin tener que saltar a través de aros.

Este es el fragmento:

def import_path(fullpath): """ Import a file with full path specification. Allows one to import from anywhere, something __import__ does not do. """ path, filename = os.path.split(fullpath) filename, ext = os.path.splitext(filename) sys.path.append(path) module = __import__(filename) reload(module) # Might be out of date del sys.path[-1] return module 

Es de aquí: ¿Cómo hacer importaciones relativas en Python?

Me gustaría saber si puedo usarlo o no, y si hay efectos secundarios no deseados que pueden no ser obvios para un novato.

Tengo la intención de usar algo como esto:

 import_path(/home/pydev/path1/script1.py) script1.func1() 

etc

¿Es “seguro” usar la función de la manera que pretendo?

El enfoque “oficial” y completamente seguro es el módulo imp de la biblioteca estándar de Python.

Use imp.find_module para encontrar el módulo en su lista de directorios aceptables, especificados con precisión: devuelve una tupla de 3 (file, pathname, description) ; si no tiene éxito, el file es en realidad None (pero también puede boost ImportError para que debe usar un try / except para eso, así como comprobar if file is None: .

Si la búsqueda es exitosa, llame a imp.load_module (¡en un try / finally para asegurarse de cerrar el archivo!) Con los tres argumentos anteriores después del primero que debe ser el mismo name que pasó a find_module – devuelve el módulo objeto (phew ;-).

Se siente como un truco, pero en este momento, no puedo pensar en ningún efecto secundario no deseado que pueda ocurrir, al menos no mientras esté usando esto para sus propios scripts. Básicamente, lo que hace es agregar temporalmente el directorio principal del archivo especificado (en su ejemplo, /home/pydev/path1/ ) a la lista de rutas que Python comprueba cuando está buscando un módulo para importar.

El único riesgo que se me ocurre ahora podría surgir en un entorno multiproceso, donde dos o más subprocesos (o procesos) ejecutan esta función simultáneamente. Si el hilo A quiere importar el módulo A de la ruta dirA/A.py , y el hilo B quiere importar el módulo B de la ruta dirB/B.py , usted terminará con dirA y dirB en sys.path por un corto tiempo . Y si hay un archivo llamado B.py en dirA , es posible que el hilo B encuentre que ( dirA/B.py ) en lugar del archivo que está buscando ( dirB/B.py ), importando así el módulo incorrecto. Por esta razón, no lo usaría en el código de producción o en el código que usted va a distribuir a otras personas (¡al menos no sin advertirles que este truco está aquí!). En una situación como esa, podría escribir una función más compleja que le permita especificar el archivo para importar sin alterar el conjunto estándar de rutas. (Eso es lo que hace mod_python, por ejemplo)

Me preocuparía que su nombre de script se corresponda con un módulo que se muestre anteriormente en la ruta. Para disipar este temor, reemplazaría completamente la ruta con una nueva lista que contenga solo el directorio que contiene el módulo, y luego la volvería a colocar una vez que la importación haya finalizado. Además, debes envolver esto en algún tipo de locking para que varios subprocesos que intentan hacer lo mismo no interfieran entre sí.

Como se mencionó, por favor considere la seguridad del hilo, si es apropiado. Prefiero algo más cercano a una solución publicada en una publicación similar. Las principales diferencias a continuación: el uso de inserción para especificar la prioridad de la importación, la restauración correcta de sys.path utilizando try … finalmente y la configuración del espacio de nombres global.

 # inspired by Alex Martelli's solution to # http://stackoverflow.com/questions/1096216/override-namespace-in-python/1096247#1096247 def import_from_absolute_path(fullpath, global_name=None): """Dynamic script import using full path.""" import os import sys script_dir, filename = os.path.split(fullpath) script, ext = os.path.splitext(filename) sys.path.insert(0, script_dir) try: module = __import__(script) if global_name is None: global_name = script globals()[global_name] = module sys.modules[global_name] = module finally: del sys.path[0]