fix(hunt): an empty product file no longer fails the source producer on every row of a change

The 150 unread rows were not a checkout failure: the entry-point scan read every product file and one empty
file raised empty_source, which escaped the cached property and failed the source producer on every row of
the change (and re-read the whole tree per row). Empty product files are skipped; the row's own file still
has to be non-empty.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-14 00:53:37 +02:00
co-authored by Claude Fable 5.1
parent c01e61c4dd
commit fcb43fdd47
2 changed files with 10 additions and 2 deletions
+6
View File
@@ -91,7 +91,13 @@ class Context:
@cached_property @cached_property
def entry_points(self) -> list[mapping.EntryPointRecord]: def entry_points(self) -> list[mapping.EntryPointRecord]:
for path in self.product_files: for path in self.product_files:
try:
self.read(path) self.read(path)
except _Absent:
# An empty product file elsewhere in the tree is not this row's problem. Before this guard one
# such file failed the source producer on every row of the change, and the failure escaped the
# cache so every row re-read the whole tree.
continue
return mapping.analyze_entry_points(self.root, [build_file_target(self.root, self.root / path) for path in self.product_files]) return mapping.analyze_entry_points(self.root, [build_file_target(self.root, self.root / path) for path in self.product_files])
@cached_property @cached_property
+3 -1
View File
@@ -181,7 +181,8 @@ def test_a_removed_guard_anchors_the_function_it_left(tmp_path):
check = " if not user.is_admin:\n raise PermissionError\n" check = " if not user.is_admin:\n raise PermissionError\n"
guarded = "def first():\n return 1\n\n\ndef admin(user):\n" + check + " return delete_everything()\n" guarded = "def first():\n return 1\n\n\ndef admin(user):\n" + check + " return delete_everything()\n"
(tmp_path / "app.py").write_text(guarded) (tmp_path / "app.py").write_text(guarded)
git("add", "app.py") (tmp_path / "empty.py").write_text("") # an empty product file elsewhere must not fail the row's producers
git("add", "app.py", "empty.py")
git("commit", "-qm", "fix: require admin") git("commit", "-qm", "fix: require admin")
base = git("rev-parse", "HEAD") base = git("rev-parse", "HEAD")
(tmp_path / "app.py").write_text(guarded.replace(check, "")) (tmp_path / "app.py").write_text(guarded.replace(check, ""))
@@ -191,6 +192,7 @@ def test_a_removed_guard_anchors_the_function_it_left(tmp_path):
change = {"id": "c", "case_id": "c-1", "repo": "fixture/repo", "base": base, "head": head, "kind": "ordinary", "fold": 1, change = {"id": "c", "case_id": "c-1", "repo": "fixture/repo", "base": base, "head": head, "kind": "ordinary", "fold": 1,
"files": [{"path": "app.py", "head_lines": [[1, 1], [5, 6]]}]} # fmt: skip "files": [{"path": "app.py", "head_lines": [[1, 1], [5, 6]]}]} # fmt: skip
rows = compute_rows(tmp_path, change) rows = compute_rows(tmp_path, change)
assert all(r["provenance.source.ok"] for r in rows), [r["provenance.source.reason"] for r in rows]
row = next(r for r in rows if r["feature.source.function"] == "admin") row = next(r for r in rows if r["feature.source.function"] == "admin")
assert row["feature.diff.hunks"] == [] and row["feature.diff.deletions"] == [5] assert row["feature.diff.hunks"] == [] and row["feature.diff.deletions"] == [5]
assert row["feature.diff.on_changed_line"] and row["feature.diff.distance"] == 0 assert row["feature.diff.on_changed_line"] and row["feature.diff.distance"] == 0