@@ -358,7 +358,7 @@ def is_clean_skip_exit(output: str) -> bool:
358358 return bool (tail ) and _SKIP_EXIT_RE .match (tail [- 1 ]) is not None
359359
360360
361- def regenerate_notebook (nb_path , scripts_dir ) -> Path :
361+ def regenerate_notebook (nb_path , scripts_dir , rel = None ) -> Path :
362362 """
363363 Regenerate one notebook from its source ``.py`` into a temp dir.
364364
@@ -376,8 +376,13 @@ def regenerate_notebook(nb_path, scripts_dir) -> Path:
376376 The notebook that failed, e.g. ``notebooks/imaging/model_fit.ipynb``.
377377 scripts_dir
378378 The directory holding the source scripts, e.g. ``<workspace>/scripts``.
379- The source is looked up at the notebook's path relative to its own
380- ``notebooks/`` root, with a ``.py`` suffix.
379+ rel
380+ The notebook's path RELATIVE to its own ``notebooks/`` root, e.g.
381+ ``imaging/model_fit.ipynb``. The source script is that same relative
382+ path under ``scripts_dir`` with a ``.py`` suffix. Omitting it falls back
383+ to the bare filename, which is only correct for a notebook sitting at
384+ the root — every workspace notebook lives in a subdirectory, so callers
385+ iterating a tree must pass this.
381386
382387 Returns
383388 -------
@@ -390,7 +395,8 @@ def regenerate_notebook(nb_path, scripts_dir) -> Path:
390395 """
391396 nb_path = Path (nb_path )
392397 scripts_dir = Path (scripts_dir )
393- script_path = scripts_dir / Path (nb_path .name ).with_suffix (".py" )
398+ rel = Path (rel ) if rel is not None else Path (nb_path .name )
399+ script_path = scripts_dir / rel .with_suffix (".py" )
394400 if not script_path .exists ():
395401 raise FileNotFoundError (f"No source script at { script_path } " )
396402
@@ -553,7 +559,7 @@ def _classify_notebook_run(run_target, recorded, report, env, timeout_secs):
553559
554560
555561def execute_notebook (f , report = None , env = None , write_back = True ,
556- retry_from_scripts = None , report_as = None ):
562+ retry_from_scripts = None , report_as = None , notebook_rel = None ):
557563 """
558564 Execute one notebook as a subprocess, with the kernel cwd at the repo root.
559565
@@ -594,7 +600,7 @@ def execute_notebook(f, report=None, env=None, write_back=True,
594600
595601 print (" notebook failed; regenerating from source script and retrying..." )
596602 try :
597- regenerated = regenerate_notebook (f , retry_from_scripts )
603+ regenerated = regenerate_notebook (f , retry_from_scripts , rel = notebook_rel )
598604 except Exception as exc :
599605 # No source script, or generation itself failed. The first attempt's
600606 # FAIL stands — the recovery was unavailable, not the notebook fixed.
@@ -690,12 +696,20 @@ def execute_notebooks_in_folder(
690696 else :
691697 from env_config import build_env_for_script
692698 env = build_env_for_script (file , env_config )
699+ # The path relative to the notebooks root is what maps a notebook
700+ # to its source script; the bare filename would collide across
701+ # subdirectories and miss the source entirely.
702+ try :
703+ notebook_rel = file .relative_to (Path .cwd () / directory )
704+ except ValueError : # pragma: no cover - file outside the root
705+ notebook_rel = Path (file .name )
693706 execute_notebook (
694707 file ,
695708 report = report ,
696709 env = env ,
697710 write_back = write_back ,
698711 retry_from_scripts = retry_from_scripts ,
712+ notebook_rel = notebook_rel ,
699713 )
700714
701715
0 commit comments