Python ModuleNotFoundError and circular ImportError — Fix Imports
Diagnose Python import failures by checking sys.executable and sys.path, finding shadowing modules and circular imports, then installing into the active environment.
Symptoms
- ModuleNotFoundError: No module named 'requests'
- ImportError: cannot import name 'UserService' from partially initialized module
- The package works in one terminal but not in a script, cron job, or container
- The traceback points to a file with the same name as the module being imported
Possible causes
- The dependency was installed into a different interpreter or virtual environment
- A local file such as requests.py or json.py shadows the installed package
- Two modules import each other at module scope, so one is only partially initialized
- The project root or src directory is missing from sys.path when run as a script
Diagnosis steps
Confirm which interpreter is running
python -c "import sys; print(sys.executable); print(*sys.path, sep='\n')"Interpretation
Compare the executable with the environment where you installed packages. A system Python next to a project venv explains most ModuleNotFoundError reports.
Check where the module resolves, if at all
python -c "import importlib.util; print(importlib.util.find_spec('requests'))"Interpretation
None means the module is not importable from this interpreter. A path inside your project means a local file is shadowing the installed package.
Verify the installed distribution
python -m pip show requests && python -m pip list | grep -i requestsInterpretation
pip show reports the location it installed into. If that path is not in sys.path, the install belongs to another environment.
Time the import chain for circular failures
python -X importtime -c "import app" 2>&1 | tail -25Interpretation
A partially initialized module in the traceback confirms a cycle. The import time log shows which module was still loading when the other tried to use it.
Root cause
Python resolves imports through sys.path and executes a module only once. A missing package means no path entry contains it for this interpreter, often because pip installed into a different environment. A circular import fails because module A starts executing, imports B, and B tries to read a name from A before A has defined it, leaving A partially initialized.
Fix
Activate the project environment and install with python -m pip so the package lands in the running interpreter. Remove or rename local files that shadow standard or third-party modules. Break import cycles by moving shared types to a separate module, importing inside the function that needs it, or using TYPE_CHECKING for type-only imports. Run modules with python -m package.module so package-relative imports resolve.
Risk note
Adding broad paths to sys.path or PYTHONPATH can mask a broken package layout and import the wrong code. Importing inside functions hides cycles but can move the failure to runtime. Installing globally with sudo risks breaking system tools that depend on the distribution's packages.
Verify the fix
Run the failing entry point from a clean shell with the same interpreter and confirm the import succeeds. Execute the test suite and check no partially initialized module warnings appear, then repeat inside the container or cron environment where the failure started.
Two different import failures
ModuleNotFoundError means the module was never found on sys.path. A circular import looks different:
ImportError: cannot import name 'UserService' from partially initialized module
'app.services' (most likely due to a circular import)
The module exists; it simply had not finished executing when another module asked for a name from it.
Check the interpreter first
python -c "import sys; print(sys.executable); print(*sys.path, sep='\n')"
python -c "import importlib.util; print(importlib.util.find_spec('requests'))"
python -m pip show requests
If find_spec returns None, the package is absent for this interpreter. If it returns a path inside your repository, a local requests.py is shadowing the real package. If pip shows a site-packages directory not present in sys.path, you installed into a different environment.
Fix the real cause
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python -m app.main
For a cycle, move shared definitions to a third module, or defer the import:
def create_user(payload):
from app.services import UserService
return UserService.create(payload)
Prefer python -m package.module over running the file path directly, because it sets up package context correctly.
Verify
Re-run the failing entry point from a clean shell, then in the container or cron environment. python -m pytest should collect and pass without import warnings. If the fix only works in your IDE, the environment still differs: compare sys.executable there before declaring success.