ImportError: attempted relative import with no known parent package

Software Development
by: Ryan Ernst

You hate to see it. You just started a new project. Things are going great. You decide it is time to get serious — so you start a second file and begin moving the extraneous functions into that file. You finish the cleanup effort. Feeling great. Let’s run it one more time before I sign off for the day… wait… this was just working!

ImportError: attempted relative import with no known parent package
python import errors meme
a glorious python import errors meme

ImportError — what does that even mean!? I just wanted to import another file. What even is a parent package!?

 

If you are going through this now or have in the past, this post is for you.

The Fix

In a hurry, and just want a quick fix? This section is for you. If you want to understand the why behind these fixes, check out Packages Explained a bit further down. You can also jump to the Common Project Structures section if you just want some tips on how to structure your files to avoid issues like this one.

Many resources on the web will have you fiddling with your sys.path but in most cases, this is a code smell that indicates you are not properly using modules and/or packages.

This issue almost always occurs because you are using a flat file structure for your project, and you try to split the functionality into two files. For example:

.
└── src/
 ├── main.py
 └── util.py
# main.py
from util import doSomethingCool

print("About to do something cool!")
doSomethingCool()
# util.py
def doSomethingCool():
    print("Doing something cool")

Running main.py with this setup will result in:

ImportError: attempted relative import with no known parent package

Option 1: Get Rid of From

Don’t use from and just use import, then make sure you use the module reference when using your imported functions. In our example, this is  util . So your main.py would now look like this:

# main.py
import util

print("About to do something cool!")
util.doSomethingCool()

Problem solved. Get on with your life. If you want to learn about why this fixes the issue, check out Packages Explained.

Option 2: Just Make an Actual Package

If you are making a separate file to do helpful things, why not make it an actual package? It is actually much easier than you would think.

Change up your file structure so that the functionality you want to split out is in a separate folder and includes an empty __init__.py file. Something like this:

.
└── src/
    ├── main.py
    └── utils/
        ├── __init__.py
        └── util.py

With this structure, you can actually import in many different ways. Any of the following will work.

# Main file for doing cool things
import utils.util

print("About to do something cool!")
utils.util.doSomethingCool()
# Main file for doing cool things
from utils import util

print("About to do something cool!")
util.doSomethingCool()
# Main file for doing cool things
from utils.util import doSomethingCool

print("About to do something cool!")
doSomethingCool()

You can also get extra fancy and use the as keyword, which will let you define a new alias for your import.

# Main file for doing cool things
import utils.util as foo

print("About to do something cool!")
foo.doSomethingCool()

Packages Explained

If you are not in a rush and want to learn about how modules and packages work, then keep on reading!

What’s a Module?

Python is an interpreted scripting language. The Python interpreter is the core engine of python that runs your scripts. If you have ever just run python from a terminal without a file, you are just running the Python interpreter. It will read in the commands you type and execute them as you enter them. This isn’t a very useful paradigm though; no one wants to sit at a terminal all day and type in their code. This is where the script comes into play. You have likely made a .py file before; this is what we call a script.

What do you do when you make a function that you want to use in multiple scripts? This is where we use a module. A module is simply a .py script that contains one or more definitions. Definitions are functions that we define in a module that can be used in other scripts via the import keyword. The file name becomes the module name. So, for example, if we have foo.py we would import that using import foo. Basic modules tend to work best when they are in the same directory as your main script.

When you use the import keyword, for example, import foo, python will load that script and bring all of the definitions into your current script. The functions are not imported directly into the script, but they are accessible via the module name.

# foo.py

def bar():
    print("E-Flat walks into a bar. The bartender says, 'Sorry, we don't serve minors!'")
# TellBarJoke.py
import foo

foo.bar()

Modules let you logically separate your code into functional pieces. You can also include code that is not a function definition when importing a module. It is only executed once when the module is first imported. See the example below that initializes barJoke when imported.

# foo.py

barJoke = "E-Flat walks into a bar. The bartender says, 'Sorry, we don't serve minors!'"

def bar():
    print(barJoke)

In most cases though, this is a bad practice and should be avoided as it can create unintended side-effects.

Module Debug Trick

Sometimes when you are writing a module intended to be imported by another script, you want to test what you have written. This is where you can use the special __name__ variable! When a module is imported by another script __name__ is set to the name of the module, but when you run a module directly __name__ is set to __main__. We can use this knowledge by adding code to our module that is only run if __name__ == "__main__":.

# foo.py

def bar():
    print("E-Flat walks into a bar. The bartender says, 'Sorry, we don't serve minors!'")

if __name__ == "__main__":
    # bar will be invoked if this module is being run directly, but not via import!
    bar()

What’s a Package?

A package is a mechanism to bundle one or more modules in a way that makes them easier to organize into logical groups. Packages also allow us to organize our modules into different folders. If you find yourself with more than a handful of modules, it is probably time to use a package. This article will not cover distributing packages. If you want to learn more about package distribution check out the doc page for Python Wheels.

In its simplest form, any directory that has an __init__.py file and one or more modules is a package. The __init__.py file serves as both a marker for python that this is a package and also a place to execute any initialization code required to use the package. Typically __init__.py is just an empty file unless you need to do some form of advanced package initialization.

.
└── src/
    ├── main.py
    └── utils/
        ├── __init__.py
        └── util.py

In the example above, utils is a package. The __init__.py file is just an empty file. Python just looks for the __init__.py file as a way to tell that a directory is being treated as a package.

A package can have multiple modules in it:

.
└── src/
    ├── main.py
    └── utils/
        ├── __init__.py
        ├── util.py
        └── otherUtil.py

You can also have multiple packages in the same parent folder:

.
└── src/
    ├── main.py
    ├── utils/
    │   ├── __init__.py
    │   ├── util.py
    │   └── otherUtil.py
    └── calculations/
        ├── __init__.py
        └── financials.py

You can even have nested packages:

.
└── src/
    ├── main.py
    ├── utils/
    │   ├── __init__.py
    │   ├── util.py
    │   └── shared/
    │       ├── __init__.py
    │       └── helpers.py
    └── calculations/
        ├── __init__.py
        └── financials.py

Packages can be imported the same way as modules. Intrapackage relative imports can get a bit tricky. Below is an example of importing a package from within a package using relative imports.

.
└── src/
    ├── main.py
    └── utils/
        ├── __init__.py
        ├── util.py
        └── shared/
            ├── __init__.py
            └── helpers.py
# Main.py for doing cool things
import utils.util

print("About to do something cool!")
utils.util.doSomethingCool()
# util.py
from . shared import helpers

def doSomethingCool():
    print(helpers.doHelp("Doing something cool"))
# helpers.py

def doHelp(x):
    return "help {}".format(x)

Common Project Structures

This section is intended to be a cookbook for how you might want to structure your project as it evolves.

Humble Beginnings

Maybe you just want to make a quick Proof of Concept, and don’t see this turning into a large project. Hint: most large projects come from humble beginnings!

.
└── src/
    ├── main.py
    └── util.py

Cleaning Up a Bit

You are ready to pull out some functionality that is either used in several places or is not core to what you are trying to accomplish in main.

.
└── src/
    ├── main.py
    └── util.py

Ok, Getting Serious Now

Maybe your small project is starting to get serious, or you have created functionality that you would like to use in other projects as a package. Notice that your main entry point, main.py, is always outside of the package. This will save you a lot of headaches.

.
└── src/
    ├── main.py
    └── MyUtils/
        ├── __init__.py 
        ├── utils.py
        └── helpers.py

This Is Definitely Going to Be Used Again

Ok, now your small project has grown into a beast and has created several useful packages that can stand alone. It might be structured like this.

.
└── src/
    ├── main.py
    ├── PackageA/
    │   ├── __init__.py
    │   ├── logic.py
    │   ├── SubPackageA1/
    │   │   ├── __init__.py
    │   │   └── util.py
    │   └── SubPackageA2/
    │       ├── __init__.py
    │       └── otherUtil.py
    └── PackageB/
        ├── __init__.py
        └── helpers.py

Wrapping Up

I hope you have managed to fix your import problems and maybe learned something along the way. If you have any questions about this post, or maybe there is a Python-related project you could use some help on? Don’t hesitate to send us an email at tech@iq-inc.com!