Recovery drill: diagnose a failed environment
Independent Mastery Check — no AI assistance during assessed work. Disclosing any assistance honestly is required, but assisted work is remediation only: complete a fresh unaided check before claiming mastery.
Prerequisites
Day 02 activities. Bash is required for the assessed path; the verified execution scope is Linux Bash. A working `python3` is required; no installs and no machine-wide changes.
Learning Objective
Diagnose a deliberately broken virtual-environment or path assumption using prediction, minimal reproduction, and recovery notes.
Instructions
This drill ships an intentionally broken but harmless fixture inside a disposable env-drill directory. Predict the symptom before running, reproduce it, gather diagnostic evidence, apply the smallest justified correction, and recheck. Stay inside the fixture directory; change nothing machine-wide (no installs, no edits outside env-drill, no persistent environment changes — the fix below is scoped to a single command invocation). The blocks run in contained fail-stop subshells: setup validates a fresh private mktemp root and aborts before any write on failure, and it also aborts if a non-executing importlib.util.find_spec check finds ambient Python configuration already resolving helper (the intended failure needs that assumption; the probe does not import or run the module). Paste the printed export SBX_ROOT= line before the later blocks; record your shell's original PYTHONPATH setness and value - unset, empty, and nonempty originals are all valid evidence. The final block prints a literal rm -r -- <private-root> cleanup line for the exact root created. Run these blocks under Bash - the printed export/rm -r -- lines use Bash %q quoting and Linux Bash is the executed scope; zsh/dash/Windows are unexecuted.
Practice & Prediction Log
Build the broken fixture and reproduce the failure (paste the whole block; it aborts before writing if setup fails or if a non-executing check finds helper already resolvable):
(
set -e
SBX_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/d03env.XXXXXX")
[ -n "$SBX_ROOT" ]
[ -d "$SBX_ROOT" ]
[ -w "$SBX_ROOT" ]
[ -x "$SBX_ROOT" ]
probe_rc=0
python3 -c "import sys, importlib.util
try:
spec = importlib.util.find_spec('helper')
except Exception:
sys.exit(4)
sys.exit(3 if spec is not None else 0)" || probe_rc=$?
if [ "$probe_rc" -ne 0 ]; then
if [ "$probe_rc" -eq 3 ]; then
echo "abort: ambient Python config already resolves 'helper' - the intended failure cannot be demonstrated"
else
echo "abort: helper-resolvability probe failed (rc=$probe_rc) - cannot verify the precondition safely"
fi
exit 1
fi
mkdir "$SBX_ROOT/env-drill"
cd "$SBX_ROOT/env-drill"
mkdir tools
printf 'def greet():\n return "ok"\n' > tools/helper.py
printf 'from helper import greet\nprint(greet())\n' > app.py
rc=0
python3 app.py 2>err.txt || rc=$?
cat err.txt
grep -q "ModuleNotFoundError: No module named 'helper'" err.txt
echo "expected failure observed (rc=$rc)"
printf 'export SBX_ROOT=%q\n' "$SBX_ROOT"
)
Predict the error before running. Paste the printed export SBX_ROOT= line, then gather evidence:
(
set -e
: "${SBX_ROOT:?run the setup block first, then paste its printed export line}"
[ -d "$SBX_ROOT/env-drill" ]
cd "$SBX_ROOT/env-drill"
python3 -c "import sys, os; print([os.path.abspath(p or '.') for p in sys.path])"
ls tools
)
Apply the smallest justified correction for this invocation only and recheck, comparing PYTHONPATH against the recorded original — the block prints an explicit set/unset marker plus the value, so unset, empty, and nonempty originals are all distinguishable unchanged evidence):
(
set -e
: "${SBX_ROOT:?run the setup block first, then paste its printed export line}"
[ -d "$SBX_ROOT/env-drill" ]
cd "$SBX_ROOT/env-drill"
pp_state() { if [ "${PYTHONPATH+x}" = x ]; then printf 'PYTHONPATH %s: set value=<%s>\n' "$1" "$PYTHONPATH"; else printf 'PYTHONPATH %s: unset\n' "$1"; fi }
pp_state before
PYTHONPATH=tools python3 app.py
pp_state after
printf 'rm -r -- %q\n' "$SBX_ROOT"
)
Record symptom, evidence, the fix, why it is minimal, and the recheck result.
Python searches sys.path — the script's directory (for python3 app.py) or the current directory (for python3 -c). tools/ is on neither, which is why from helper import greet cannot resolve. PYTHONPATH=tools adds it for that one invocation; the pp_state lines print an explicit set/unset marker plus the value, so compare before/after to confirm nothing persisted. PYTHONPATH may be unset, empty, or already nonempty - unchanged proves no persistence in all three cases; it is an import-search variable, unrelated to executable PATH.
Observed (Linux, Python 3.14):
1. Symptom: ModuleNotFoundError: No module named 'helper' — env-drill is on sys.path, env-drill/tools is not.
2. Evidence: the resolved sys.path listing shows env-drill and the standard library — no env-drill/tools; ls tools showed helper.py exists — the file is present but unimportable from the script location.
3. Smallest justified fix: PYTHONPATH=tools python3 app.py → ok. It corrects the import path for this invocation only — no file edits, no persistent environment change (PYTHONPATH before/after prints the recorded original - unset, empty, or the same nonempty value; unchanged proves no persistence). Moving app.py or editing files would change more than needed. The fixture's intended failure also needs one ambient assumption, which the setup block checks and aborts on without running the module: helper must not already be resolvable — the importlib.util.find_spec probe never imports or executes it (an ambient PYTHONPATH or site-package could otherwise resolve it).
4. Recheck: prints ok; the fixture is unchanged otherwise.
Windows orientation only (not executed, and NOT the scoped equivalent): cmd set PYTHONPATH=tools && python app.py and PowerShell $env:PYTHONPATH='tools'; python app.py set the variable for the whole session - restoring it afterwards is required to claim no persistence; single-invocation scoping would need a different construct. Untested here.
Pass when, unaided: (a) the `ModuleNotFoundError` symptom was predicted before running; (b) the diagnostic evidence is recorded (the resolved `sys.path` listing lacking `tools/` and the `ls tools` result showing the file exists); (c) the applied fix is scoped to one invocation with a written justification of why it is minimal; (d) the successful recheck and the no-persistence check are recorded - `PYTHONPATH` after the scoped fix equals the recorded original (unset, empty, or unchanged nonempty). Assistance is remediation only and requires a fresh unaided check before mastery is claimed.
Authoritative Source Resources & Provenance
The Python Tutorial: Virtual Environments and Packages
Relevant Section / Range: Section 12 (creating venvs, pip installs, requirements files)
Verified Version & Date: Python 3.12 (Verified: 2026-10-30)
Primary reference for python -m venv, interpreter isolation, and requirements files used by the wrong-environment drills.
Basic Python syntax from Study Days 01-09
The drill needs no network: venv creation, sys.executable inspection, and ModuleNotFoundError reproduction all run offline.
Compared Candidate Sources & Selection Analysis (2)
Primary reference for pip commands, but the tutorial presents the create-activate-install loop as one beginner-friendly narrative.
Mature third-party environment tool with overlapping behavior; the stdlib venv tutorial was selected to avoid an extra dependency for first-time setup.
The Python Tutorial: Errors and Exceptions
Relevant Section / Range: Sections 8.3-8.6 (handling exceptions, else, finally, cleanup actions)
Verified Version & Date: Python 3.12 (Verified: 2026-10-30)
Primary reference for try/except/else/finally semantics and the with statement as predefined cleanup for Day 11.
Basic Python syntax from Study Days 01-09
Every clause behavior in the lesson is demonstrated by runnable local snippets.
Compared Candidate Sources & Selection Analysis (2)
Solid lecture treatment but paced for a semester course and requires video viewing; the tutorial section matches the exercise scope directly.
Reference-style exception hierarchy listing rather than the tutorial narrative; retained as lookup material for the recovery drills.