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")) # TrueRemember
sys.modulescaches imported modules; module code runs oncesys.pathis 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.pyortest.pyand then wondering why imports behave strangely.