sys.path and Module Search¶
Python searches for modules in a well-defined order stored in sys.path. Understanding this is crucial for managing imports in larger projects.
The Module Search Path¶
import sys
for path in sys.path:
print(path)
Example output:
/home/user/project # Script's directory
/usr/lib/python311.zip
/usr/lib/python3.11
/usr/lib/python3.11/lib-dynload
/home/user/.local/lib/python3.11/site-packages
/usr/lib/python3.11/site-packages
Search Order¶
When you import mymodule, Python searches in this order:
| Priority | Location | Description |
|---|---|---|
| 1 | Script directory | Directory containing the running script |
| 2 | PYTHONPATH |
Directories in this environment variable |
| 3 | Standard library | Built-in modules and standard lib |
| 4 | Site-packages | Installed third-party packages |
First match wins — Python stops searching after finding the module.
sys.path[0]: The Script Directory¶
The first entry is always the script's directory (or empty string for interactive):
# /home/user/project/main.py
import sys
print(sys.path[0]) # '/home/user/project'
This allows importing sibling modules:
project/
├── main.py
└── utils.py # Can be imported as: import utils
PYTHONPATH Environment Variable¶
Add custom directories to the search path:
# Linux/macOS
export PYTHONPATH="/home/user/mylibs:/home/user/shared"
# Windows
set PYTHONPATH=C:\mylibs;C:\shared
These directories are added after the script directory but before the standard library.
import sys
print(sys.path)
# ['', '/home/user/mylibs', '/home/user/shared', ...]
Modifying sys.path at Runtime¶
Append (Lower Priority)¶
import sys
sys.path.append('/path/to/my/modules')
Insert (Higher Priority)¶
import sys
sys.path.insert(0, '/priority/path')
Example¶
import sys
sys.path.insert(0, '/home/user/custom_libs')
import mymodule # Now searches /home/user/custom_libs first
PATH vs sys.path vs PYTHONPATH¶
| Variable | Purpose | Used By |
|---|---|---|
PATH |
Find executables (python, pip) |
Operating system shell |
sys.path |
Find Python modules | Python import system |
PYTHONPATH |
Add directories to sys.path |
Python (environment variable) |
import os
import sys
# PATH: for executables
print(os.environ.get('PATH'))
# sys.path: for modules
print(sys.path)
# PYTHONPATH: affects sys.path
print(os.environ.get('PYTHONPATH'))
Finding Where a Module Lives¶
Using file¶
import numpy
print(numpy.__file__)
# /usr/lib/python3.11/site-packages/numpy/__init__.py
Using inspect¶
import inspect
import os
print(inspect.getfile(os))
# /usr/lib/python3.11/os.py
Check if Already Imported¶
import sys
'numpy' in sys.modules # True if imported
sys.modules['numpy'] # The module object
Common Pitfalls¶
1. Name Shadowing¶
Problem: Local file shadows standard library.
project/
├── main.py
└── math.py # Shadows built-in math!
# main.py
import math # Imports YOUR math.py, not the standard library!
math.sqrt(16) # AttributeError!
Solution: Don't name files after standard library modules.
2. Importing from Wrong Directory¶
Problem: Multiple versions of a module exist.
import sys
sys.path.insert(0, '/old/version')
sys.path.insert(0, '/new/version')
import mymodule # Gets /new/version (first in path)
Solution: Use virtual environments to isolate dependencies.
3. Relative Path Issues¶
Problem: Script works from one directory but not another.
# Fragile
sys.path.append('../lib')
# Better: Use absolute paths
import os
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(script_dir, '..', 'lib'))
Built-in Modules¶
Some modules are built into the interpreter (no .py file):
import sys
print(sys.builtin_module_names)
# ('_abc', '_ast', '_codecs', 'builtins', 'sys', ...)
These are always available and searched before sys.path.
Site-Packages¶
Third-party packages installed via pip go to site-packages:
import site
print(site.getsitepackages())
# ['/usr/lib/python3.11/site-packages']
print(site.getusersitepackages())
# '/home/user/.local/lib/python3.11/site-packages'
Best Practices¶
| Practice | Reason |
|---|---|
| Use virtual environments | Isolate project dependencies |
Avoid modifying sys.path in code |
Makes code non-portable |
Use pip install -e . for local packages |
Proper package management |
| Don't shadow standard library names | Prevents confusing bugs |
| Use absolute imports | More explicit and reliable |
Debugging Import Issues¶
import sys
# Where is Python looking?
print('\n'.join(sys.path))
# Is module already loaded?
print('numpy' in sys.modules)
# Where did module come from?
import mymodule
print(mymodule.__file__)
# Verbose import debugging
python -v script.py # Shows all import steps
Key Takeaways¶
sys.pathis a list of directories Python searches for modules- First match wins — order matters
- Script's directory is always first in
sys.path PYTHONPATHadds directories to the search path- Avoid name collisions with standard library modules
- Use virtual environments instead of modifying
sys.path - Use
module.__file__to find where a module is located