Are you struggling with the ‘ImportError: Attempted Relative Import With No Known Parent Package’ in Python? Discover the essential guide to understanding and fixing this common Python error. In this article, you’ll learn:
- The detailed explanation of the ‘ImportError: Attempted Relative Import With No Known Parent Package’ error.
- Step-by-step solutions to resolve this import issue in your Python code.
- Insights into Python’s relative imports and best practices for package management.
This guide is perfect for Python developers seeking to enhance their coding skills and troubleshoot import errors effectively. Dive into our comprehensive tutorial and master the art of resolving Python import errors today!
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.
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:
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.
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.
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:
- 9 Examples of Unexpected Character After Line Continuation Character
- 3 Ways to Solve Series Objects Are Mutable and Cannot be Hashed
- How to Solve ‘Tuple’ Object Does Not Support Item Assignment
- How to Solve SyntaxError: Invalid Character in Identifier
- IndentationError: Unexpected Unindent in Python (and 3 More)