Private
Public Access
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)
This commit is contained in:
+31
-4
@@ -1284,11 +1284,12 @@ class App:
|
|||||||
if outline.startswith("ERROR") or outline.startswith("ACCESS DENIED"):
|
if outline.startswith("ERROR") or outline.startswith("ACCESS DENIED"):
|
||||||
return
|
return
|
||||||
pattern = re.compile(r'^\s*\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)', re.MULTILINE)
|
pattern = re.compile(r'^\s*\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)', re.MULTILINE)
|
||||||
try:
|
file_read_result = _populate_auto_slices_file_read_result(self, f_item)
|
||||||
with open(f_item.path, "r", encoding="utf-8") as f:
|
if not file_read_result.ok:
|
||||||
text = f.read()
|
if not hasattr(self, '_last_request_errors'): self._last_request_errors = []
|
||||||
except Exception:
|
self._last_request_errors.append(("_populate_auto_slices.file_read", file_read_result.errors[0]))
|
||||||
return
|
return
|
||||||
|
text = file_read_result.data
|
||||||
#TODO(Ed): Exception(Review)
|
#TODO(Ed): Exception(Review)
|
||||||
try:
|
try:
|
||||||
from src.fuzzy_anchor import FuzzyAnchor
|
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,
|
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: Phase 5 Event Handler Result Helpers
|
||||||
|
|
||||||
#endregion: MMA
|
#endregion: MMA
|
||||||
|
|||||||
@@ -839,3 +839,42 @@ def test_phase_5_l1284_populate_auto_slices_outline_result_failure():
|
|||||||
err = result.errors[0]
|
err = result.errors[0]
|
||||||
assert err.source == "gui_2._populate_auto_slices_outline_result"
|
assert err.source == "gui_2._populate_auto_slices_outline_result"
|
||||||
assert "outline fetch failed" in err.message
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user