How to Import Modules in a Large Python Project

You’ll structure your project as a package and use stable imports that keep working as you add more files.

What you’ll build or solve

You’ll set up a package folder, import modules using absolute imports, and run your code in a way that matches how Python resolves imports. Done looks like your imports keep working when you move code into new folders.

When this approach works best

This approach works best when you:

  • Split code into multiple folders like api/, services/, models/, and utils/.
  • Run code from more than one entry point, such as a CLI script and a web server.
  • Collaborate with teammates and want imports that behave the same on every machine.

Avoid this approach when:

  • You have a single-file script. A full package layout will slow you down.

Prerequisites

  • Python installed
  • A code editor
  • Basic terminal familiarity
  • Comfort with files and folders

Step-by-step instructions

1) Put your code under one top-level package

Give your project a single import root by putting code under one package folder.

Example structure:

my_project/
  src/
    my_app/
      __init__.py
      main.py
      utils/
        __init__.py
        formatting.py
      services/
        __init__.py
        billing.py
  tests/
    test_billing.py

Minimal module:

# src/my_app/utils/formatting.py
defformat_money(amount_cents:int) ->str:
returnf"${amount_cents/100:.2f}"

What to look for

Each folder you want to import from includes an __init__.py. This tells Python it’s a package.


2) Prefer absolute imports inside the project

Absolute imports start from your package name. They stay readable and survive refactors better.

# src/my_app/services/billing.py
frommy_app.utils.formattingimportformat_money

definvoice_line(amount_cents:int) ->str:
returnf"Total:{format_money(amount_cents)}"

What to look for

Imports begin with my_app, not .. or filesystem paths.


3) Run your code from the project root using m

Many import errors happen because a file gets run directly from inside a package folder. Running as a module from the project root gives Python the package context it expects.

From the project root:

python-m my_app.main

Example main.py:

# src/my_app/main.py
frommy_app.services.billingimportinvoice_line

defmain() ->None:
print(invoice_line(1299))

if__name__=="__main__":
main()

What to look for

If python -m my_app.main works, your package structure and absolute imports line up.

If python src/my_app/main.py fails but python -m my_app.main works, the issue is how you’re running the code.


4) Import from sibling modules without changing your working directory

In large projects, you’ll often move between folders. Keep one habit consistent: run from the project root.

From the root, you can run other entry points the same way:

python-m my_app.services.billing

To make that runnable, add a small __main__ block:

# src/my_app/services/billing.py
frommy_app.utils.formattingimportformat_money

definvoice_line(amount_cents:int) ->str:
returnf"Total:{format_money(amount_cents)}"

if__name__=="__main__":
print(invoice_line(2500))

What to look for

If imports break only when you cd into subfolders, go back to the root and run with python -m ....


5) Use __init__.py for shorter imports

If you find yourself importing deep paths everywhere, you can re-export names from a package folder.

# src/my_app/utils/__init__.py
frommy_app.utils.formattingimportformat_money

__all__= ["format_money"]

Now you can write:

frommy_app.utilsimportformat_money

What to look for

Keep re-exports small. Too many can make it harder to trace where code lives.


Examples you can copy

Example 1: Import a helper from utils/

# src/my_app/utils/slug.py
defslugify(text:str) ->str:
returntext.strip().lower().replace(" ","-")
# src/my_app/main.py
frommy_app.utils.slugimportslugify

defmain() ->None:
print(slugify("Hello World"))

if__name__=="__main__":
main()

Run:

python-m my_app.main

Example 2: Split logic across modules

# src/my_app/services/math_tools.py
defadd(a:int,b:int) ->int:
returna+b
# src/my_app/main.py
frommy_app.services.math_toolsimportadd

print(add(2,3))

Run:

python-m my_app.main

Example 3: Tests that import your package

# tests/test_formatting.py
frommy_app.utils.formattingimportformat_money

deftest_format_money() ->None:
assertformat_money(250)=="$2.50"

Run from the project root:

python-m pytest

Common mistakes and how to fix them

Mistake 1: Running a file directly and breaking imports

You might run:

python src/my_app/main.py

Why it breaks

Python sets a different import root, so from my_app... may fail with ModuleNotFoundError.

Fix

Run it as a module from the project root:

python-m my_app.main

Mistake 2: Adding sys.path hacks to “make imports work”

You might write:

importsys
sys.path.append("..")

Why it breaks

It depends on the current working directory, so behavior changes across terminals, IDEs, and CI.

Fix

Use a real package layout and run from the root with python -m package.module.


Troubleshooting

If you see: ModuleNotFoundError: No module named 'my_app'

Confirm you’re running from the project root and using:

python-m my_app.main

Also confirm your package folder exists under src/my_app/ or your chosen root.


If you see: ImportError: attempted relative import with no known parent package

You ran a module directly. Run it with:

python-m package.module

from the project root.


If imports work in your IDE but fail in the terminal

Your IDE may add paths automatically. Run from the project root and use the same interpreter in your terminal and IDE.


If you see: cannot import name ... from partially initialized module ...

You likely have a circular import. Move shared code into a third module, or delay one import by moving it inside a function.


Quick recap

  • Put code under one top-level package folder, such as src/my_app/.
  • Use absolute imports starting with your package name.
  • Run from the project root with python -m package.module.
  • Avoid sys.path hacks that depend on where you run commands.
  • If you hit “partially initialized module,” check for circular imports.