Reputation: 23
I'm having trouble with a python package that uses separate modules to structure code. The package itself is working, however when imported from another environment fails with a ModuleNotFound error. Here's the structure:
Project-root
|
|--src/
| __init__.py
| module_a.py
| module_b.py
| module_c.py
| module_d.py
|--tests
etc.
In module_a.py I have:
from module_a import function_a1,...
from module_b import function_b1,...
from module_c import function_c1,...
In module_c I import module_d like:
from module_d import function_d1,...
As mentioned above, executing module_a or module_c directly from the CLI work as expected, the unit tests I've created in the test directory also work (with the help of sys.path.insert), however if I create a new environment and import the package I get the following error:
>>> import module_a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/<abs_path>/.venv/lib/python3.9/site-packages/module_a.py", line 22, in <module>
from module_c import function_c1, function_c2
File /<abs_path>/.venv/lib/python3.9/site-packages/module_c.py", line 9, in <module>
import module_d
ModuleNotFoundError: No module named 'module_d'
>>>
I've exhausted all ideas how to overcome this, besides combining the code of modules c and d in one file, which I'd hate to do, or rethink the flow so that all modules are imported from module_a.
Any suggestions how to approach this would be greatly appreciated.
Update: It turned out to be a typing mistake in the name of module_d in setup.py. For whatever reason python setup.py install
was failing silently or I wasn't reading the logs carefully.
Upvotes: 2
Views: 2950
Reputation: 4368
The problem comes down to understanding the basics of the import system and the PYTHONPATH.
When you try to import a module (import module_a
), Python will search in order in every directory listed in sys.path
. If in one of them it finds a directory that matches the name (module_a
)1, then it runs the __init__.py
file is such exist. If it finds a file, it gets directly converted to a module.
When you get an ImportError
, it means that there is no directory in sys.path
containing a file/directory with the name asked.
You said for your tests you did something like sys.path.insert(0, "some/path/")
, but it is not a solution, just a broken fix.
What you should do is set your PYTHONPATH
environment variable to contain the directory where your modules are located, Project-root/src
in your case. That way, no need to ever use sys.path.insert
, or fiddle with relative/absolute paths in import statements.
When you create your new environment, just set your environment variable PYTHONPATH
to include Project-root/src
and you are done. This is how installing regular Python modules (libraries) work : they are all put into a directory in site-packages
.
1: this changed since old Python versions, it used to be required for the directory to contain an __init__.py
file, see PEP-420
Upvotes: 2