From 6c66c03e82b2207847ff3facdf3f43a398361c8b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 18 Jun 2026 00:14:17 -0400 Subject: [PATCH] refactor(src): file_cache.py Phase 11.3.5 - extract _get_mtime_safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 11.3.5. The original try/except (OSError, ValueError): mtime = 0.0 in get_cached_tree is now extracted to a Result-returning helper. The helper returns Result[float]; the caller uses .data (0.0 fallback) and can inspect .errors. The convention requires Result[T] for try/except sites that can fail; the helper satisfies this requirement. Audit post-migration: - _get_mtime_safe L48 = INTERNAL_COMPLIANT (Heuristic A) ✓ - get_cached_tree L92 = no try/except for mtime (extracted) Tests: 24/24 pass (test_ast_parser, test_file_cache_no_top_level_tree_sitter). --- .../migrate_file_cache.py | 70 +++++++++++++++++++ src/file_cache.py | 31 ++++++-- 2 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 scripts/tier2/artifacts/result_migration_small_files_20260617/migrate_file_cache.py diff --git a/scripts/tier2/artifacts/result_migration_small_files_20260617/migrate_file_cache.py b/scripts/tier2/artifacts/result_migration_small_files_20260617/migrate_file_cache.py new file mode 100644 index 00000000..54863d02 --- /dev/null +++ b/scripts/tier2/artifacts/result_migration_small_files_20260617/migrate_file_cache.py @@ -0,0 +1,70 @@ +"""Migrate file_cache.py: extract Result-returning _get_mtime_safe helper.""" +from __future__ import annotations +from pathlib import Path + +p = Path("src/file_cache.py") +content = p.read_text(encoding="utf-8") + +# Add Result imports +old_imports = "from typing import Any, Dict, List, Optional, Tuple" +new_imports = ( + "from typing import Any, Dict, List, Optional, Tuple\n" + "\n" + "from src.result_types import ErrorInfo, ErrorKind, Result" +) +if old_imports not in content: + print("ERROR: imports not found") + raise SystemExit(1) +content = content.replace(old_imports, new_imports) + +# Replace the try/except in get_cached_tree with helper call +old_block = ( + " try:\n" + " p = Path(path)\n" + " mtime = p.stat().st_mtime if p.exists() else 0.0\n" + " except (OSError, ValueError):\n" + " mtime = 0.0" +) +new_block = ( + " mtime_result = _get_mtime_safe(path)\n" + " mtime = mtime_result.data # 0.0 on error (Result.errors has the details)" +) +if old_block not in content: + print("ERROR: mtime block not found") + raise SystemExit(1) +content = content.replace(old_block, new_block) + +# Add helper after _ast_cache definition, before class ASTParser +helper = ''' + +def _get_mtime_safe(path: Optional[str]) -> Result[float]: + """Get file mtime, returning Result[float] with errors on OSError/ValueError. + + The convention requires Result[T] for try/except sites that can fail. Used + by ASTParser.get_cached_tree to abstract the mtime computation; the caller + uses `.data` (0.0 fallback) and can inspect `.errors` if needed. + """ + if path is None: + return Result(data=0.0) + try: + p = Path(path) + mtime = p.stat().st_mtime if p.exists() else 0.0 + return Result(data=mtime) + except (OSError, ValueError) as e: + return Result(data=0.0, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=f"failed to get mtime for {path}: {e}", + source="file_cache._get_mtime_safe", + original=e, + )]) +''' + +old_class_marker = "\n\nclass ASTParser:" +new_class_marker = helper + "\n\nclass ASTParser:" +if old_class_marker not in content: + print("ERROR: class marker not found") + raise SystemExit(1) +content = content.replace(old_class_marker, new_class_marker) + +p.write_text(content, encoding="utf-8", newline="") +print("ok") \ No newline at end of file diff --git a/src/file_cache.py b/src/file_cache.py index 3de24c18..6240636f 100644 --- a/src/file_cache.py +++ b/src/file_cache.py @@ -40,9 +40,33 @@ import re from pathlib import Path from typing import Any, Dict, List, Optional, Tuple +from src.result_types import ErrorInfo, ErrorKind, Result + _ast_cache: Dict[str, Tuple[float, tree_sitter.Tree]] = {} +def _get_mtime_safe(path: Optional[str]) -> Result[float]: + """Get file mtime, returning Result[float] with errors on OSError/ValueError. + + The convention requires Result[T] for try/except sites that can fail. Used + by ASTParser.get_cached_tree to abstract the mtime computation; the caller + uses `.data` (0.0 fallback) and can inspect `.errors` if needed. + """ + if path is None: + return Result(data=0.0) + try: + p = Path(path) + mtime = p.stat().st_mtime if p.exists() else 0.0 + return Result(data=mtime) + except (OSError, ValueError) as e: + return Result(data=0.0, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=f"failed to get mtime for {path}: {e}", + source="file_cache._get_mtime_safe", + original=e, + )]) + + class ASTParser: """ Parser for extracting AST-based views of source code. @@ -78,11 +102,8 @@ class ASTParser: if not path: return self.parse(code) - try: - p = Path(path) - mtime = p.stat().st_mtime if p.exists() else 0.0 - except (OSError, ValueError): - mtime = 0.0 + mtime_result = _get_mtime_safe(path) + mtime = mtime_result.data # 0.0 on error (Result.errors has the details) if path in _ast_cache: cached_mtime, tree = _ast_cache[path]