-
Notifications
You must be signed in to change notification settings - Fork 76
CI blind spot: interactive ipywidgets callbacks aren't exercised (slider-state bugs slip through) #441
Description
Problem
CI runs notebooks via jupyter nbconvert --execute — each cell once, top-to-bottom, with no widget interaction. So bugs that only appear when a user interacts with an ipywidgets control (moves a slider), or that depend on shared global state mutated by later cells, are invisible to CI.
Concrete example (just fixed in 60_linear_algebra_2/200)
The Power Method arrow-slider callback show_iterate(step) read the global n. Cells 49/52/58 (lam, vecX, n = power_method(...)) overwrite n → 99999 for the non-converging "What if" matrices. After running those cells, moving the slider raised:
ValueError: shape mismatch ... (99999,) vs (2,) # ax.bar(range(99999), vec[2])
CI passed cleanly because:
- the
interactcallback only fires once, at creation, whennwas still correct; - nbconvert never moves the slider, and never re-fires the callback after
nis clobbered; - the
if os.getenv('CI'): show_iterate(last)guard renders one static frame — by design, to keep CI green — which also means the interactive path is never exercised.
(Fixed by capturing the dimension locally: dim = vec_array.shape[1], independent of the mutable global n.)
Options
- Convention (cheapest): widget callbacks must capture all needed state locally in their own cell, never reading mutable globals that later cells reassign. Make it a review/lint checklist item.
- Exercise callbacks in a test: for notebooks containing
interact(...), after executing all cells, call the callback across its full slider range and assert no exception. Non-trivial to wire generically. - Static check: flag a widget callback that references a module-level name reassigned elsewhere in the same notebook. Hard to do reliably (needs data-flow analysis).
Related
Same family as #440 and the "missing Colab clone cell" gap — CI executes the repo, non-interactive path, not the interactive/Colab path.
🤖 Filed via Claude Code after fixing the 200 slider crash.