ivica
ivica

Reputation: 1428

Trouble with Python3 imports

This question was asked a lots of times but none of the solutions seem to help in my case.

I have a directory structure like this

my_project/
    main.py
    bootstrap/
        __init__.py
        boot.py
    consumer/
        __init__.py
        main.py

Being at the toplevel directory (myproject) and executing python3 consumer/main.py throws an error:

Traceback (most recent call last):
  File "consumer/main.py", line 7, in <module>
    from bootstrap.boot import MyClass
ImportError: No module named 'bootstrap'

Weird thing is that importing that module using the interpreter works as expected. Running the code from PyCharm also works fine.

I've tried importing with "full path" e.g. from my_project.bootstrap.boot import MyClass which fails with the same ImportError. I have also tried using relative imports e.g. from .bootstrap.boot import MyClass which also failed with SystemError: Parent module '' not loaded, cannot perform relative import

One hack that fixes this is when I add export PYTHONPATH="/root/my_project" at the bottom of virtualenv activate script

Upvotes: 0

Views: 203

Answers (1)

Burhan Khalid
Burhan Khalid

Reputation: 174614

You are getting this error because module search path only includes the current directory, and not its parents; and since your other module is not in the PYTHONPATH it isn't available to import.

You can find this out yourself by printing sys.path in your script.

I created a directory t with the following:

$ tree
.
├── a.py
├── bar
│   ├── __init__.py
│   └── world.py
└── foo
    ├── hello.py
    └── __init__.py

2 directories, 5 files

Here is the source of hello.py:

$ cat foo/hello.py
import sys
print("I am in {}".format(__file__))
for path in sys.path:
    print(path)

from bar.world import var
print(var)

Now watch what happens, when I execute foo/hello.py and try to import something from bar/world.py;

$ python foo/hello.py
I am in foo/hello.py
/home/burhan/t/foo
/usr/lib/python2.7
/usr/lib/python2.7/plat-x86_64-linux-gnu
/usr/lib/python2.7/lib-tk
/usr/lib/python2.7/lib-old
/usr/lib/python2.7/lib-dynload
/home/burhan/.local/lib/python2.7/site-packages
/usr/local/lib/python2.7/dist-packages
/usr/lib/python2.7/dist-packages
Traceback (most recent call last):
  File "foo/hello.py", line 6, in <module>
    from bar.world import var
ImportError: No module named bar.world

You can tell from the paths printed that only the system-wide Python library paths, and the current directory of the script is listed. This is why it cannot find bar.world.

To fix this issue, you can adjust the PYTHONPATH or use relative imports; for example:

$ PYTHONPATH=../t python foo/hello.py
I am in foo/hello.py
/home/burhan/t/foo
/home/burhan/t
/usr/lib/python2.7
/usr/lib/python2.7/plat-x86_64-linux-gnu
/usr/lib/python2.7/lib-tk
/usr/lib/python2.7/lib-old
/usr/lib/python2.7/lib-dynload
/home/burhan/.local/lib/python2.7/site-packages
/usr/local/lib/python2.7/dist-packages
/usr/lib/python2.7/dist-packages
42

You notice here I am manually overriding the PYTHONTPATH and adding the common parent of the scripts (42 is coming from bar/world).

To fix this using relative imports, you first have a to create a package in the top most directory, otherwise you'll get the famous Attempted relative import in non-package error; for more on this and details on how Python 3 importing works, have a look at: Relative imports in Python 3

Upvotes: 4

Related Questions