From 30ad197d3ff434d44348b598cf53508eb21b9aa7 Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Wed, 14 Oct 2026 08:42:25 +0200 Subject: [PATCH] fix(bench): an unparsable file in one ordinary commit no longer ends a repository's history walk The combined access-control freeze stopped at its 31st repository: one ordinary commit of a PHP tree touched a Python file that does not parse, functions() raised Rejected, and ordinary_history let it end the walk. The file is now marked parse_failed on its row and such a commit is rejected as an ordinary push (reason function_parse), which keeps the draw conservative without losing the repository. Co-Authored-By: Claude Fable 5.1 --- benchmarks/unseen/extract.py | 11 ++++++++++- tests/test_unseen_extract.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/benchmarks/unseen/extract.py b/benchmarks/unseen/extract.py index ada87c9..94d4be2 100644 --- a/benchmarks/unseen/extract.py +++ b/benchmarks/unseen/extract.py @@ -417,6 +417,8 @@ def ordinary_rejection(row: dict, fixes: set[str], known_functions: set[tuple[st return "no_source" if all(f["generated"] or generated_path(f["path"]) for f in source): return "generated" + if any(f.get("parse_failed") for f in source): + return "function_parse" if any((f["path"], name) in known_functions for f in files for name in f["functions"]): return "vulnerable_function" if any((f["path"], "") in known_functions for f in files): @@ -521,6 +523,7 @@ def ordinary_history(git, head: str, *, cache_dir: Path | None = None, limit: in "head_lines": [[max(1, n), max(1, n + max(c, 1) - 1)] for _, _, n, c in hunks], "functions": [], "generated": False, + "parse_failed": False, } if Path(path).suffix in LANGUAGES: # Inspect both sides: deleting a vulnerable function is still touching it. @@ -528,7 +531,13 @@ def ordinary_history(git, head: str, *, cache_dir: Path | None = None, limit: in text = git.blob(revision, path) if text is None: continue - spans = functions(path, text) + try: + spans = functions(path, text) + except Rejected: + # One unparsable file in one ordinary commit (a Python 2 script in a PHP tree, say) + # marks that commit; it must not end the repository's walk. + info["parse_failed"] = True + continue for hunk in hunks: start, count = hunk[side : side + 2] for name, lo, hi in spans: diff --git a/tests/test_unseen_extract.py b/tests/test_unseen_extract.py index 4537576..f53d073 100644 --- a/tests/test_unseen_extract.py +++ b/tests/test_unseen_extract.py @@ -683,3 +683,14 @@ def test_history_limit_walks_only_the_newest_commits(history): assert e.ordinary_history(git, "HEAD", limit=2) == full[:2] # the newest first-parent commits, same rows with pytest.raises(ValueError, match="positive integer"): e.ordinary_history(git, "HEAD", limit=0) + + +def test_unparsable_source_in_one_ordinary_commit_rejects_that_commit_only(history): + """A syntax error in one file of one ordinary commit used to end the whole repository's history walk.""" + git, commit, candidate, _, _, _, _ = history + broken = commit("def handler(value):\n return value\n", "ordinary", extra={"broken.py": "def (\n"}) + rows = e.ordinary_history(git, "HEAD") + row = next(r for r in rows if r["head"] == broken) + assert [f["path"] for f in row["files"] if f["parse_failed"]] == ["broken.py"] + assert e.ordinary_rejection(row, set(), set()) == "function_parse" + assert e.introducing(git, candidate) is not None