Free lesson · Python Basic to Advance
Module Search Path
Module Search Path
What the Search Path Is
When Python encounters import module, it searches a sequence of directories for the module's source file. This sequence is the module search path, accessible at runtime as sys.path. Understanding it is essential for debugging import errors, configuring projects, and writing scripts that find their dependencies.
The search path is an ordered list of strings, each representing a directory. Python checks them in order - the first match wins. If no directory contains the module, ModuleNotFoundError is raised.
import sys
for directory in sys.path:
print(directory)
# (typical output, varies by system)
# '' (script's own directory)
# /usr/lib/python3.13
# /usr/lib/python3.13/lib-dynload
# /home/user/.local/lib/python3.13/site-packages
# /usr/local/lib/python3.13/site-packages
This code prints the module search path. The first entry - empty string - represents the script's own directory. Following entries are the Python standard library directories and the site packages directories where third-party packages live. The order is significant: earlier entries are checked first.
The exact contents vary by Python installation, virtual environment, and operating system. The first entry is usually significant - the directory containing the running script (or '' for the current directory in interactive mode).
How sys.path Is Built
Python builds sys.path at startup from several sources:
- The script's directory (or current directory for interactive use)
PYTHONPATHenvironment variable entries- Installation-dependent default paths (standard library, site-packages)
The order: script directory first, then PYTHONPATH, then installation defaults.
import sys
# sys.path is the canonical, fully-resolved search path
This code accesses sys.path. The list is populated when Python starts. Modifying sys.path at runtime affects subsequent imports - useful for development but risky in production.
The first entry being the script's directory is what makes "import a sibling file" work without any setup: a script at /project/main.py can import helpers to load /project/helpers.py.
The PYTHONPATH Environment Variable
PYTHONPATH is a colon-separated list of additional directories to add to sys.path before the installation defaults:
# Linux/Mac
export PYTHONPATH="/path/to/project:/path/to/other"
python3 script.py
# Windows (cmd)
set PYTHONPATH=C:\path\to\project;C:\path\to\other
python script.py
Effect on sys.path inside the script:
['', '/path/to/project', '/path/to/other', ...installation defaults...]
This code shows the PYTHONPATH environment variable. It adds directories to the import path before the script runs. Useful for development to point Python at local source trees without installing them.
PYTHONPATH is most useful for development. For production deployments, the better pattern is to install packages properly (with pip or a similar tool) so they appear in site-packages.
The site-packages Directory
site-packages is the standard location for third-party packages installed with pip or other package managers. Its location depends on the Python installation:
import site
# All site-packages directories
print(site.getsitepackages())
# ['/usr/lib/python3.13/site-packages', ...]
# User-specific site-packages (pip install --user)
print(site.getusersitepackages())
# /home/user/.local/lib/python3.13/site-packages
This code uses the site module to find site-packages locations. The system locations contain packages installed globally; the user location contains packages installed with pip install dash dash user. Virtual environments have their own site-packages.
For most projects, virtual environments (Topic 11.4 below) provide their own isolated site-packages, separate from system-wide installations.
Virtual Environments
A virtual environment is an isolated Python installation with its own site-packages. Created with venv:
# Create
python3 -m venv .venv
# Activate (Linux/Mac)
source .venv/bin/activate
# Activate (Windows)
.venv\Scripts\activate
# Install packages - they go into the venv's site-packages
pip install requests
Inside the activated venv, sys.path includes the venv's site-packages first:
['', '/path/to/project/.venv/lib/python3.13/site-packages', ...]
This code shows creating and using a virtual environment. The venv has its own site packages, isolated from the system Python. Packages installed inside the venv do not affect other Python projects. This is the standard tool for managing per-project dependencies.
Virtual environments are the standard practice for managing Python dependencies. Each project has its own environment with its own dependency versions, avoiding conflicts.
Module 11 (this module) covers imports generally; tool-specific details like pip, poetry, and uv for managing virtual environments are project-management concerns covered in Module 11.5 and related project-setup material.
Modifying sys.path at Runtime
sys.path can be modified at runtime to add directories:
import sys
sys.path.insert(0, "/path/to/extra/modules")
# Now imports can find modules in that directory
import my_extra_module
This code adds a directory to sys.path at runtime. Inserting at index zero makes it the highest priority. After the modification, imports check that directory first. Useful for development but generally avoided in production.
Runtime path modification is a common technique for development scripts, but it has costs:
- Implicit dependencies. The path manipulation is not visible to packaging tools.
- Ordering surprises. If multiple modules with the same name exist on the path, the order determines which is loaded.
- Tool blindness. Linters and IDEs may not understand the dynamic path.
For production code, prefer proper package installation or a pyproject.toml-managed setup.
Finding Where a Module Came From
After importing a module, its __file__ attribute shows the source:
import json
print(json.__file__)
# /usr/lib/python3.13/json/__init__.py
import requests # third-party
print(requests.__file__)
# /path/to/venv/lib/python3.13/site-packages/requests/__init__.py
This code shows where each module was loaded from. The file attribute reveals the actual source location. Useful for debugging "wrong version" issues - if a module behaves unexpectedly, checking its file confirms which file is being loaded.
Some modules (sys, builtins, and other built-in modules) lack __file__ because they're compiled into the interpreter. These print as <module 'sys' (built-in)>.
Namespace Packages
A directory without __init__.py can still be a package in Python 3 - a namespace package. This is an advanced feature mostly relevant for splitting a logical package across multiple physical directories:
# Layout that creates a namespace package
project_root/
├── module_a/
│ └── shared_namespace/
│ └── part1.py
└── module_b/
└── shared_namespace/
└── part2.py
With both module_a and module_b on sys.path, shared_namespace is a single virtual package combining part1 and part2. Topic 11.6 covers packages in detail.
For most code, regular packages (with __init__.py) are simpler and recommended.
The Module Cache: sys.modules
Once loaded, modules are cached in sys.modules:
import sys
import math
# math is in the cache
print("math" in sys.modules)
# True
# Re-importing reuses the cached version
import math # very fast - just a dict lookup
This code shows the module cache. After the first import, math is in sys.modules. Subsequent imports return the cached module without re-executing the module's code. The cache is what makes repeated imports cheap.
The cache means that two imports of the same module produce the same module object. Mutating module attributes from one place is visible everywhere.
To force a fresh import, the entry must be removed from sys.modules first - or importlib.reload must be used (Topic 11.9).
Frozen Modules
Some standard library modules are frozen - embedded into the Python executable for fast startup. These have no __file__:
import sys
print(sys.__file__)
# AttributeError: module 'sys' has no attribute '__file__'
# But sys is loaded:
print(sys)
# <module 'sys' (built-in)>
This code attempts to access the file attribute of sys. Sys is built into the interpreter, so it has no source file. Frozen modules speed up Python startup by avoiding file I/O for core modules.
Frozen modules are an optimization. The set of frozen modules expanded significantly in recent Python versions to speed up startup.
Debugging Import Failures
When an import fails with ModuleNotFoundError, check these in order:
# 1. What's on sys.path?
import sys
for p in sys.path:
print(p)
# 2. Does the file actually exist at any of those paths?
import os
for p in sys.path:
target = os.path.join(p, "expected_module.py")
if os.path.exists(target):
print(f"Found at: {target}")
# 3. Is there a typo in the import name?
# (check the actual filename and the import statement)
# 4. Is the package installed in the active environment?
# pip list
This code shows debugging steps for module not found errors. Check sys.path, check whether the file exists at any path entry, check the spelling, and check whether the package is installed. These four checks resolve the majority of import errors.
The "wrong virtual environment" case is especially common: a package is installed in one venv but the script runs with a different Python, which doesn't see it.
Performance
sys.path lookup is O(n) in the number of entries - Python checks each directory until it finds the module. For most installations, the path has 5-15 entries, so the cost is small.
The first import of a module is much more expensive than subsequent ones. The cache lookup is O(1).
For startup-sensitive applications, minimizing imports at module load time is the main optimization. Lazy imports (Topic 11.1) defer cost until needed.
Interview Relevance
Interview: The search path is essential debugging knowledge:
- Knowing that
sys.pathis the canonical search path- That the first entry is the script's directory (or current dir interactively)
- The role of
PYTHONPATHenvironment variable- That virtual environments isolate dependencies
- Using
module.__file__to debug "which version is loaded"- The
sys.modulescache and its consequencesA common interview moment: a candidate gets
ModuleNotFoundError. The right debugging steps involve checkingsys.pathand the actual file location. Knowing this process signals operational experience.Another moment: a candidate doesn't understand virtual environments. The role of isolated
site-packagesis fundamental to modern Python development.Module 30 covers Python interview preparation.
Common Pitfalls
Warning: Wrong virtual environment. Installing a package in one venv and running a script with a different Python (system Python, another venv) is the most common cause of import errors.
Shadowing a stdlib module. Naming a local file
json.pyoros.pyshadows the standard library. Imports of those names load the local file, not the stdlib. Often produces confusing errors.Modifying
sys.pathat runtime. Works but couples code to a specific environment layout. Prefer proper installation.Forgetting to activate the venv. Running
python script.pywithout activating the venv uses the system Python. Always checkwhich python(orwhere python) to confirm.Confusing
PYTHONPATHwithPATH.PATHis for executables;PYTHONPATHis for Python module locations. They are separate environment variables.
site-packagesfor development. Installing development code into site-packages is fine, butpip install --editable .(editable install) is usually better - it lets edits be picked up without reinstalling.Caching across reloads.
sys.modulesretains the old module after source changes. Useimportlib.reload(Topic 11.9) or restart the interpreter.
Try It
Exercise: Practice with the search path:
sys.pathand identify each entry's purpose (script dir, stdlib, site-packages, etc.).- Create a Python file
myhelper.pyin the same directory as a script. Verify the script canimport myhelperwithout any path setup.- Move
myhelper.pyto a different directory. Verify the import now fails. Usesys.path.insert(0, ...)to make it work.- Create a virtual environment with
python3 -m venv .venv. Activate it and install a small package (e.g.,requests). Verify it's only available in the activated venv.- Inspect the
__file__attribute ofos,json, and any installed third-party package. Confirm each comes from a different location.- Try
import sys; del sys.modules["math"]; import math. Observe that the second import re-executes the module setup.