Relative import in python can sometimes be mysterious and obscure. Therefore, from time to time, you may encounter the unclear exception ValueError: Attempted relative import beyond top-level package. Let’s see what this exception means, why it is raised. Finally, we see how to fix the ValueError: attempted relative import beyond top-level package exception.
Why “ValueError: Attempted relative import beyond top-level package” is Raised?
If you try to do relative import in a module that does not belong to a package, you will get the ValueError: Attempted relative import beyond top-level package exception.
Let’s see why. In PEP 328 (Imports: Multi-Line and Absolute/Relative), we can find how the python interpreter should resolve the relative modules.
Relative imports use a module’s
PEP 328__name__
attribute to determine that module’s position in the package hierarchy.
This description implies that relative import is relative to the current package – the package the current module belongs to. Therefore, the module where we do the relative import must belong to a package, or otherwise, the python interrupter will complain that you are doing something wrong.
As we know, not all modules belong to a package. For example, the __main__
(The script you invoked the python interrupter with) do not belong to a package.
The following description from PEP 328 describes how python interrupter decides whether the module belong to a package.
If the module’s name does not contain any package information (e.g., it is set to
PEP 328__main__
), then relative imports are resolved as if the module were a top-level module, regardless of where the module is actually located on the file system.
When the Python interpreter tries to locate the imported module, it finds what the current package is. It does this by examining the value of the __name__
variable (the module’s name) and extract from it the package information.
Suppose __name__
variable does not have any package information. In that particular case, the python interpreter treats the module as a top-level module. Since the top-level modules do not belong to any package, the interpreter can not resolve the location of the imported module. Therefore, the python interpreter raises the ValueError: Attempted relative import beyond top-level package exception.
Avoid ValueError: attempted relative import beyond top-level package
As we see, when you try to do relative import in a module that does not belong to a package, you will get the ValueError: Attempted relative import beyond top-level package exception. In other words, It is important to know that module where you do relative import, belongs to a package, or otherwise you get this annoying exception.
2 Ways to Check Whether the module belongs to a package
So how can you know whether the current module belongs to a package and you can safely do relative imports inside this module?
One way to check if a module belongs to a package is to rely on the value of the __name__
variable. The module belongs to a package only if __name__
variable contains a dot.
The second way is to rely on the he value ofthe __package__
variable. The module belongs to a package only if__package__
variable do not equal to None
.
Sample for ValueError: attempted relative import beyond top-level package
As said, the __main__
module (The script you invoked the python interrupter with) does not belong to a package. The following sample demonstrates this:
Suppose you have a project with the following simple directory structure:
Project directory structure
root
├── data.py
└── package
├── __init__.py
└── program.py
You are trying access variables defined in the data.py
in your program.py
. It seems a straightforward task and you choose to use relative import:
root/data.py
size = 10
root/package/program.py
from .. import data
print("data.size => {0}".format(data.size))
However, when invoking program.py
script, An exception is raised :
“ValueError: attempted relative import beyond top-level package” Is Raised
Y:/root>python package/program.py
Traceback (most recent call last):
File "package/program.py", line 1, in <module>
from .. import data
ValueError: attempted relative import beyond top-level package
The __name__ and __package__ variables
Let’s add some log messages that display the values of the __name__
and __package__
variables at the top of the above modules:
root/data.py with logs
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
size = 10
root/package/program.py with logs
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
from .. import data
print("data.size => {0}".format(data.size))
Invoking program.py
Y:/root>python package/program.py
__file__=package/program.py | __name__=__main__ | __package__=None
Traceback (most recent call last):
File "package/program.py", line 3, in <module>
from .. import data
ValueError: attempted relative import beyond top-level package
As we can see in the above output when invoking program.py
script, the value of __name__
variable is __main__
and the value of __package__
is None
.
Since, the value of the variable __name__
does not contains any dot and the value __package__
is None
, we can say that the module does not belong to any package and python interrupter will complain the annoying exception.
How to fix “ValueError: Attempted relative import beyond top-level package”
Solution 1 : Change Directory Structure
Let’s change directory structure and create a new script
- First, create a new directory named
new_root
- Move the
root
directory tonew_root
- Create a new empty
__init__.py
inside theroot
directory. This will signal to the python interrupter that this directory is a package. - Create
main.py
innew_root
directory
The project directory – solution 1
new_root
├── program.py
└── root
├── __init__.py
├── data.py
└── package
├── __init__.py
└── program.py
Updating new_root/program.py
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import root.package.program
When we invoke the new script, we get the following output:
Invoking program.py
Y:/new_root>python program.py
__file__=program.py | __name__=__main__ | __package__=None
__file__=Y:/new_root/root/package/program.py | __name__=root.package.program | __package__=root.package
__file__=Y:/new_root/root/data.py | __name__=root.data | __package__=root
data.size => 10
It works. Let’s see why?
Now, the module root.package.program
belongs to root.package
.
We can see it in the second line (The print from the top of new_root/root/package/program.py).
__file__=Y:/new_root/root/package/program.py | __name__=root.package.program | __package__=root.package
Now, when this module belong to a package, the python interpreter has all the information has all the information to resolve the relative import in root/package/program.py
successfully.
Solution 2 : Use the -m option
In this solution, we needed to create a new script. In the following solution, we use -m option without creating a new script.
Let’s change directory structure and use -m option
- First, create a new directory named
new_root
- Move the
root
directory tonew_root
- Create a new empty
__init__.py
inside theroot
directory. This will signal to the python interrupter that this directory is a package.
The project directory – solution 2
new_root
└── root
├── __init__.py
├── data.py
└── package
├── __init__.py
└── program.py
Now, we invoke python with -m option and provide the module root.package.program
.
The python -m option allows modules to be located using the Python module namespace for execution as scripts. As the following output demonstrate, It will also set the package information:
Invoking program.py with -m option
Y:/new_root>python -m root.package.program
__file__=Y:/new_root/root/package/program.py | __name__=__main__ | __package__=root.package
__file__=Y:/new_root/root/data.py | __name__=root.data | __package__=root
data.size => 10
It works. Let’s see why?
Now, the module root.package.program
belongs to root.package
.
We can see it in the first line (The print from the top of new_root/root/package/program.py).
__file__=Y:/new_root/root/package/program.py | __name__=__main__ | __package__=root.package
Now, when this module belong to a package, the python interpreter has all the information has all the information to resolve the relative import in root/package/program.py
successfully.
Note: Unlike PEP 328, the python interpreter do not rely on the value of __name__ to find the package information and uses the value of __package__.
Summary
We see how the python interrupter resolve relative imports. Then, we see why the ValueError: Attempted relative import beyond top-level package exception is raised. Finally we see how to get rid of ValueError: Attempted relative import beyond top-level package.