From ce289db99999f4bb42ccf40efce35dcecee46072 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 19 Jun 2026 23:33:04 -0400 Subject: [PATCH] TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 5: refactor(gui_2): migrate L1293 _populate_auto_slices file_read to Result[T] (Phase 5) Extract _populate_auto_slices_file_read_result helper from the file read try/except in App._populate_auto_slices. Legacy wrapper drains errors to app._last_request_errors per FR-BC-4 event-handler pattern. [pre-audit] L1293 INTERNAL_BROAD_CATCH [post-audit] V count: 13 -> 12 (L1293 removed) --- src/gui_2.py | 35 ++++++++++++++++++++++++++++++---- tests/test_gui_2_result.py | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/gui_2.py b/src/gui_2.py index 49fe482d..82a60961 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -1284,11 +1284,12 @@ class App: if outline.startswith("ERROR") or outline.startswith("ACCESS DENIED"): return pattern = re.compile(r'^\s*\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)', re.MULTILINE) - try: - with open(f_item.path, "r", encoding="utf-8") as f: - text = f.read() - except Exception: + file_read_result = _populate_auto_slices_file_read_result(self, f_item) + if not file_read_result.ok: + if not hasattr(self, '_last_request_errors'): self._last_request_errors = [] + self._last_request_errors.append(("_populate_auto_slices.file_read", file_read_result.errors[0])) return + text = file_read_result.data #TODO(Ed): Exception(Review) try: from src.fuzzy_anchor import FuzzyAnchor @@ -7758,6 +7759,32 @@ def _populate_auto_slices_outline_result(app: "App", f_item, abs_path: str) -> R original=e, )]) + +def _populate_auto_slices_file_read_result(app: "App", f_item) -> Result[str]: + """Drain-aware variant of L1293 _populate_auto_slices file read. + + Extracts the file read try/except from App._populate_auto_slices into a + Result-returning helper. On success, returns Result(data=content) where + content is the file text. On exception (file not found, encoding error, + permission denied), returns Result(data="", errors=[ErrorInfo]). + + The legacy wrapper drains errors to app._last_request_errors (per FR-BC-4 + event-handler drain pattern; data plane attribute). + + [C: src/gui_2.py:App._populate_auto_slices (L1293 legacy wrapper)] + """ + try: + with open(f_item.path, "r", encoding="utf-8") as f: + text = f.read() + return Result(data=text) + except Exception as e: + return Result(data="", errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=f"File read failed for {f_item.path}: {e}", + source="gui_2._populate_auto_slices_file_read_result", + original=e, + )]) + #endregion: Phase 5 Event Handler Result Helpers #endregion: MMA diff --git a/tests/test_gui_2_result.py b/tests/test_gui_2_result.py index e911121d..38aa8022 100644 --- a/tests/test_gui_2_result.py +++ b/tests/test_gui_2_result.py @@ -839,3 +839,42 @@ def test_phase_5_l1284_populate_auto_slices_outline_result_failure(): err = result.errors[0] assert err.source == "gui_2._populate_auto_slices_outline_result" assert "outline fetch failed" in err.message + + +def test_phase_5_l1293_populate_auto_slices_file_read_result_success(): + """ + L1293 _populate_auto_slices_file_read_result returns Result.ok=True on success. + + The helper wraps the file read try/except in App._populate_auto_slices. On + success, returns Result(data=content) where content is the file text. + """ + from src import gui_2 + from unittest.mock import MagicMock, patch, mock_open + app = MagicMock() + f_item = MagicMock() + f_item.path = "/proj/foo/src/bar.py" + with patch("builtins.open", mock_open(read_data="line1\nline2\nline3")): + result = gui_2._populate_auto_slices_file_read_result(app, f_item) + assert result.ok, f"Expected ok=True on success, got errors: {result.errors}" + assert result.data == "line1\nline2\nline3" + + +def test_phase_5_l1293_populate_auto_slices_file_read_result_failure(): + """ + L1293 _populate_auto_slices_file_read_result returns Result.ok=False with ErrorInfo on failure. + + When the file read raises (e.g., file not found, encoding error), the helper + converts the exception to ErrorInfo and returns Result(data="", errors=[ErrorInfo]). + """ + from src import gui_2 + from unittest.mock import MagicMock, patch + app = MagicMock() + f_item = MagicMock() + f_item.path = "/proj/foo/src/missing.py" + with patch("builtins.open", side_effect=FileNotFoundError("no such file")): + result = gui_2._populate_auto_slices_file_read_result(app, f_item) + 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._populate_auto_slices_file_read_result" + assert "no such file" in err.message