Search code examples
pythonpython-modulepythonpath

How do I import an object from main.py (in the root directory), into a module contained in a subdirectory?


So, this is the project structure:

<root directory>
- app 
- - name
- - - module1
- - - module2
- - - module3
- - - - tests.py 
- test_db.py

test_db.py contains an object called client which I need in all the tests.py files in different modules.

I could simply move test_db.file inside the app directory but test_db.py needs to import app from main.py which is again in the root directory and will cause the same issue again.

In every tests.py I need something like:

from ....test_db import client

but it just gets:

from ....test_db import client

E ImportError: attempted relative import beyond top-level package

So, I just can't figure out importing an object/package from the root directory.

P.S: I know one simple solution would be to change the directory structure a little, but It's a large project and changing the structure would be a LOT of work. So, ideally I need some way to add the root directory to the path inside every tests.py file. Something like this:

app/name/module3/tests.py:

import sys

#sys.path.something.. I am not really familiar with how to use this

from ....test_db import client #this should import client from test_db in root instead of throwing error

def test_create_todo():
    pass

Solution

  • If you want to do a relative import without hacking around with sys.path or custom loaders/finders, you need:

    • the top-most directory you import relative to to contain an __init__.py
    • to specify said directory as a package in the path to your __main__ module (i.e. python -m root.stuff.main)

    In short, this means you could add a directory project to contain the test_db.py and app, add an __init__.py, and call your program with python -m project.app.stuff.main.

    If this doesn't work for you (you mention you don't want to change the project structure) you do have other options.

    You could install each as its own package (app, and tests). Create a setup.py/setup.cfg/pyproject.toml, put test_db.py in a package tests, and pip install them as editable packages pip install -e .. This will allow you to import either without relative imports (just import app, and import tests.test_db, regardless of file). This is personally the route I would go, and recommend doing.

    Otherwise, if you're just looking for a quick fix, there exists one more easy + hacky solution. You could add the path for test_db.py to sys.path (every path in this list is explored for the target module when importing), so you could import test_db from anywhere. Again, this is very hacky, and I don't recommend it beyond quick sanity checks or extremely urgent patches.