61

I have a project which I want to structure like this:

myproject
├── api
│   ├── __init__.py
│   └── api.py
├── backend
│   ├── __init__.py
│   └── backend.py
├── models
│   ├── __init__.py
│   └── some_model.py
└── __init__.py

Now, I want to import the module some_model.py in both api.py and backend.py. How do I properly do this?

I tried:

from models import some_model

but that fails with ModuleNotFoundError: No module named 'models'.

I also tried:

from ..models import some_model

which gave me ValueError: attempted relative import beyond top-level package.

What am I doing wrong here? How can I import a file from a different directory, which is not a subdirectory?

Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150
Gasp0de
  • 1,199
  • 2
  • 12
  • 30

3 Answers3

42

Firstly, this import statement:

from models import some_model

should be namespaced:

# in myproject/backend/backend.py or myproject/api/api.py
from myproject.models import some_model

Then you will need to get the directory which contains myproject, let's call this /path/to/parent, into the sys.path list. You can do this temporarily by setting an environment variable:

export PYTHONPATH=/path/to/parent

Or, preferably, you can do it by writing a setup.py file and installing your package. Follow the PyPA packaging guide. After you have written your setup.py file, from within the same directory, execute this to setup the correct entries in sys.path:

pip install --editable .
sodimel
  • 864
  • 2
  • 11
  • 24
wim
  • 338,267
  • 99
  • 616
  • 750
  • 1
    This will work, but you should also be able to do a relative import from within the `myproject` package, which OP has tried. What would cause that not to work? – Phydeaux Feb 28 '18 at 22:20
  • 2
    That has been covered a billion times over at [Relative imports for the billionth time](https://stackoverflow.com/q/14132789/674039), so I won't go into it again. – wim Feb 28 '18 at 22:33
  • you think OP is running `api.py` as a script? that would explain it, yes – Phydeaux Feb 28 '18 at 22:37
  • I guess I'll dive into the PyPA packaging guide then =) – Gasp0de Feb 28 '18 at 23:34
  • 2
    I created a package and installed it using pip install --editable as suggested. Works like a charm, thank you! – Gasp0de Mar 05 '18 at 17:46
  • This answer really helped me. Although I’ve seen similar answers on many other similar questions, I couldn’t believe you have to install the package _you want to develop_ itself. That is – at least for me – _highly_ contra intuitive. Your answer clarified it. – claudio Dec 13 '20 at 09:07
  • After doing `pip install --editable .` if we made a change on any file under the imported folder, should we do `pip install --editable .` again to fetch the new updates? – alper Jun 06 '21 at 20:59
  • 1
    @alper it should not be necessary. The whole point of the —editable installation is that you are able to edit the installed code directly. – wim Jun 06 '21 at 21:42
29

Unfortunately, Python will only find your file if your file is in the systems path. But fear not! There is a way around this!

Using python's sys module, we can add a directory to the path just while Python is running, and once Python stops running, it will remove it from the path.

You can do this by:

import sys
sys.path.insert(0, '/path/to/application/app/folder')
import [file]

It is important to import sys and set the directory path before you import the file however.

Good luck!

Jordan.

  • 15
    Changing the path from within code, though possible, is messy and bad - it makes the test setup more difficult and makes the import ordering become significant. – wim Feb 28 '18 at 22:27
1

I would lay out two approaches:

Simply import some_model via absolute importing:

from myproject.models import some_model

Note that the myproject should be treated as an module (i.e. having __init__.py)


Or

You can add the previous path to the sys.path which I use in such parallel level modules:

import sys
sys.path.append('../')

from models import some_model
Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150