2 Best Solutions For “ValueError: Attempted relative import beyond top-level package”

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 __name__ attribute to determine that module’s position in the package hierarchy.

PEP 328

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 __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.

PEP 328

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 to new_root
  • Create a new empty __init__.py inside the root directory. This will signal to the python interrupter that this directory is a package.
  • Create main.py in new_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 to new_root
  • Create a new empty __init__.py inside the root 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.

Leave a Reply

Your email address will not be published. Required fields are marked *