Misha
Misha

Reputation: 39

Python package import error Import error: Relative import attempt without known parent package

The project has the same structure as in the picture: I'm trying to import from "mod.py " in "index.py "

from .. import mod

However, it gives the error: "ImportError: attempted relative import with no known parent package" If you use this option:

from pack1 import mod

Then error: "ModuleNotFoundError error: there is no module named 'pack1'"

enter image description here

PROJECT/
  pack1/
    __init__.py
    mod.py
  pack2/
    __init__.py
    index.py

What is the problem?

Upvotes: 2

Views: 461

Answers (1)

James
James

Reputation: 36608

This is a recurring question on StackOverflow. And much of the confusion (in my opinion) comes from how Python interprets the files and folders it sees is based on where Python is run from. First, some terminology:

  • module: a file containing Python code.
  • package: a folder containing files with Python code and other folders.

When you start Python in a directory (folder), it doesn't "know" what the namespace of that directory should be. I.e., if you are working in Z:\path\to_my\project\ when you start Python:

  1. it does NOT consider project to be a package.
  2. any .py files you want to import from will be in their own namespace as modules.
  3. any folders you want to import from will also be in their own namespace as packages.
  • What about __init__.py? Since version 3.3, Python has implicit namespace packages, which allows importing without needing to create an empty __init__.py file.

Consider #2: if you have two files: first.py and second.py:

path/
  to_my/
    project/
      >>Python is running here<<
      first.py
      second.py

with these contents:

# first.py
first_var = 'hello'
# second.py
from .first import first_var

second_var = first_var + ' world'

if you try to import like this:

>>> import second

Python basically does the following:

  • "ok, I see second.py"
  • "Reading that in as a module, chief!"
  • "Ok, it wants to import .first
  • "The . means get the package (folder) that contains first.py"
  • "Wait, I don't have a parent package for first.py!"
  • "Better raise an error."

The same rules apply for #3 as well. If we add a few packages to the project like this:

path/
  to_my/
    project/
      >>Python is running here<<
      first.py
      second.py
      pack1/
        mod.py
        other_mod.py
      pack2/
        index.py

with the following contents:

# pack1/mod.py
mod_var = 1234
# pack1/other_mod.py
from .mod import mod_var

other_var = mod_var * 10
# pack2/index.py

from ..pack1 import mod

and when you try to import like this:

>>> from pack2 import index.py

The import in pack2/index.py is going to fail for the same reason second.py, Python will work its way up the import chain of dots like this:

  • "Reading in in index.py as a module."
  • "Looks like it wants to import mod from ..pack1.
  • "Ok, . is the pack2 parent package namespace of index.py, found that."
  • "So, .. is the parent package of pack2."
  • "But, I don't have a parent package for pack2!"
  • "Better raise an error."

How do we make it work? Two thing.

First, move where Python is running up one level so that all of the .py files and subfolders are considered to be part of the same package namespace, which allows the file to reference each other using relative references.

path/
  to_my/
    >>Python is running here now<<
    project/
      first.py
      second.py
      pack1/
        mod.py
        other_mod.py
      pack2/
        index.py

So now Python sees project as a package namespace, and all of the files within can use relative references up to that level.

This changes how you import when you are in the Python interpreter:

>>> from project.pack2 import index.py

Second, you make explicit references instead of relative references. That can make the import statements really long, but if you have several top-level modules that need to pull from one another, this is how you can do it. This is useful when you are defining your functions in one file and writing your script in another.

# first.py
first_var = 'hello'
# second.py
from first import first_var  # we dropped the dot

second_var = first_var + ' world'

I hope this helps clear up some of the confusion about relative imports.

Upvotes: 2

Related Questions