Python: módulo de importación desde otro directorio en el mismo nivel en la jerarquía del proyecto

He visto todo tipo de ejemplos y otras preguntas similares, pero parece que no puedo encontrar un ejemplo que coincida exactamente con mi escenario. Me siento como un matón total al preguntar esto porque hay muchas preguntas similares, pero parece que no puedo hacer que esto funcione “correctamente”. Aquí está mi proyecto:

user_management (package) | |------- __init__.py | |------- Modules/ | | | |----- __init__.py | |----- LDAPManager.py | |----- PasswordManager.py | |------- Scripts/ | | | |----- __init__.py | |----- CreateUser.py | |----- FindUser.py 

Si muevo “CreateUser.py” al directorio principal de administración de usuarios, puedo usar fácilmente: "import Modules.LDAPManager" para importar LDAPManager.py — esto funciona. Lo que no puedo hacer (lo que quiero hacer) es mantener CreateUser.py en la subcarpeta Scripts e importar LDAPManager.py. Tenía la esperanza de lograr esto mediante el uso de "import user_management.Modules.LDAPManager.py" . Esto no funciona. En resumen, puedo hacer que los archivos de Python se vean con mayor profundidad en la jerarquía, pero no puedo obtener una secuencia de comandos de Python para hacer referencia a un directorio y a otro.

Tenga en cuenta que puedo resolver mi problema usando:

 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import Modules.LDAPManager as LDAPManager 

He oído que esto es una mala práctica y desalentado.

Los archivos en Scripts están diseñados para ejecutarse directamente (¿es el init .py en Scripts incluso necesario?). He leído que, en este caso, debería estar ejecutando CreateUser.py con la marca -m. He intentado algunas variaciones en esto y parece que no puedo lograr que CreateUser.py reconozca LDAPManager.py.

Si muevo CreateUser.py al directorio principal de administración de usuarios, puedo usar fácilmente: import Modules.LDAPManager para importar LDAPManager.py — esto funciona.

Por favor no lo hagas De esta manera, el módulo LDAPManager utilizado por CreateUser no será el mismo que el importado a través de otras importaciones. Esto puede crear problemas cuando se tiene algún estado global en el módulo o durante el proceso de decapado y decapado. Evite las importaciones que funcionan solo porque el módulo está en el mismo directorio.

Cuando tienes una estructura de paquete debes:

  • Utilice las importaciones relativas, es decir, si el CreateUser.py está en Scripts/ :

      from ..Modules import LDAPManager 

    Tenga en cuenta que PEP 8 desalentó (nota el tiempo pasado ) solo porque las versiones antiguas de python no los soportaban muy bien, pero este problema se resolvió hace años. La versión actual de PEP 8 los sugiere como una alternativa aceptable a las importaciones absolutas. En realidad me gustan dentro de los paquetes.

  • Use importaciones absolutas usando el nombre completo del paquete ( CreateUser.py en Scripts/ ):

      from user_management.Modules import LDAPManager 

Para que el segundo funcione, el paquete user_management debe instalarse dentro de PYTHONPATH . Durante el desarrollo, puede configurar el IDE para que esto suceda, sin tener que agregar manualmente las llamadas a sys.path.append cualquier lugar.

También me parece extraño que Scripts/ sea ​​un subpaquete. Debido a que en una instalación real, el módulo de user_management se instalaría en los site-packages encuentran en el directorio lib/ (el que se use para instalar bibliotecas en su sistema operativo), mientras que los scripts deberían instalarse en un directorio bin/ (el que contenga ejecutables para su sistema operativo).

De hecho, creo que Script/ incluso no debería estar bajo user_management . Debe estar en el mismo nivel de user_management de user_management . De esta manera, no tiene que usar -m , sino que simplemente debe asegurarse de que se puede encontrar el paquete (esto también es una cuestión de configurar el IDE, instalar el paquete correctamente o usar PYTHONPATH=. python Scripts/CreateUser.py para iniciar los scripts con la ruta correcta).


En resumen, la jerarquía que usaría es:

 user_management (package) | |------- __init__.py | |------- Modules/ | | | |----- __init__.py | |----- LDAPManager.py | |----- PasswordManager.py | Scripts/ (*not* a package) | |----- CreateUser.py |----- FindUser.py 

Luego, el código de CreateUser.py y FindUser.py debe usar importaciones absolutas para importar los módulos:

 from user_management.Modules import LDAPManager 

Durante la instalación, se asegura de que user_management termine en algún lugar de PYTHONPATH y de los scripts dentro del directorio de los ejecutables para que puedan encontrar los módulos. Durante el desarrollo, depende de la configuración de IDE, o CreateUser.py agregando el directorio Scripts/ parent al PYTHONPATH (me refiero al directorio que contiene user_management y Scripts ):

 PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py 

O puede modificar PYTHONPATH globalmente para no tener que especificar esto cada vez. En los sistemas operativos UNIX (Linux, Mac OS X, etc.) puede modificar uno de los scripts de shell para definir la variable externa PYTHONPATH . En Windows, debe cambiar la configuración de las variables de entorno.


Addendum Creo que, si está usando python2, es mejor asegurarse de evitar las importaciones relativas implícitas poniendo:

 from __future__ import absolute_import 

en la parte superior de tus módulos. De esta manera, import X siempre significa importar el módulo de nivel superior X y nunca intentaremos importar el archivo X.py que está en el mismo directorio (si ese directorio no está en PYTHONPATH ). De esta manera, la única forma de realizar una importación relativa es usar la syntax explícita ( from . import X ), que es mejor ( explícito es mejor que implícito ).

Esto asegurará que nunca utilice las importaciones relativas implícitas “falsas”, ya que esto generaría un ImportError que indica claramente que algo está mal. De lo contrario, podrías usar un módulo que no es lo que crees que es.

Desde Python 2.5 en adelante, puedes usar

 from ..Modules import LDAPManager 

El período inicial te lleva a “subir” un nivel en tu jerarquía.

Consulte los documentos de Python en las referencias dentro del paquete para las importaciones.

En la “raíz” __init__.py también puede hacer una

 import sys sys.path.insert(1, '.') 

Lo que debería hacer que ambos módulos sean importables.