sys.path and sys.modules

sys · Practical · 11 min

Most 'ModuleNotFoundError' and 'wrong version imported' problems are explained by sys.path.

When you import foo, Python first looks in sys.modules, a dict cache of every module already imported. If foo is there, that object is returned immediately; that's why a module's top-level code runs only once.

Otherwise the import system searches the directories in sys.path, in order, and takes the first match. sys.path is built at startup from:

1. The directory of the script being run (or the current directory for -c, -m and the REPL). This is why a local file named random.py or json.py shadows the standard library module. 2. Entries from the PYTHONPATH environment variable. 3. The standard library directories. 4. site-packages of the active environment, where pip installs packages.

Debug import problems by printing sys.path and module.__file__, or run python -m site.

You can append to sys.path at runtime, but it's a fragile habit: the code then depends on its location. Installing your project in editable mode (pip install -e .) or running with python -m package.module from the project root is cleaner.

python -P or PYTHONSAFEPATH=1 (3.11+) skip adding the script's directory, avoiding accidental shadowing.

Deleting an entry from sys.modules forces a fresh import next time; importlib.reload(mod) re-executes a module in place, but existing references to old objects remain.

Example

import sys

print("json" in sys.modules)        # False
import json
print("json" in sys.modules)        # True
print(sys.modules["json"] is json)  # True

# a local file shadows the standard library module of the same name
open("calendar.py", "w").write("WHO = 'local file'\n")
sys.path.insert(0, ".")
import calendar
print(getattr(calendar, "WHO", "stdlib"))   # local file
print(calendar.__file__.endswith("calendar.py"))  # True

Remember

  • sys.modules caches imported modules; module code runs once
  • sys.path is searched in order: script dir, PYTHONPATH, stdlib, site-packages
  • A local file with a stdlib name shadows the real module

Watch out for

  • Naming your own script random.py, email.py or test.py and then wondering why imports behave strangely.

Check yourself

  1. Your project has a file named json.py next to main.py. What does import json in main.py load?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/sys.html#sys.path

sys.path and sys.modules · sys · Topolearn