Python: import a module from another directory at the same level in the project hierarchy

I have seen all kinds of examples and other similar questions, but I cannot find an example that exactly matches my scenario. I feel like a complete idiot asks about it, because there are so many such questions, but I just can't get it to work “correctly”. Here is my project:

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

If I translate "CreateUser.py" into the main user_management directory, I can easily use: "import Modules.LDAPManager" to import LDAPManager.py - this works. What I cannot do (what I want to do) supports CreateUser.py in the Scripts subfolder and imports LDAPManager.py. I was hoping to accomplish this using "import user_management.Modules.LDAPManager.py" . This does not work. In short, I can get Python files to look deeper in the hierarchy, but I can't get a Python script to link to one directory and down to another.

Please note that I can solve my problem using:

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

I heard that this is bad practice and not recommended.

Files in scripts must be executed directly (is init .py required in scripts?). I read that in this case I have to execute CreateUser.py with the -m flag. I tried some variations of this and just can't get CreateUser.py to recognize LDAPManager.py.

+46
python module path package
Nov 19 '13 at 15:45
source share
3 answers

If I moved CreateUser.py to the main user_management directory, I can easily use: import Modules.LDAPManager to import LDAPManager.py --- this works.

Please don't . Thus, the LDAPManager used by CreateUser will not be the same as the one imported through another import. This can create problems when you have some kind of global condition in the module or during pickling / spilling. Avoid imports that only work because the module is in the same directory.

If you have a package structure, you should:

  • Use relative imports, i.e. if CreateUser.py is in Scripts/ :

      from ..Modules import LDAPManager 

    Note that this one (note the past tense) was discouraged by PEP 8 just because older versions of python did not support them very well, but this problem was solved many years ago. The current version of PEP 8 offers them as an acceptable alternative to absolute imports. I really love them inside the packages.

  • Use absolute imports using the fully qualified package name ( CreateUser.py in Scripts/ ):

      from user_management.Modules import LDAPManager 

In order for the second to work, the user_management package must be installed inside PYTHONPATH . During development, you can configure the IDE to make this happen without having to manually add calls to sys.path.append anywhere.

It also seemed strange to me that Scripts/ is a subfolder. Since in a real installation, the user_management module will be installed under the site-packages found in the lib/ directory (which directory is used to install the libraries in your OS), while the scripts must be installed in the bin/ directory (depending on what it contains executable files for your OS).

In fact, I believe that Script/ should not even be under user_management . It must be at the user_management level. Thus, you do not need to use -m , but you just need to make sure that the package can be found (this is again a matter of configuring the IDE, installing the package correctly, or using PYTHONPATH=. python Scripts/CreateUser.py to run scripts from the correct path).




Thus, I would use the following hierarchy:

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

Then the code CreateUser.py and FindUser.py should use absolute import to import the modules:

 from user_management.Modules import LDAPManager 

During installation, you will make sure that user_management ends somewhere in PYTHONPATH and scripts inside the directory for executable files so that they can find modules. During development, you either rely on the IDE configuration or run CreateUser.py add the parent Scripts/ directory to PYTHONPATH (I mean the directory that contains both user_management and Scripts ):

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

Or you can change PYTHONPATH globally, so you do not need to specify this every time. On UNIX OS (Linux, Mac OS X, etc.) you can change one of the shell scripts to define the external variable PYTHONPATH , on Windows you need to change the settings of environment variables.




Addendum I believe that if you are using python2, it is better to avoid implicit relative imports by setting:

 from __future__ import absolute_import 

at the top of your modules. Thus, import X always means importing a top-level module X and will never try to import the X.py file in the same directory (unless that directory is in PYTHONPATH ). Thus, the only way to do relative imports is to use explicit syntax ( from . import X Import from . import X ), which is better (explicit is better than implicit).

This ensures that you will never be able to use "dummy" implicit relative imports, as they will increase the clarity of ImportError , signaling that something is wrong. Otherwise, you can use a module that is not what you think.

+44
Nov 19 '13 at 16:06
source share

Starting with Python 2.5, you can use

 from ..Modules import LDAPManager 

Leading period takes you to the level of your hierarchy.

See Python docs in the links inside the package for import.

+11
Nov 19 '13 at 16:02
source share

In "root" __init__.py you can also do

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

which should make both modules available.

+1
Nov 19 '13 at 16:07
source share



All Articles