From f0c0de915c4ded4bedfdb89d428ec01213f15580 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 20 Jun 2026 00:25:33 -0400 Subject: [PATCH] TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 8: refactor(gui_2): migrate L897 _capture_workspace_profile to Result[T] (Phase 8) Migrate the imgui.save_ini_settings_to_memory try/except in App._capture_workspace_profile (L897) to the canonical Result[T] pattern: - Extract _capture_workspace_profile_ini_result(app) -> Result[str] helper into Phase 8 Property Setter / State Result Helpers region. - The legacy _capture_workspace_profile method calls the helper and drains errors to app._last_request_errors (per FR-BC-4 event-handler drain pattern; this is a property setter on the App). - The original fallback behavior (ini = '' on failure) is preserved so the legacy WorkspaceProfile still constructs with empty ini_content. Tests: - test_phase_8_l897_capture_workspace_profile_ini_result_success - test_phase_8_l897_capture_workspace_profile_ini_result_failure Audit: INTERNAL_BROAD_CATCH count in src/gui_2.py is now 0. All 22 INTERNAL_BROAD_CATCH sites originally in src/gui_2.py have been migrated to Result[T] across Phases 3-8. --- src/gui_2.py | 34 ++++++++++++++++++++++++++++++--- tests/test_gui_2_result.py | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/gui_2.py b/src/gui_2.py index 1ee0fa02..f1e641ac 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -891,9 +891,13 @@ class App: self._ini_capture_ready = True ini = "" else: - #Note(Ed): Thirdparty Exception - try: ini = str(imgui.save_ini_settings_to_memory() or "") - except Exception: ini = "" + ini_result = _capture_workspace_profile_ini_result(self) + if ini_result.ok: + ini = ini_result.data + else: + if not hasattr(self, "_last_request_errors"): self._last_request_errors = [] + self._last_request_errors.append(("_capture_workspace_profile", ini_result.errors[0])) + ini = "" panel_states = { "ui_separate_context_preview": getattr(self, "ui_separate_context_preview", False), "ui_separate_message_panel": getattr(self, "ui_separate_message_panel", False), @@ -8128,6 +8132,30 @@ def _diag_layout_state_ini_text_result(app: "App", ini_path: str) -> Result[str] original=e, )]) +def _capture_workspace_profile_ini_result(app: "App") -> Result[str]: + """Drain-aware variant of L897 _capture_workspace_profile ini-capture try block. + + Extracts the thirdparty imgui.save_ini_settings_to_memory try/except + from App._capture_workspace_profile into a Result-returning helper. + On exception, returns Result(data="", errors=[ErrorInfo]) so the legacy + wrapper can fall back to the empty-string ini content. On success, + returns Result(data=ini_str) where ini_str is the serialized INI content. + The legacy wrapper drains errors to app._last_request_errors (per FR-BC-4 + event-handler drain pattern; this site is a property setter on the App). + + [C: src/gui_2.py:App._capture_workspace_profile (L897 legacy wrapper)] + """ + try: + ini = str(imgui.save_ini_settings_to_memory() or "") + return Result(data=ini) + except Exception as e: + return Result(data="", errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=f"imgui.save_ini_settings_to_memory failed: {e}", + source="gui_2._capture_workspace_profile_ini_result", + original=e, + )]) + #endregion: Phase 8 Property Setter / State Result Helpers #endregion: MMA diff --git a/tests/test_gui_2_result.py b/tests/test_gui_2_result.py index 8df08c81..ef599f4c 100644 --- a/tests/test_gui_2_result.py +++ b/tests/test_gui_2_result.py @@ -1553,3 +1553,42 @@ def test_phase_8_l591_diag_layout_state_ini_text_result_failure(): assert "permission denied" in err.message assert result.data == "" + +def test_phase_8_l897_capture_workspace_profile_ini_result_success(): + """ + L897 _capture_workspace_profile_ini_result returns Result(data=ini_str) on success. + + The helper wraps the imgui.save_ini_settings_to_memory try/except in + App._capture_workspace_profile. On success, returns Result(data=ini_str) + where ini_str is the serialized INI content. The legacy wrapper drains + errors to app._last_request_errors. + """ + from src import gui_2 + from unittest.mock import MagicMock, patch + app = MagicMock() + with patch.object(gui_2.imgui, "save_ini_settings_to_memory", return_value="[Window][Provider]\nPos=10,20"): + result = gui_2._capture_workspace_profile_ini_result(app) + assert result.ok, f"Expected ok=True on success, got errors: {result.errors}" + assert result.data == "[Window][Provider]\nPos=10,20" + + +def test_phase_8_l897_capture_workspace_profile_ini_result_failure(): + """ + L897 _capture_workspace_profile_ini_result returns Result(data="", errors=[ErrorInfo]) on failure. + + When imgui.save_ini_settings_to_memory raises, the helper returns Result + with ErrorInfo describing the failure and data="" (matching the original + except branch's empty-string fallback). + """ + from src import gui_2 + from unittest.mock import MagicMock, patch + app = MagicMock() + with patch.object(gui_2.imgui, "save_ini_settings_to_memory", side_effect=RuntimeError("imgui backend down")): + result = gui_2._capture_workspace_profile_ini_result(app) + assert not result.ok, f"Expected ok=False on failure, got data: {result.data}" + assert result.errors, "Expected at least one error on failure" + err = result.errors[0] + assert err.source == "gui_2._capture_workspace_profile_ini_result" + assert "imgui backend down" in err.message + assert result.data == "" +