Gasp0de
Gasp0de

Reputation: 1429

How to import a module from a different folder?

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?

Upvotes: 75

Views: 203730

Answers (5)

user26849054
user26849054

Reputation: 1

Proper Reliable Solution:

  • First, change the Current Working Directory to 2 folders before the Current File (the root folder of this tree).

  • Then, change the Module Search Path, to the folder that the desired module is in.

  • Finally, just import the module as per usual.

(this will require the "os" and "sys" standard libraries/modules)


For the example where:

  • The file being imported into is: "folder_shared/folder1a/folder1b/MainFile.py";
  • And the module being imported is: "folder_shared/folder2a/folder2b/ModuleFile.py";

Simply put in MainFile.py:

import os
import sys

os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
sys.path.append(os.path.join(os.getcwd(), 'folder2a', 'folder2b'))

import ModuleFile

And that's literally it.

The Current Working Directory will remain as the Root Directory "folder_shared", but the Path that python will look for modules in, will remain in "Folder2b" until sys.path.append(os.path.join(os.getcwd(), 'path', 'to', 'module')) is called again to change it.


For your example, in both your "api.py" and "backend.py" files, you would just put:

import os
import sys

os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'))
sys.path.append(os.path.join(os.getcwd(), 'models'))

import some_model

And you should be all sweat.

Upvotes: -1

wim
wim

Reputation: 362687

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 pyproject.toml file and installing your package. Follow the PyPA packaging guide. After you have written your pyproject.toml file, from within the same directory, execute this to setup the correct entries in sys.path:

pip install --editable .

Upvotes: 50

Jaden Taylor
Jaden Taylor

Reputation: 13

Due to the file not being in your systems path, you will need to append the parent directory using import sys, like so.

import sys
sys.path.append('myproject')
from models import some_model

This is the simplest way to do it, because the module you want to access is so close to your parent directory. So now your systems path will be in myproject.

Upvotes: 0

Jordan Mattiuzzo
Jordan Mattiuzzo

Reputation: 442

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 while Python is running just for the current run of the program. This will not affect any other Python programs run later.

You can do this by:

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

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

If your library is NOT close to the current directory, an absolute path could be simpler:

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

Yes, this is a hack. But it can be helpful as a short-term work-around.

Upvotes: 36

Benyamin Jafari
Benyamin Jafari

Reputation: 34026

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

Upvotes: 10

Related Questions