ImportError: Attempted Relative Import With No Known Parent Package (Python)

This is about the error ImportError: attempted relative import with not known parent package in Python.

You’ll learn:

  • The meaning of the error ImportError: attempted relative import with not known parent package
  • How to solve the error ImportError: attempted relative import with not known parent package
  • Lots more

So if you want to understand this error in Python and how to solve it, then you’re in the right place.

Let’s get started!

Table of Contents

How to Solve ImportError: Attempted Relative Import With No Known Parent Package (ModuleNotFoundError: No module named ‘name’)

For example, you get the error when running a file inside a package as a script.

Programmer stressed while analyzing codes in his computer.

To get rid of the error ImportError: attempted relative import with no known parent package you have two ways to test package functions:

  • Run a script with the -m switch.
  • Use global import inside the package files that you plan to run as scripts.

To get rid of the former version of this error ModuleNotFoundError: No module named ‘name’, the path to the package folder must be in the PATH system variable. You can do this in different ways, for example:

  • Move the package folder to a directory that is already in PATH.
  • Add the folder where the package is located to PATH on your own through the console or system settings.
  • Install the package to the system using the setuptools module.
  • Add the address of the package folder to PATH using the sys and pathlib modules in those package files that you plan to run as a separate script.

Let’s dive right in:

Understand the Error ImportError: Attempted Relative Import With No Known Parent Package

One of the key advantages of the Python language is its infrastructure. You can find a ready-made module for almost any of your tasks. 

The meme from xkcd below is a good illustration of this statement:

Python comic.

In this comic, one man says that flying is a straightforward task; you just need to type import antigravity. WARNING! Don’t try to import antigravity at home without securing fragile items.

It’s effortless to create your modules and packages for Python. However, this also has a downside. As packages evolved, some of the internal names in packages began to duplicate global ones. 

For example, there is a package called graphics, but this is a fairly common word and it is inevitably used in other packages related to graphics. 

Let’s say you created a supergraphics package with the following structure:

supergraphics
 | - graphics
 |	 | --rectangles.py
 |	 | --circles.py 	
 | - utils
 |	 | --resize
 |	 |	| --shrink.py
 |	 |	| --stretch.py
 |	 | --screen.py 
 |	 | --printer.py
 | --unittests.py

How can you determine when to call import graphics inside the unittests.py file, whether you mean a package from PyPI (Python Package Index) (let’s assume that you have installed it) or a local module inside your library? 

One way is to always use an absolute import:

import supergraphics.graphics 

But in Python you can use the so-called relative import. Its capability and usage are specified in PEP328

Relative imports are implemented as follows: You can use one dot . to import a package from the same level where you are. If you import graphics from unittest.py, you would write this:

import .graphics

If you need to import something from another folder, you would put a point to rise to a higher level.

For example, to import graphics from the shrink.py file, you would write this:

import ...graphics Imagine

You can consider that the invisible word parent precedes each dot:

import __parent__.__parent__.__parent__.graphics

This makes the principle a little clearer, but writing this, of course, is not very convenient. In the end, you can use global imports.

Thanks to relative imports, you can add new folders to the package structure. This will not always require you to redo all imports.

Close-up of hand typing on a keyboard, concept of an office job.

For example, if you want to move all your current folders and files to a higher-level folder, this will not cause changes in the child files.

Relative imports are allowed only within a package. This is consistent with common sense and safety. Only the folder structure within the package can be guaranteed. 

A package is an independent structural unit, and its components should not interact with relative paths with neighbouring packages.

It is very easy to create a Python package; you just need to create an __init__.py file in the root of the folder to import this folder as a package.

Let’s create a little module with the following folder structure and file contents:

hi
 | - say_hello.py
 |	def hello(name):  
 |   	   return f"Hello, {name}!" 
 | - introduce.py
 |	 from .say_hello import hello  
 |	    def hello_to():  
 |	    name = input('What is your name?\n')  
 |	    return hello(name) + '\ nNice to meet you!'  
 | --__ init__.py

You can now use your package from the Python terminal:

>>> from hi.introduce import hello_to
>>> print(hello_to())
What is your name?
Robot
Hello, Robot!
Nice to meet you!

Now let’s add testing to the introduce.py file. It will look like this:

from .say_hello import hello 	
def hello_to():	
   name = input('What is your name?\n')  
   return hello(name) + '\ nNice to meet you!' 	
if __name__ == '__main__':
   assert 'Alex' in hello('Alex')  
   print('Test passed!')

Now you can import the hello_to() function from it.

If you run the introduce.py file as a script, it will check on whether the value returned from the hello() function contains the name that you submitted to as an argument.

If you try to run this file as a script, you will get an ImportError: attempted relative import with no known parent package:

(venv)> python hi\introduce.py
Traceback(most recent call last):
  File "hi\introduce.py", line 1, in <module>
    from .say_hello import hello
ImportError: attempted relative import with no known parent package

Another solution would be to not use relative imports.

Let’s change the introduce.py file as follows:

(venv)> python -m hi.introduce
Test passed!

Now you can run this file as a script without the -m key:

from hi.say_hello import hello 	
def hello_to():	
   name = input('What is your name?\n')  
   return hello(name) + '\ nNice to meet you!' 	
if __name__ == '__main__':
   assert 'Alex' in hello('Alex')  
   print('Test passed!')  

A separate problem may be that Python does not know where the package is located.

Let’s move the package down one level inside the Project folder and try again to run introduce.py:

Project
   | --hi
 | - say_hello.py
 |	def hello(name):  
 |   	   return f"Hello, {name}!" 
 | - introduce.py
 |	 from .say_hello import hello  
 |	    def hello_to():  
 |	    name = input('What is your name?\n')  
 |	    return hello(name) + '\ nNice to meet you!'  
 | --__ init__.py

		(venv)\Project> python hi\introduce.py
Traceback(most recent call last):
  File "hi\introduce.py", line 1, in <module>
    from hi.say_hello import hello
ModuleNotFoundError: No module named 'hi'

To get rid of this problem, you need to add the package’s folder path to PATH or place the package in a folder that is already in PATH. For example, <Python directory>/lib/site-packages

Before adding a module’s address to PATH or copying it to a folder that already exists in PATH, you must ensure that your package name is unique.

Otherwise, one of the packages will not work—either yours or the one already installed with the same name.

Programmer looking stressed while looking at codes on the monitor.

For your package to be globally available, Python has a special mechanism.
You can install your package using the utilities from the setuptools module.

Let’s create a setup.py file with the following content in the root folder of the package:

from setuptools import setup, find_packages  
setup(name = 'hi', packages = find_packages())

Next, start installing the package with the following command. By the way, you should always use a virtual environment; do not install any packages in the global interpreter.

For Python 3, there is a built-in venv utility. This is very useful if, for example, you have to use different versions of libraries in your different projects:

(venv)> python hi\setup.py install

After launching, you will get a long installation log. If there were no errors, you could use the hi module globally. 

If for some reason you do not want to install your module into the system, you can add the current directory to the path.

Make sure you do this before all imports from the module. Add a block that will modify PATH to the introduce.py file:

import sys  
from pathlib import Path  
file = Path(__ file __). resolve()  
package_root_directory = file.parents [1]  
sys.path.append(str(package_root_directory))  
  
from hi.say_hello import hello  
def hello_to():  
    name = input('What is your name?\n')  
    print(hello(name) + '\nNice to meet you!')  
if __name__ == '__main__':  
    assert 'Alex' in hello('Alex')  
    print('Test passed!')

Now you can use global import in your introduce.py file even if the hi module is not installed in the system via setuptools. 

Note that file.parents[1] is being used here because you need to move up one level for the hi package to be available.

In other words, you must go to the folder where the folder with the package is located. In the current case, this is Project:

Project
   | --hi
 | - say_hello.py
 | - introduce.py
 | --__ init__.py

file.parents[0] =.\Project\hi
file.parents[1] =.\Project

If the tree structure were deeper, then the parents with higher indices would have to be used.

Here’s more Python support: