Import executes module initialization, and a failed initialization can leave external side effects even when its module entry is removed.
Python import failures: module removal does not roll back every side effect
Operation contract
The program writes an owned temporary module that creates a marker and then raises. A child interpreter imports it through an explicit temporary path, catches the failure and checks sys.modules. The failed module entry is gone, but the marker remains. A retry would execute initialization again rather than resume the failed statement.
Failure and ownership boundary
Other modules imported during initialization can remain cached too. Import is not an application transaction. Keep irreversible startup work outside module top-level code, and register an accepted plugin only after its loader returns successfully. The executed source is written by this fixture; Python plugin project: load owned code and publish only an accepted interface is not a sandbox for received code. Python file project: stage a report before replacing the visible file supplies another boundary.
Working program
from pathlib import Path
import subprocess
import sys
import tempfile
with tempfile.TemporaryDirectory() as directory:
owned = Path(directory)
(owned / "aitrove_failed_import.py").write_text('from pathlib import Path\nPath(__file__).with_suffix(".marker").write_text("started")\nraise RuntimeError("initialization failed")\n', encoding="utf-8")
source = r'''import sys
from pathlib import Path
sys.path.insert(0, sys.argv[1])
try:
import aitrove_failed_import
except RuntimeError:
print("failed module retained:", "aitrove_failed_import" in sys.modules)
print("side effect retained:", (Path(sys.argv[1]) / "aitrove_failed_import.marker").exists())'''
result = subprocess.run([sys.executable, "-I", "-c", source, str(owned)], capture_output=True, text=True, check=True, timeout=5)
print(result.stdout.strip())Output
failed module retained: False
side effect retained: TrueCosts and limits
Initialization cost includes whatever the module executes, not just source lookup. This child has a fixed source and deadline. A timeout is not a general process-tree, memory or disk quota for arbitrary code.
Common Mistakes
- Failed imports can leave files or other initialized modules behind.
- Top-level initialization is not an atomic publication mechanism.
Connected lessons
Python modules: separate import-time definitions from program execution, Python plugin project: load owned code and publish only an accepted interface, Python subprocess: argument vectors, exit codes and bounded fixtures.
