Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

You could use relative imports (python >= 2.5):

from ... import nib

(What’s New in Python 2.5) PEP 328: Absolute and Relative Imports

EDIT: added another dot '.' to go up two packages

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,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir) 

import mymodule

edit: the __file__ attribute is not always given. Instead of using os.path.abspath(__file__) I now suggested using the inspect module to retrieve the filename (and path) of the current file

I posted a similar answer also to the question regarding imports from sibling packages. You can see it here. The following is tested with Python 3.6.5, (Anaconda, conda 4.5.1), Windows 10 machine.

Solution without sys.path hacks

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 setup.py to the root folder

The contents of the setup.py can be simply

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

Basically "any" setup.py would work. This is just a minimal working example.

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)

To learn more about this, just Google out "python virtual env 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 myproject 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.

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:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

4) Import by appending 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!

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

You can use OS depending path in "module search path" which is listed in sys.path . So you can easily add parent directory like following

import sys
sys.path.insert(0,'..')

If you want to add parent-parent directory,

sys.path.insert(0,'../..')

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

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 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 less 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 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

Here is more generic solution that includes the parent directory into sys.path (works for me):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))

For 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.

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

When not being in a package environment with __init__.py files 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('.').absolute().parent))

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)

Above 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.

same sort of style as the past answer - but in fewer lines :P

import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)

file returns the location you are working in

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.

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top