182

I can't for the life of me get python's relative imports to work. I have created a simple example of where it does not function:

The directory structure is:

__init__.py
start.py
parent.py
sub/
    __init__.py
    relative.py

/start.py contains just: import sub.relative

/sub/relative.py contains just from .. import parent

All other files are blank.

When executing the following on the command line:

$ cd /
$ python start.py

I get:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/home/cvondrick/sandbox/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: Attempted relative import beyond toplevel package

I am using Python 2.6. Why is this the case? How do I make this sandbox example work?

KetZoomer
  • 2,701
  • 3
  • 15
  • 43
carl
  • 49,756
  • 17
  • 74
  • 82
  • Personally I find the top answer in the question https://stackoverflow.com/questions/30669474/beyond-top-level-package-error-in-relative-import to be very helpful in understanding this import error – torez233 Jun 14 '21 at 03:36

3 Answers3

146

You are importing from package "sub". start.py is not itself in a package even if there is a __init__.py present.

You would need to start your program from one directory over parent.py:

./start.py

./pkg/__init__.py
./pkg/parent.py
./pkg/sub/__init__.py
./pkg/sub/relative.py

With start.py:

import pkg.sub.relative

Now pkg is the top level package and your relative import should work.


If you want to stick with your current layout you can just use import parent. Because you use start.py to launch your interpreter, the directory where start.py is located is in your python path. parent.py lives there as a separate module.

You can also safely delete the top level __init__.py, if you don't import anything into a script further up the directory tree.

ebo
  • 8,985
  • 3
  • 31
  • 37
  • 3
    You are confusing the terms 'module' and 'package'. 'start.py' represents the module 'start', 'mod' and 'mod.sub' are packages, 'mod' is a toplevel package. – Ferdinand Beyer Dec 16 '09 at 23:45
  • 40
    Thanks, but this honestly seems really silly. For such a beautiful language, I can't believe the designers would create such a restriction. Isn't there any other way? – carl Dec 16 '09 at 23:48
  • 2
    It's not silly at all. Relative imports are a mean to refer to sibling modules within a package. If you want to import a toplevel module, use absolute imports. – Ferdinand Beyer Dec 17 '09 at 15:30
  • 64
    Not silly? So in bash, not been able to address relative upper dir with ".." wouldn't bother you? – Bite code Mar 04 '10 at 09:27
  • @ferdinandbeyer The usage is correct, but a package with the name "mod" is confusing. – Niels Bom Aug 14 '12 at 12:26
  • @NielsBom: Please note that my comment refers to an older version of the answer. – Ferdinand Beyer Aug 14 '12 at 13:35
  • @FerdinandBeyer I'm not criticizing your comment, I'm criticizing ebo decision to name a package "mod". – Niels Bom Aug 14 '12 at 15:20
  • 2
    It seems to me that the python's idea is to use "absolute" imports from directory where you launched your parent script. So you can use absolute path "import parent" to import parent module from sibling. And relative imports some kind of legacy or whatever.. – Odysseus Apr 06 '15 at 15:12
  • @ebo , why "start.py is not itself in a package" ? – Bin Mar 21 '16 at 16:59
  • There is no __init__.py. start.py could be anywhere as long as the package itself is in the search path. – ebo Jun 05 '16 at 12:37
  • 1
    But what actually error `ValueError: Attempted relative import beyond toplevel package` mean? Why `.` (or in other words `/`) is not a package? – mrgloom Sep 12 '19 at 02:24
35

If you are going to call relative.py directly and i.e. if you really want to import from a top level module you have to explicitly add it to the sys.path list.
Here is how it should work:

# Add this line to the beginning of relative.py file
import sys
sys.path.append('..')

# Now you can do imports from one directory top cause it is in the sys.path
import parent

# And even like this:
from parent import Parent

If you think the above can cause some kind of inconsistency you can use this instead:

sys.path.append(sys.path[0] + "/..")

sys.path[0] refers to the path that the entry point was ran from.

4

Checking it out in python3:

python -V
Python 3.6.5

Example1:

.
├── parent.py
├── start.py
└── sub
    └── relative.py

- start.py
import sub.relative

- parent.py
print('Hello from parent.py')

- sub/relative.py
from .. import parent

If we run it like this(just to make sure PYTHONPATH is empty):

PYTHONPATH='' python3 start.py

Output:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/python-import-examples/so-example-v1/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

If we change import in sub/relative.py

- sub/relative.py
import parent

If we run it like this:

PYTHONPATH='' python3 start.py

Output:

Hello from parent.py

Example2:

.
├── parent.py
└── sub
    ├── relative.py
    └── start.py

- parent.py
print('Hello from parent.py')

- sub/relative.py
print('Hello from relative.py')

- sub/start.py
import relative
from .. import parent

Run it like:

PYTHONPATH='' python3 sub/start.py

Output:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 2, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

If we change import in sub/start.py:

- sub/start.py
import relative
import parent

Run it like:

PYTHONPATH='' python3 sub/start.py

Output:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 3, in <module>
    import parent
ModuleNotFoundError: No module named 'parent'

Run it like:

PYTHONPATH='.' python3 sub/start.py

Output:

Hello from relative.py
Hello from parent.py

Also it's better to use import from root folder, i.e.:

- sub/start.py
import sub.relative
import parent

Run it like:

PYTHONPATH='.' python3 sub/start.py

Output:

Hello from relative.py
Hello from parent.py
mrgloom
  • 20,061
  • 36
  • 171
  • 301