PythonMastery
reference 4 min read · lesson 54 of 60 in Errors

ImportError: attempted relative import with no known parent package

1 · The lesson

read

What this error means

The file has an import with a leading dot — from .totals import invoice_total — and Python doesn't know what package that dot refers to. A relative import means "from the package I belong to". When you run a file directly with python path/to/file.py, Python loads it as a standalone script called __main__, with no package at all. So "relative to my package" has nothing to be relative to.

The code is usually fine. The way you started it is the problem.

When you see it

Take a small project with one package:

python
billing/
    invoices/
        __init__.py
        totals.py
        report.py

report.py imports from its sibling:

python · run on your machine
# invoices/report.py
from .totals import invoice_total

Run that file directly — from the project root or from inside invoices/, it makes no difference:

python
cd C:\Users\Ada\billing
python invoices/report.py
python
Traceback (most recent call last):
  File "C:\Users\Ada\billing\invoices\report.py", line 1, in <module>
    from .totals import invoice_total
ImportError: attempted relative import with no known parent package

python -m report from inside the package folder fails the same way, with two extra <frozen runpy> lines at the top of the traceback.

Why it happens

Every module knows two things about itself: its name (__name__) and the package it lives in (__package__). Relative imports are resolved from __package__. Here is what each way of starting report.py gives it:

How you run it__name____package__from .totals works?
python invoices/report.py'__main__'NoneNo
cd invoices then python report.py'__main__'NoneNo
python -m invoices.report (from billing/)'__main__''invoices'Yes

With None as the package, the leading dot has no meaning, and Python raises this error rather than guess.

Note what does not fix it: adding an __init__.py. The package above already has one. __init__.py marks a folder as a package, but only for code that is imported as part of that package — running a file by its path skips packages entirely.

How to fix it

Run it as a module, from the folder above the package. This is the real fix, and it keeps the relative import:

python
cd C:\Users\Ada\billing
python -m invoices.report
python
Invoice total: 11.25

Two details trip people up. Use dots, not slashes, and leave off .py — it is invoices.report, a module name, not a file path. And run it from billing/, the folder that contains invoices/, so Python can find the package.

Or switch to an absolute import. from invoices.totals import invoice_total names the package outright. It still needs python -m invoices.report from the project root — run the file directly and you trade this error for a different one:

python
Traceback (most recent call last):
  File "C:\Users\Ada\billing\invoices\report.py", line 1, in <module>
    from invoices.totals import invoice_total
ModuleNotFoundError: No module named 'invoices'

Absolute imports are the style PEP 8 recommends, and they read better in a large codebase, but they do not remove the need to start the program properly.

Better: keep entry points outside the package. A file you run directly shouldn't be one that other modules import. Put a small script next to the package and let everything inside use relative imports freely:

python
billing/
    run_report.py         <- python run_report.py
    invoices/
        __init__.py
        totals.py
        report.py
python · run on your machine
# run_report.py
from invoices.report import main

main()

Now report.py defines a main() function instead of doing its work at the top level, python run_report.py works from billing/, and so do your tests. For an installable project, the same idea is a [project.scripts] entry in pyproject.toml.

Don't drop the dot to get from totals import .... The file then runs by path, but the moment it is used as part of the package — python -m invoices.report, or an import from anywhere else — it fails with No module named 'totals'. And don't reach for sys.path.append(...) at the top of the file: it makes imports depend on where the file sits on disk, and the next person to move a folder inherits the mess.

In your editor, change how it runs the file. The Run button is usually what causes this:

  • VS Code: "Run Python File" runs python path/to/file.py. Create a debug configuration in .vscode/launch.json with "module": "invoices.report" instead of "program", and set "cwd" to the project root.
  • PyCharm: edit the run configuration and switch the dropdown from Script path to Module name, then enter invoices.report. Set the working directory to the project root.

You can watch Python do the resolving with importlib.util.resolve_name, the same rule the import statement uses. Give it a package and the dot turns into a real module name; give it none and you get this page's error in different words:

python
from importlib.util import resolve_name

print(resolve_name(".totals", "invoices"))            # python -m invoices.report
print(resolve_name("..config", "billing.invoices"))   # two dots climb one level

try:
    resolve_name(".totals", None)                     # python invoices/report.py
except ImportError as err:
    print("ImportError:", err)

When you'd actually see this in real code

  • Clicking Run in VS Code on a module deep inside src/myapp/ to test one function.
  • A Flask or FastAPI project where app/main.py does from .routes import router and someone runs python app/main.py instead of uvicorn app.main:app or python -m app.main.
  • Adding if __name__ == "__main__": test code to a module that uses relative imports, then trying to run it.
  • Reorganising a flat folder of scripts into a package — the scripts were run by path before, and still are.

See Also

Found this useful? Share it with a friend who’s learning Python.