Having Trouble Understanding Relative Imports in Python

110 views Asked by At

I am trying to import methods from script2.py in script1.py

This is my current folder structure

I keep getting "ImportError: attempted relative import with no known parent package", and I've tried many variations of importing without any success.

What exactly do I need in my init.py(s) and what do I need in my script1.py import lines in order to make this work. I've seen other questions related to this, answered on much shallower file structures and the solutions do not seem to work when I try and modify them to fit mine.

root
└── __init__.py
├── Dir1
│   └── Dir2
│       └── Dir3
|           └── Dir4
|               └── __init__.py
|               └── script1.py
└── Dir5
    └── Dir6
        └── __init__.py
        └── script2.py

I've tried changing sys.path to the root directory and Dir6 before importing.

I've also tried from .......Dir5.Dir6 import script2

I've also tried using absolute paths like from root.Dir5.Dir6 import script2

I've also tried adding init.py to every directory

I greatly appreciate any help!

2

There are 2 answers

0
nigh_anxiety On

Relative imports can't be used by a top-level script (the script invoked with python my_script.py), even if that top-level script is nominally in a package, because the __package__ special variable is None for the top-level script.

For more explanation see:

Relative imports for the billionth time

Import from another file inside the same module and running from a main.py outside the module throws an import error [duplicate]

Python sibling relative import error: 'no known parent package'

relative python imports. What is the difference between single dot and no dot

If your project structure has to be this way, and you actually want all of those files in the same package by having that top level __init__.py file in the root directory, then there are some solutions that come to mind:

  1. Run script.py as a module: Starting from one directory level above the location of root, invoke script1.py as a module with
python -m root.Dir1.Dir2.Dir3.Dir4.script1.py
  1. Import script1 from a main/app script outside of root. Create an app.py (or similar) file in the directory one level above root. This script should import root, and perhaps specifically import root.Dir1.Dri2.Dir3.Dir4.script1, then run the primary function within script1.

  2. Reconsider the structure of this project. This may actually be an XY Problem where the issue that relative imports are not necessary here at all, but not enough information is available. If Dir1 and Dir5 aren't actually supposed to be part of the same package, then there should not be an __init__.py at root.
    If Dir1 and Dir5 are meant to be two separate packages, where Dir1 is dependent on Dir5, then perhaps Dir5 should be developed separately, and then installed to local site-packages using pip -e so that the package in Dir1 can import it using absolute ipmorts.

4
SpaceKatt On

The __init__.py file is needed in ALL directories for Python to "to make Python treat directories containing the file as packages." (Link)

Futhermore, you need to be referencing relative paths lower in the dir struct that your __main__ context

Try creating more __init__.py files to achieve the follow dir struct:

src/
├── __init__.py
├── main.py
└── root
    ├── Dir1
    │   ├── Dir2
    │   │   ├── Dir3
    │   │   │   ├── Dir4
    │   │   │   │   ├── __init__.py
    │   │   │   │   └── script1.py
    │   │   │   └── __init__.py
    │   │   └── __init__.py
    │   └── __init__.py
    ├── Dir5
    │   ├── Dir6
    │   │   ├── __init__.py
    │   │   └── script2.py
    │   └── __init__.py
    └── __init__.py

The __init__.py files are empty, and the other files contents are as follows:

# src/main.py

from root.Dir1.Dir2.Dir3.Dir4.script1 import hello

if __name__ == '__main__':
    hello()
# src/root/Dir1/Dir2/Dir3/Dir4/script1.py

from .....Dir5.Dir6.script2 import world

def hello():
    print("Hello, ")
    world()

# src/root/Dir5/Dir6/script2.py

def world():
    print("World!")

To run this example:

$ python3 src/main.py 
Hello, 
World!