Source: python – Importing modules from parent folder – Stack Overflow
I am running Python 2.5.
This is my folder tree:
ptdraft/
nib.py
simulations/
life/
life.py
(I also have __init__.py
in each folder, omitted here for readability)
How do I import the nib
module from inside the life
module? I am hoping it is possible to do without tinkering with sys.path.
Note: The main module being run is in the ptdraft
folder.
33 Answers
You could use relative imports (Python >= 2.5):
from ... import nib
(What’s New in Python 2.5) PEP 328: Absolute and Relative Imports
14 Comments
ValueError: Attempted relative import in non-package
__init__.py
is not the only thing you have to do: stackoverflow.com/questions/11536764/…ImportError: attempted relative import with no known parent package
I posted a similar answer also to the question regarding imports from sibling packages. You can see it here.
Solution without sys.path
hacks
Summary
- Wrap the code into one folder (e.g.
packaged_stuff
) - Create a
pyproject.toml
(older alternative:setup.py
) - Pip install the package in editable state with
pip install -e <myproject_folder>
- Import using
from packaged_stuff.modulename import function_name
Setup
I assume the same folder structure as in the question
.
└── ptdraft
├── __init__.py
├── nib.py
└── simulations
├── __init__.py
└── life
├── __init__.py
└── life.py
I call the .
the root folder, and in my case it is located in C:\tmp\test_imports
.
Steps
1) Add a pyproject.toml
to the root folder
The contents of the pyproject.toml
can be simply
[project]
name = "ptdraft"
version = "0.1.0"
description = "My small project"
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.2,<4"]
Basically “any” valid pyproject.toml
would work. This is just a minimal working example, which uses flit as build backend.
2) Use a virtual environment
If you are familiar with virtual environments, activate one, and skip to the next step. Usage of virtual environments are not absolutely required, but they will really help you out in the long run (when you have more than 1 project ongoing..). The most basic steps are (run in the root folder)
- Create virtual env
python -m venv venv
- Activate virtual env
. venv/bin/activate
(Linux) or./venv/Scripts/activate
(Win)
- Deactivate virtual env
deactivate
(Linux)
To learn more about this, just Google out “python virtualenv tutorial” or similar. You probably never need any other commands than creating, activating and deactivating.
Once you have made and activated a virtual environment, your console should give the name of the virtual environment in parenthesis
PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>
3) pip install your project in editable state
Install your top level package (here ptdraft
) using pip
. The trick is to use the -e
flag when doing the install. This way it is installed in an editable state, and all the edits made to the .py files will be automatically included in the installed package. Note that the -e
flag with pyproject.toml requires pip 21.3 or newer.
In the root directory, run
pip install -e .
(note the dot, it stands for “current directory”)
You can also see that it is installed by using pip freeze
(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///home/user/projects/ptdraft
Installing build dependencies ... done
Checking if build backend supports build_editable ... done
Getting requirements to build editable ... done
Preparing editable metadata (pyproject.toml) ... done
....
Successfully built ptdraft
Installing collected packages: ptdraft
Successfully installed ptdraft-0.1.0
(venv) PS C:\tmp\test_imports> pip freeze
ptdraft==0.1.0
4) Import by prepending mainfolder
to every import
In this example, the mainfolder
would be ptdraft
. This has the advantage that you will not run into name collisions with other module names (from python standard library or 3rd party modules).
Example Usage
nib.py
def function_from_nib():
print('I am the return value from function_from_nib!')
life.py
from ptdraft.nib import function_from_nib
if __name__ == '__main__':
function_from_nib()
Running life.py
(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!
38 Comments
sys.path
and where the code would typically be installed by end users. That’s better than sys.path
hacks (and you can include using PYTHONPATH in that category of path hacks) because it means the code in development should behave the same way for you as it does for other users. In contrast, when using path modifications, import statements would be resolved in a different way.Relative imports (as in from .. import mymodule
) only work in a package. To import ‘mymodule’ that is in the parent directory of your current module:
import os
import sys
import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)
import mymodule
Note: The __file__
attribute is not always given. Instead of using os.path.abspath(__file__)
I suggest using the inspect module to retrieve the filename (and path) of the current file.
7 Comments
sys.path.insert(1, os.path.join(sys.path[0], '..'))
parentdir
, but in one of the paths allready specified in sys.path
, there is another module with the name ‘mymodule’. Inserting the parentdir
as the first element of the sys.path
list assures that the module from parentdir
will be imported instead.sys.path.insert(1, os.path.realpath(os.path.pardir))
works too.It seems that the problem is not related to the module being in a parent directory or anything like that.
You need to add the directory that contains ptdraft
to PYTHONPATH
You said that import nib
worked with you, that probably means that you added ptdraft
itself (not its parent) to PYTHONPATH.
4 Comments
import sys
and then sys.path.append("..\<parent_folder>")
You can use an OS-dependent path in “module search path” which is listed in sys.path.
So you can easily add the parent directory like the following:
import sys
sys.path.insert(0, '..')
If you want to add the parent-parent directory,
sys.path.insert(0, '../..')
6 Comments
sys.path.insert(0,'../sibling_dir')
I don’t know much about Python 2.
In Python 3, the parent folder can be added as follows:
import sys
sys.path.append('..')
…and then one is able to import modules from it.
4 Comments
'..'
leads to the directory with the module in question.sys.path.append(os.path.dirname(os.path.abspath(__file__)))
instead. This will work no matter what the working directory is.If adding your module folder to the PYTHONPATH didn’t work, You can modify the sys.path list in your program where the Python interpreter searches for the modules to import, the Python documentation says:
When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:
- the directory containing the input script (or the current directory).
- PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
- the installation-dependent default.
After initialization, Python programs can modify sys.path. The directory containing the script being run is placed at the beginning of the search path, ahead of the standard library path. This means that scripts in that directory will be loaded instead of modules of the same name in the library directory. This is an error unless the replacement is intended.
Knowing this, you can do the following in your program:
import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')
# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft
3 Comments
/path/to/ptdraft
would have to be edited. There are solutions that work out the current directory of the file and import it from the parent folder that way as well.The pathlib library (included with >= Python 3.4) makes it very concise and intuitive to append the path of the parent directory to the PYTHONPATH:
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).absolute().parent))
8 Comments
Here is an answer that’s simple so you can see how it works, small and cross-platform. It only uses built-in modules (os
, sys
and inspect
), so it should work on any operating system (OS) because Python is designed for that.
Shorter code for answer – fewer lines and variables
from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module # Replace "my_module" here with the module name.
sys.path.pop(0)
For fewer lines than this, replace the second line with import os.path as path, sys, inspect
. Add inspect.
at the start of getsourcefile
(line 3) and remove the first line.
- however this imports all of the module so it could need more time, memory and resources.
The code for my answer (longer version)
from inspect import getsourcefile
import os.path
import sys
current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]
sys.path.insert(0, parent_dir)
import my_module # Replace "my_module" here with the module name.
It uses an example from a Stack Overflow answer How do I get the path of the current executed file in Python? to find the source (filename) of running code with a built-in tool.
from inspect import getsourcefile from os.path import abspath
Next, wherever you want to find the source file from you just use:
abspath(getsourcefile(lambda:0))
My code adds a file path to sys.path
, the Python path list because this allows Python to import modules from that folder.
After importing a module in the code, it’s a good idea to run sys.path.pop(0)
on a new line when that added folder has a module with the same name as another module that is imported later in the program. You need to remove the list item added before the import, not other paths.
If your program doesn’t import other modules, it’s safe to not delete the file path because after a program ends (or restarting the Python shell), any edits made to sys.path
disappear.
Notes about a filename variable
My answer doesn’t use the __file__
variable to get the file path/filename of running code because users here have often described it as unreliable. You shouldn’t use it for importing modules from parent folder in programs used by other people.
Some examples where it doesn’t work (quote from this Stack Overflow question):
• it can’t be found on some platforms. It sometimes isn’t the full file path
py2exe
doesn’t have a__file__
attribute, but there is a workaround- When you run from IDLE with
execute()
there is no__file__
attribute- OS X 10.6 where I get
NameError: global name '__file__' is not defined
5 Comments
sys.path.pop(0)
immediately after importing the desired module. However, subsequent imports still went to that location. For example, I have app/config
, app/tools
and app/submodule/config
. From submodule
, I insert app/
to import tools
, then remove app/
and try to import config
, but I get app/config
instead of app/submodule/config
.tools
‘s imports was also importing config
from the parent dir. So when I later tried to do sys.path.pop(0); import config
inside submodule
, expecting to get app/submodule/config
, I was actually getting app/config
. Evidently Python returns a cached version of a module with the same name instead of actually checking the sys.path
for a module matching that name. sys.path
in this case was being altered correctly, Python was just not checking it due to the same-named module already having been loaded.sys.modules
… sys.modules is writable. Deleting a key will invalidate the cache entry for the named module, causing Python to search anew upon its next import. … Beware though, as if you keep a reference to the module object, invalidate its cache then re-import, the two objects will be different. By contrast, importlib.reload()
will reuse and reinitialise the module contents …os.path.realpath(getsourcefile(lambda:0) + "/../..")
. Will get current source file appended with two parent directory symbols. I didn’t use os.path.sep
as getsourcefile
was returns a string using /
even on Windows. realpath
will take care of popping off two directory entries from the full path (in this case, the filename, and then the current directory) which gives the parent directory.In a Jupyter Notebook (opened with JupyterLab or Jupyter Notebook)
As long as you’re working in a Jupyter Notebook, this short solution might be useful:
%cd ..
import nib
It works even without an __init__.py
file.
I tested it with Anaconda 3 on Linux and Windows 7.
Here is a more generic solution that includes the parent directory into sys.path (it works for me):
import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
3 Comments
import os, sys\n sys.path.insert(0,os.path.pardir)
same thing, but sorther 🙂 (no line feeds in comments)os.path.pardir
you won’t be getting the realpath of the parent, just relative path from where you’re calling the sys.path.insert()
__file__
variable which can be unreliable (isn’t always the full file path, doesn’t work on every operating system etc.) as StackOverflow users have often mentioned. Changing the answer to not include it will cause less problems and be more cross-compatible. For more information, see stackoverflow.com/a/33532002/3787376.I found the following way works for importing a package from the script’s parent directory. In the example, I would like to import functions in env.py
from app.db
package.
.
└── my_application
└── alembic
└── env.py
└── app
├── __init__.py
└── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
2 Comments
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
sys.path
at all is usually a bad idea. You need some way (e.g., PYTHONPATH
) for your library to be accessible to other code (or else it’s not really a library); just use that all the time!The previous mentioned solutions are also fine. Another solution to this problem is:
If you want to import anything from top level directory. Then,
from ...module_name import *
Also, if you want to import any module from the parent directory. Then,
from ..module_name import *
Also, if you want to import any module from the parent directory. Then,
from ...module_name.another_module import *
This way you can import any particular method if you want to.
4 Comments
sys.path.append
hacks above and I cannot import my lib like this. This is what I first tried to do before starting to google 🙂 Ok, it was this ValueError: attempted relative import beyond top-level package
from ... import name
does not mean “import name from top-level”, but “import name from the parent’s parent”. I created a nested directory structure a1/.../a9
of nine directories where each one contains an __init__.py
importing the next directory with from . import next_dir
. The last directory had a file with from ....... import b
which then imported a1/a2/a3/b
. (tested with Python 3.8.2)Two line simplest solution
import os, sys
sys.path.insert(0, os.getcwd())
If parent is your working directory and you want to call another child modules from child scripts.
You can import all child modules from parent directory in any scripts and execute it as
python child_module1/child_script.py
1 Comment
For completeness, there one simple solution. It’s to run life.py as a module like this:
cd ptdraft
python -m simulations.life.life
This way you can import anything from nib.py as ptdraft directory is in the path.
2 Comments
sys.path
or environment variables like PYTHONPATH
, for more detail about -m
you can check out this stackoverflow threadFor me the shortest and my favorite oneliner for accessing to the parent directory is:
sys.path.append(os.path.dirname(os.getcwd()))
or:
sys.path.insert(1, os.path.dirname(os.getcwd()))
os.getcwd() returns the name of the current working directory, os.path.dirname(directory_name) returns the directory name for the passed one.
Actually, in my opinion Python project architecture should be done the way where no one module from child directory will use any module from the parent directory. If something like this happens it is worth to rethink about the project tree.
Another way is to add parent directory to PYTHONPATH system environment variable.
3 Comments
import sys
sys.path.append('../')
1 Comment
This is the same sort of style as the past answers, but in fewer lines 😛
import os, sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0, parentdir)
file returns the location you are working in.
8 Comments
I have a solution specifically for Git repositories.
First I used sys.path.append('..')
and similar solutions. This causes especially problems if you are importing files which are themselves importing files with sys.path.append('..')
.
I then decided to always append the root directory of the Git repository. In one line it would look like this:
sys.path.append(git.Repo('.', search_parent_directories=True).working_tree_dir)
Or in more details like this:
import os
import sys
import git
def get_main_git_root(path):
main_repo_root_dir = git.Repo(path, search_parent_directories=True).working_tree_dir
return main_repo_root_dir
main_repo_root_dir = get_main_git_root('.')
sys.path.append(main_repo_root_dir)
For the original question: Based on what the root directory of the repository is, the import would be
import ptdraft.nib
or
import nib
1 Comment
In a Linux system, you can create a soft link from the “life” folder to the nib.py file. Then, you can simply import it like:
import nib
2 Comments
Our folder structure:
/myproject
project_using_ptdraft/
main.py
ptdraft/
__init__.py
nib.py
simulations/
__init__.py
life/
__init__.py
life.py
The way I understand this is to have a package-centric view. The package root is ptdraft
, since it’s the top most level that contains __init__.py
All the files within the package can use absolute paths (that are relative to package root) for imports, for example in life.py
, we have simply:
import ptdraft.nib
However, to run life.py
for package dev/testing purposes, instead of python life.py
, we need to use:
cd /myproject
python -m ptdraft.simulations.life.life
Note that we didn’t need to fiddle with any path at all at this point.
Further confusion is when we complete the ptdraft
package, and we want to use it in a driver script, which is necessarily outside of the ptdraft
package folder, aka project_using_ptdraft/main.py
, we would need to fiddle with paths:
import sys
sys.path.append("/myproject") # folder that contains ptdraft
import ptdraft
import ptdraft.simulations
and use python main.py
to run the script without problem.
Helpful links:
Comments
I had a problem where I had to import a Flask application, that had an import that also needed to import files in separate folders. This is partially using Remi’s answer, but suppose we had a repository that looks like this:
.
└── service
└── misc
└── categories.csv
└── test
└── app_test.py
app.py
pipeline.py
Then before importing the app object from the app.py
file, we change the directory one level up, so when we import the app (which imports the pipeline.py
), we can also read in miscellaneous files like a csv file.
import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir)
os.chdir('../')
from app import app
After having imported the Flask app, you can use os.chdir('./test')
so that your working directory is not changed.
Comments
Using pathlib.Path
import sys
from pathlib import Path
sys.path.append(str(Path(f"{__file__}").parent.parent))
import my_module
Comments
Work with libraries. Make a library called nib, install it using setup.py, let it reside in site-packages and your problems are solved. You don’t have to stuff everything you make in a single package. Break it up to pieces.
1 Comment
site-packages
every time they run it on a different computer.It’s seems to me that you don’t really need to import the parent module. Let’s imagine that in nib.py you have func1() and data1, you need to use in life.py
nib.py
import simulations.life.life as life
def func1():
pass
data1 = {}
life.share(func1, data1)
life.py
func1 = data1 = None
def share(*args):
global func1, data1
func1, data1 = args
And now you have the access to func1 and data in life.py. Of course you have to be careful to populate them in life.py before you try to use them,
1 Comment
After removing some sys path hacks, I thought it might be valuable to add
My preferred solution.
Note: this is a frame challenge – it’s not necessary to do in-code.
Assuming a tree,
project
└── pkg
└── test.py
Where test.py
contains
import sys, json; print(json.dumps(sys.path, indent=2))
Executing using the path only includes the package directory
python pkg/test.py
[
"/project/pkg",
...
]
But using the module argument includes the project directory
python -m pkg.test
[
"/project",
...
]
Now, all imports can be absolute, from the project directory. No further skullduggery required.
Comments
i struggled with this a lot when developing a new module, but went with the solution to append the directories to os.path, so what i did was to add to every __init__.py
in my project this:
import os, sys
pardir = ""
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(current_dir))
while pardir != "p":
parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir))
pardir = os.path.basename(parent_dir)
current_dir = parent_dir
sys.path.append(parent_dir)
where “p” is my project folder, like: c:/p/ this solution won’t help you when opening the project to others and you will have to (like me) find another solution (i probably will post it here when i’m done with my code on how i went from here) but at least this will help you start into prototyping faster. i know this solution has been discussed, i just wanted to share my copy-and-paste solution. My structure was this:
p
|---project
|---src
| |----__init__.py
| |----module.py
|---tests
| |----__init__.py
| |----tests.py
|---__init__.py
having an __init__.py
in all places did not help me running my tests.py from p/project/tests/tests.py
i tried a lot of solutions by now, but most of them scared me or simply didnt work for me. Why is this so complicated with python? The solution i chose did not satisfy me either.
Comments
First the code:
from pathlib import Path
import sys
ptdraft = str(Path(sys.path[0]).parent.parent)
sys.path.insert(0, ptdraft)
import nib
Per the docs,
sys.path is initialized from these locations:
- The directory containing the input script (or the current directory when no file is specified).
- PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
- The installation-dependent default (by convention including a site-packages directory, handled by the site module).
Hence, sys.path
is a list like ["script/directory/path", "pythonpath/element/1", "pythonpath/element/2", ..., ".../lib/python3.x", ..., ".../lib/python3.x/site-packages"]
The path to the script directory is always in position 0 of that list. Since OP has the module (nib.py
) that he wants to import two directories up, he can get its directory with Path(sys.path[0].parent.parent)
. sys.path
only works with strings, so the result must be wrapped in str()
.
The next step is to insert this path into the correct location in sys.path
. The location only matters if multiple modules with the same name can be found anywhere in the sys.path
directories. There are three sensible choices for this:
.insert(0, ptdraft)
inserts at the start of the list, hence the highest priority..insert(1, ptdraft)
puts the (grand)parent directory after the script directory and before the directories on thePYTHONPATH
..append(ptdraft)
puts the (grand)parent directory at the very end as a fall-back.
CommentedApr 3, 2009 at 16:27
sys.path
orPYTHONPATH
answers and checking out np8’s excellent answer. Yes, it’s a long read. Yes, it looks like a lot of work. But it’s the only answer that actually solves the problem correctly and cleanly.CommentedOct 30, 2018 at 22:24
CommentedFeb 21, 2019 at 5:38
python -m
).CommentedSep 27, 2019 at 0:50
CommentedDec 13, 2019 at 0:21