fix: run notebooks with the kernel cwd pinned to the workspace root - #205
Merged
Conversation
`jupyter nbconvert --execute` starts the kernel in the notebook's own
directory, not the directory nbconvert was launched from. The workspaces
document the opposite ("scripts are run from the repository root so relative
paths to dataset/ and output/ resolve") and their auto-simulate guards shell
out to root-relative simulator paths, so every auto-simulating notebook failed
with exit 2 ("can't open file") — the largest contributor to the red
run_notebooks shards.
nbconvert exposes no CLI flag for the kernel's working directory; the only
knob is resources['metadata']['path'], which nbclient turns into the kernel
cwd, and that is reachable only from the Python API. So execute_notebook now
subprocesses a new autohands/run_notebook.py which sets it. The subprocess
boundary is kept deliberately: process isolation, BUILD_SCRIPT_TIMEOUT and the
per-notebook environment are all unchanged — only the kernel's cwd moves.
run_notebook.py leaves CellExecutionError uncaught on purpose. Python's own
traceback then reproduces the exact shape build_util.is_clean_skip_exit parses
(a CellExecutionError marker whose last line is `SystemExit: 0`) to tell an
intentional optional-dependency skip from a real failure. Verified:
sys.exit(0) -> True, sys.exit(1) -> False, ValueError -> False.
Two regression tests lock in the kernel cwd and the root-relative-subprocess
case. Full suite green (236).
Closes #204
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Notebooks execute with the kernel's working directory pinned to the workspace root, so root-relative auto-simulate guards resolve.
Closes #204.
The bug
jupyter nbconvert --executestarts the kernel in the notebook's own directory, not the directory nbconvert was launched from. The workspaces document the opposite (autolens_workspace/AGENTS.md: "Scripts are run from the repository root so relative paths todataset/andoutput/resolve correctly"), and the auto-simulate guards depend on it:That path resolves from a script run at the root and cannot resolve from a notebook run in
notebooks/<topic>/— exit 2, "can't open file". It is the single largest contributor to the redrun_notebooksshards. The.pyscripts were always fine, which is whyrun_scriptsmostly passed.Why this shape
Both options in the original issue turned out to be unimplementable as written, so I checked each before building:
cwd=kwarg. The launcher's cwd is already the root; nbconvert overrides the kernel's cwd regardless and exposes no CLI flag for it. The only knob isresources['metadata']['path'], which nbclient turns into the kernelcwd(nbclient/client.py:535) — Python API only.__file__" cannot work:__file__is not defined in a notebook kernel (verified). It exists in the.pyscripts, which already work, and is missing in exactly the broken context.So
execute_notebooknow subprocesses a newautohands/run_notebook.pythat sets the resource path. The subprocess boundary is kept deliberately — process isolation,BUILD_SCRIPT_TIMEOUTand the per-notebook environment are all unchanged. Only the kernel's cwd moves. No tutorial scripts are touched.The load-bearing constraint
build_util.is_clean_skip_exitparses the run's combined stdout/stderr for aCellExecutionErrormarker whose last line isSystemExit: 0, to tell an intentional optional-dependency skip from a genuine failure.run_notebook.pytherefore leavesCellExecutionErroruncaught, so Python's own traceback reproduces that exact shape. A handler that reformatted the message would silently break skip detection — noted in the module docstring.Verified against the classifier:
Verification
End-to-end on a real workspace notebook (
autogalaxy_workspace/notebooks/guides/plot/examples/mat_plot.ipynb), whose guard shells out to a root-relative simulator:Two regression tests added (
tests/test_run_notebook_cwd.py) covering the kernel cwd and the root-relative-subprocess case. Full suite green: 236 passed.🤖 Generated with Claude Code