From 0635cdd2ea1c0745f1da26f84b96ab3c999260c2 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Fri, 17 Jul 2026 10:09:25 +0100 Subject: [PATCH] Mark test-mode bypassed fits complete so they are resumable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _fit_bypass_test_mode returned without paths.completed(), unlike start_resume_fit — so a bypassed fit was never seen as complete and a rerun re-bypassed the whole pipeline instead of taking result_via_completed_fit. Found by the SLaM resume profiler (autolens_profiling#70), which carried a runtime stopgap; defeats the resume-representative purpose of PYAUTO_TEST_MODE_SAMPLES (#1378/#1381). Fixes #1387. Co-Authored-By: Claude Fable 5 --- autofit/non_linear/search/abstract_search.py | 5 ++ .../non_linear/search/test_abstract_search.py | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/autofit/non_linear/search/abstract_search.py b/autofit/non_linear/search/abstract_search.py index c3ff11cca..63ed2190a 100644 --- a/autofit/non_linear/search/abstract_search.py +++ b/autofit/non_linear/search/abstract_search.py @@ -916,6 +916,11 @@ def _fit_bypass_test_mode( model.unfreeze() + # Mark the fit complete, exactly as start_resume_fit does — a bypassed + # fit must be resumable (paths.is_complete -> result_via_completed_fit + # on the next run), or every rerun re-bypasses the whole pipeline. + self.paths.completed() + return result def _test_mode_samples_info(self) -> dict: diff --git a/test_autofit/non_linear/search/test_abstract_search.py b/test_autofit/non_linear/search/test_abstract_search.py index 183039e1e..d4259455c 100644 --- a/test_autofit/non_linear/search/test_abstract_search.py +++ b/test_autofit/non_linear/search/test_abstract_search.py @@ -373,3 +373,50 @@ def test_label_config(self): assert conf.instance["notation"]["label"]["label"]["two"] == "two_label" assert conf.instance["notation"]["label"]["label"]["three"] == "three_label" assert conf.instance["notation"]["label"]["label"]["four"] == "four_label" + + +class TestBypassWritesCompleted: + """ + A bypassed fit (PYAUTO_TEST_MODE=2/3) must mark itself complete, exactly + as start_resume_fit does — otherwise paths.is_complete stays False and a + rerun re-bypasses the whole pipeline instead of resuming from the + completed output (found by the SLaM resume profiler, autolens_profiling#70). + """ + + def test__bypass_marks_complete__second_fit_takes_completed_path( + self, monkeypatch + ): + monkeypatch.setenv("PYAUTO_TEST_MODE", "3") + + unique_tag = "bypass_completed_test" + + model = af.Model(af.m.MockClassx2) + analysis = af.m.MockAnalysis() + + search = af.DynestyStatic(name="bypass_completed", unique_tag=unique_tag) + search.fit(model=model, analysis=analysis) + + # Under the test config's `remove_files: true` only the zip remains + # after fit() — the marker must have been zipped up with the output. + import zipfile + from pathlib import Path + + with zipfile.ZipFile(search.paths._zip_path) as f: + assert ".completed" in {Path(n).name for n in f.namelist()} + + search_resumed = af.DynestyStatic( + name="bypass_completed", unique_tag=unique_tag + ) + + def _poison(*args, **kwargs): + raise AssertionError( + "start_resume_fit taken — the .completed marker is missing" + ) + + monkeypatch.setattr(search_resumed, "start_resume_fit", _poison) + + result = search_resumed.fit( + model=af.Model(af.m.MockClassx2), analysis=af.m.MockAnalysis() + ) + + assert result is not None