Skip to content
Happy Programming Guide
Start learning
Python

How to Import from the Parent Directory in Python

Four ways to import a module from a parent folder, why sys.path.append is the worst of them, and the editable install that solves it properly.

A backlit keyboard in a dark room

Python cannot import from a parent directory by default, because only the folder of the script you ran is on the import path. There are four ways round it. The quick one is sys.path manipulation, the correct one is an editable install, and the one most tutorials recommend — running with python -m from the project root — is usually all you actually need.

The situation#

Output
project/
    config.py
    scripts/
        report.py       <- wants to import config
Terminal
python scripts/report.py
Output
ModuleNotFoundError: No module named 'config'

Running a file puts that file’s folder first on sys.path. So project/scripts/ is searched and project/ is not. Confirm it for yourself:

Python
import sys
print(sys.path[0])

Method 1: run from the root with -m (usually the answer)#

Output
project/
    __init__.py
    config.py
    scripts/
        __init__.py
        report.py
Python
# scripts/report.py
from project.config import SETTINGS
Terminal
cd /path/to/parent-of-project
python -m project.scripts.report

The -m flag runs a module rather than a file, and it puts the current directory on the path instead of the file’s folder. Absolute imports then resolve.

This requires no configuration and no path hacking. If you can arrange to run from the project root, stop here.

Method 2: an editable install (correct for anything lasting)#

Add a minimal pyproject.toml at the project root:

Output
[project]
name = "myproject"
version = "0.1.0"

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
Terminal
python -m pip install -e .

Now your package is importable from anywhere, in any script, in tests, in a notebook — and the -e means it points at your source folder, so edits take effect immediately with no reinstall.

Python
from myproject.config import SETTINGS     # works from any directory

This is what real projects do. It takes five minutes once and then you never think about import paths again.

Method 3: sys.path manipulation (last resort)#

Python
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from config import SETTINGS

It works. The reasons to avoid it:

  • It must appear before the import, which linters flag and formatters may move.
  • It ties the file to its position in the folder tree.
  • Every script that needs it has to repeat it.
  • It hides a structural problem rather than fixing it.

Two details if you do use it. Path(__file__).resolve() rather than a relative path, so it works regardless of the working directory. And insert(0, ...) rather than append, so your folder is searched first — though that also means your module can shadow a standard library one, so name things carefully.

Method 4: relative imports#

Python
# scripts/report.py
from ..config import SETTINGS

Valid only when the file is run as part of a package. Run it directly and you get:

Output
ImportError: attempted relative import with no known parent package

So this is really method 1 in different clothing — it still requires python -m. Prefer absolute imports; they say where the code lives and survive files being moved.

Tests#

Pytest handles this better than plain Python. With this layout:

Output
project/
    pyproject.toml
    myproject/
        __init__.py
        config.py
    tests/
        test_config.py
Terminal
python -m pytest

Running pytest with python -m puts the current directory on the path, so from myproject.config import SETTINGS works in the tests without any configuration. This is why python -m pytest is recommended over the bare pytest command.

An empty conftest.py at the project root also does the job, because pytest adds the folder containing it to the path.

Notebooks#

Python
import sys
from pathlib import Path

sys.path.insert(0, str(Path.cwd().parent))

Notebooks are the one place where path manipulation is genuinely reasonable — they are interactive and not part of a package. Even so, an editable install is tidier if the notebook is part of a real project.

Which to use#

Situation Use
Small project, you control how it runs python -m package.module
Anything with tests or more than one entry point Editable install
A notebook sys.path manipulation
A one-off throwaway script sys.path, and do not feel bad
Code others will install Editable install, always

Questions people ask#

Why does Python not just search upwards?

Because it would be ambiguous and unpredictable — the modules available would depend on where the file happened to sit, and two projects could shadow each other. An explicit path is safer even though it is less convenient.

Do I need __init__.py files?

Not strictly since Python 3.3, but include them. They make the package boundary explicit and avoid surprises when two folders share a name.

What is the difference between pip install . and pip install -e .?

Without -e, your code is copied into site-packages and edits do not take effect until you reinstall. With -e, it points at your source folder, which is what you want during development.

Why does it work in PyCharm but not the terminal?

PyCharm adds the project root to the path automatically. That convenience hides the problem until you run the code somewhere else, which is a good argument for the editable install.

Where to go next#

Building a complete Python project, imports includedRead next

Keep reading

Python

The Python Standard Library

The modules that come with Python and are worth knowing: pathlib, json, csv, datetime, collections, itertools, re, argparse and more, each with…

4 min read

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *