Marty Wallace
Marty Wallace

Reputation: 35734

Import file from parent directory?

I have the following directory structure:

application
    tests
        main.py
    main.py

application/main.py contains some functions.

tests/main.py will contain my tests for these functions but I can't import the top level main.py. I get the following error:

ImportError: Import by filename is not supported.

I am attempting to import using the following syntax:

import main

What am I doing wrong?

Upvotes: 144

Views: 207655

Answers (10)

Ka Wa Yip
Ka Wa Yip

Reputation: 2973

I find that the safest way to do this is by navigating to the parent directory before running the import statement and, more importantly, returning to the original directory afterward. This is safer and easier for future debug than using sys.path.append.

os.chdir('../') #equivalent to %cd ../
import main
os.chdir('./tests') #equivalent to %cd tests

Upvotes: -1

viocha
viocha

Reputation: 1

I written a script py-run.py, which provides a convenient way to run Python file from any location while supporting relative imports without the need for sys.path.append. It achieves this by dynamically constructing the parent module path and running the Python file from the top-level module.

This is particularly useful when your script relies on modules located in parent directories and you want to avoid modifying the sys.path directly.

import os
import subprocess
import sys
from pathlib import Path
from typing import Optional

if len(sys.argv) == 1:
    print('Usage: python py-run.py [file] [args]')
    exit(1)

def get_module_path(filepath) -> [Optional[str], Optional[str]]:
    p = Path(filepath).resolve()
    if '.' in p.stem: return [None, None]
    
    module_path = [p.stem]
    p = p.parent
    while not p.is_mount() and '.' not in p.name:
        module_path.insert(0, p.name)
        p = p.parent
    return '.'.join(module_path), str(p)

module_path, module_parent = get_module_path(sys.argv[1])
if module_path:
    cmd = ['python', '-m', module_path]+sys.argv[2:]
    py_path = os.environ.get('PYTHONPATH')
    if py_path is None: py_path = []
    else: py_path = py_path.split(os.pathsep)
    os.environ['PYTHONPATH'] = os.pathsep.join(py_path+[module_parent])
else:
    cmd = ['python']+sys.argv[1:]

code = subprocess.run(cmd).returncode
exit(code)

Usage:

python py-run.py [file] [args]

Example:

─top
 └───par
     │   par_script.py
     │
     ├───my
     │       my_script.py
     │
     └───sib
             sib_script.py

If you have a script my_script.py containing relative imports like from .. import par_script or from ..sib import sib_script, you can run it with:

python py-run.py my_script.py arg1 arg2

This will execute my_script.py as a module with the path top.par.my.my_script, correctly resolving relative imports from my_project/utils.

Upvotes: 0

Mr_and_Mrs_D
Mr_and_Mrs_D

Reputation: 34016

8 years after - still most other answers here are still not correct unfortunately - apart LennartRegebro's (and BrenBarn's) which is incomplete. For the benefit of future readers - the OP should, first of all, add the __init__.py files as in

root
    application
        __init__.py
        main.py
        tests
            __init__.py
            main.py

then:

$ cd root
$ python -m application.tests.main # note no .py

or

$ cd application
$ python -m tests.main

Running a script directly from inside its package is an antipattern - the correct way is running with the -m switch from the parent directory of the root package - this way all packages are detected and relative/absolute imports work as expected.

Upvotes: 51

John Harding
John Harding

Reputation: 459

All answers are slightly confusing for a python newbie like me who isn't making a package and is using pylint. Let me clear it up.

If the directory structure is:

application
    main.py
    /tests
        test.py

Here is how you import one method from main.py into test.py

Here is application/main.py

def add_nums(a, b):
    print(a + b)

def subtract_nums(a, b):
    print(a - b)

Here is application/tests/test.py

import sys

sys.path.append("..")
# or sys.path.append("/Users/full/path/to/file")

# pylint: disable=wrong-import-position
from main import add_nums
# pylint: enable=wrong-import-position

Upvotes: 5

0x90
0x90

Reputation: 40972

You must add the application dir to your path:

import sys
sys.path.append("/path/to/dir")
from app import object

Or from shell:

setenv PATH $PATH:"path/to/dir"

In case you use windows: Adding variable to path in windows.

Or from the command line:

set PATH=%PATH%;C:\path\to\dir

Please mind the diff between PYTHONPATH, PATH, sys.path.

Upvotes: 59

Andy Fraley
Andy Fraley

Reputation: 1181

To import a file in a different subdirectory of the parent directory, try something like this:

sys.path.append(os.path.abspath('../other_sub_dir'))
import filename_without_py_extension

Edit: Missing closing bracket.

Upvotes: 7

Lennart Regebro
Lennart Regebro

Reputation: 172181

First of all you need to make your directories into packages, by adding __init__.py files:

application
    tests
        __init__.py
        main.py
    __init__.py
    main.py

Then you should make sure that the directory above application is on sys.path. There are many ways to do that, like making the application infto a package and installing it, or just executing things in the right folder etc.

Then your imports will work.

Upvotes: 27

StackUP
StackUP

Reputation: 1303

in python . exists for same directory, .. for parent directory to import a file from parent directory you can use ..

from .. import filename (without .py extension)

Upvotes: -1

user4490090
user4490090

Reputation:

If you'd like your script to be more portable, consider finding the parent directory automatically:

import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# import ../db.py
import db

Upvotes: 122

BrenBarn
BrenBarn

Reputation: 251345

You cannot import things from parent/sibling directories as such. You can only import things from directories on the system path, or the current directory, or subdirectories within a package. Since you have no __init__.py files, your files do not form a package, and you can only import them by placing them on the system path.

Upvotes: 9

Related Questions