The mental model: where am I, where do I want to go?
Every time Python sees animport statement, it asks one question: in which directories should I look for this name? The answer lives in a list called sys.path. Python walks that list from top to bottom and imports the first match it finds.
sys.path are:
- The script’s directory — added automatically when you run
python script.py - The standard library — Python’s built-in modules (
os,json,datetime, …) site-packages— third-party packages installed viapiporuv
How virtual environments affect sys.path
When you activate a virtual environment, Python swaps out thesite-packages entry to point at the environment’s isolated directory instead of the global one. That is the entire mechanism — nothing more magical than a directory swap.
Always activate your virtual environment before running your project scripts. If you see
ModuleNotFoundError for a package you know you installed, a deactivated venv is the most likely culprit.Absolute vs relative imports
Within a package you can reference other modules in two ways. Always prefer absolute imports — they are unambiguous and work regardless of how you run the file.- Absolute imports (recommended)
- Relative imports (use with caution)
Absolute imports spell out the full path from the project root. They work whether you run the file directly, import it as a module, or run it with You can use absolute imports anywhere in your project, including in test files:
pytest.The PYTHONPATH environment variable
PYTHONPATH is a colon-separated (or semicolon-separated on Windows) list of directories that Python prepends to sys.path before adding the script directory. It is useful for quickly making your project root importable without installing the package.
- macOS / Linux
- Windows (PowerShell)
- .env file approach
Running scripts correctly with the -m flag
The single most reliable way to run a Python module inside a package is the -m flag. It tells Python to run the file as a module, not as a standalone script, which correctly sets up the package hierarchy.
weather_analysis.main) — no .py extension and no slashes.
Editable installs and the src layout
If you use thesrc/ layout (recommended), your package is not on sys.path by default because it lives inside the src/ wrapper. The cleanest fix is an editable install, which creates a special link so Python can always find your package — even as you edit the source files.
from weather_analysis.data_fetcher import fetch_weather works from anywhere — in scripts, tests, notebooks, and the Python REPL — without setting PYTHONPATH or using sys.path.append.
The __name__ == "__main__" guard
Every Python file has a built-in __name__ variable. When you run a file directly, Python sets __name__ to "__main__". When the same file is imported by another module, __name__ is set to the module’s dotted path instead.
Use this to write code that runs only when executed directly:
Diagnosing and fixing import errors
ModuleNotFoundError: No module named 'X'
ModuleNotFoundError: No module named 'X'
Python searched every directory in Common causes and fixes:
sys.path and found nothing named X.Diagnose it:FileNotFoundError: No such file or directory
FileNotFoundError: No such file or directory
This applies to data files (CSV, JSON, images), not to Python modules. Python looked for a file at the path you provided relative to the current working directory, not relative to the script file.Use
pathlib to build paths relative to the script file itself — this works no matter where you run from:ImportError: attempted relative import with no known parent package
ImportError: attempted relative import with no known parent package
You used a relative import (Or switch the relative import to an absolute one.
.module) but ran the file as a standalone script instead of as part of a package.Mixing up slashes (files) and dots (modules)
Mixing up slashes (files) and dots (modules)
File paths use
/ (or \ on Windows). Module paths use .. They are not interchangeable.Circular imports
Circular imports
Module A imports from module B, and module B imports from module A. Python partially initialises one of them and you get an Fix circular imports by extracting shared code into a third module (
ImportError or AttributeError.utils.py, models.py) that neither module_a nor module_b imports.Practical import patterns
Organizing Code
Learn how to split a large script into clean, reusable modules