Repository navigation
python_test_order_check: an include that binds the interpreter comes after find_package(Python3), offsets from one text (refs #1979; lands #2012 + #2072) - #2123
Conversation
…ome after find_package(Python3) (refs #1979) #1981 moves the `add_python_test()` rows into `cmake/tests-python-tools.cmake`, included from `CMakeLists.txt`. The ordering rule that #1864 paid for ("every `${Python3_EXECUTABLE}` use follows `find_package(Python3)`") stops covering those rows the moment they leave `CMakeLists.txt`, because this check globs `CMakeLists.txt` and nothing else. Recursing into `cmake/*.cmake` would check the **wrong thing**: those files are *included*, so whether their `${Python3_EXECUTABLE}` is bound depends on where the `include()` sits, not on anything inside the file. The rule for them is the include's position: > every `include(<path>.cmake)` in a `CMakeLists.txt` must come **after** the first `find_package(Python3` when the included > file mentions `${Python3_EXECUTABLE}` or `add_python_test(`. `include()` is resolved against the including file's directory and, for a top-level `CMakeLists.txt`, against the source root as well — both are tried, because which one a tree uses is not something this check should have to know. Self-test: a synthetic root with a registration `.cmake` included from two `CMakeLists.txt`, one before and one after the `find_package`; the check must return 1 (the early include is a counter-example) and name it. Verified on this tree: still rc 0 with no includes that bind the interpreter, `self-test ok`. Refs #1979, #1981, #1864, #1891.
…e (review of #2012) include_offenders() took the find_package's offset from the raw text and each include's from the comment-stripped text, so a comment above a correct find_package made the include after it an offender, reported at the wrong line. Strip first, and take the line from the include's own argument. The self-test checks each tree by itself and no longer leaves a directory behind. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
ready for review at 3be7aed |
PR Reviewer Guide 🔍(Review updated until commit 7de6d9a)Here are some key observations to aid the review process:
✅ Resolved findingstests/python_test_order_check.py:95-107Potential Performance Issue in include_offenders The tests/python_test_order_check.py:139-148Temporary Directory Usage in Self-Test The self-test uses |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 3be7aed: READY
Reviewed head 3be7aed (refs #1979). This is the first verdict on this PR. The head is the same commit as #2072's head, now based on main.
Checked
git merge-treeonto current main: no conflicts. The only file istests/python_test_order_check.py(+84/−1). CI at this head:buildand CodeQL green.--self-test: "self-test ok", rc 0.--check --root .on this tree: "ok: every ${Python3_EXECUTABLE} use, and every include that binds it, follows find_package(Python3)", rc 0.- Mutants of
include_offenders():
| mutant | result |
|---|---|
| #2072's fix reverted (find_package offsets from the raw text, include offsets from the stripped text) | rc 1: "a comment above a correct find_package does not make the include an offender: want [], got [3]" |
line number from m.start() instead of m.start(1) |
rc 1: "… still reports the include's own line: want [3], got [1]" |
| no find_package treated as fine | rc 1: "with no find_package the include is unbound" |
the $/< skip removed |
passes; equivalent, since a literal ${…} never resolves to a file and is skipped by the is_file() test anyway |
| the included file's comments not stripped | passes: see note 2 |
I restored the file (git status clean); the self-test is ok again.
- Scope: no include in this tree pulls in a registration file through a path the rule can read. The only registration include,
CMakeLists.txt:1763include(${_onebit_test_registration_file}), is a variable, which the docstring says is outside the rule. So this guards future edits rather than any current row. That matches "This does not close #1979".
Notes, not blocking
- The exec bit is dropped. Main and #2012 have
tests/python_test_order_check.pyas 100755;3be7aedmakes it 100644, and the body does not mention it. ctest runs it throughadd_python_test, so nothing breaks, but./tests/python_test_order_check.pyno longer runs. That is the same mode-only rewrite this file's own docstring describes (hrx_build_scope.py, 100755 → 100644). Unless the change is intended, restore the mode (git update-index --chmod=+x). - No fixture has a binding only in a comment. Every fixture
.cmakehas a realadd_python_test(, so stripping the included file's comments is not pinned. Adding a fixture whosereg.cmakementionsadd_python_test(only in a comment, with an expected[], would pin it. - The self-test prints a stray FAIL line. It prints
FAIL 1 registration(s) run a script through its exec bit …(from theearliercounter-example tree run throughcheck()) right before its ownoklines. A log grep for^FAILwill find it on a green run. Silencingcheck()'s output for that call (e.g.contextlib.redirect_stdout) would keep the log honest.
#2012 and #2072 should be closed when this merges, as the body says.
|
VERDICT at 3be7aed: READY
So #1981/#2126 and this PR can land in either order: the rule passes the family file where #1981 puts it, and it catches the misplacement it exists for. |
|
VERDICT: READY at Canonical line for the head that already has a READY in another form. The newest verdict on this PR reads Re-ran the two arms at And the A/B that makes the include arm real, repeated here because it is the one thing worth proving rather than asserting: inserting Two lines, the line number, and the consequence — the The honest boundary stays documented: an include of a variable or a module is skipped because there is no file to read, and the docstring says so — a silent skip that is written This is the third PR tonight whose only verdict is in the non-canonical form (#1960 per another lane's brief, #2103, and this). Eight of my own first lines were non-canonical too. The |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY -- and one latent hole worth closing now, because it costs nothing while no caller uses it
The two fixes this PR carries are the interesting part, and both are the kind of bug that makes a rule untrustworthy rather than merely wrong:
- one text for every offset -- an include's offset taken from the comment-stripped text and
find_package's from the raw text are not comparable, and a comment above a correctfind_packageturned the include after it into an offender at the wrong line; m.start(1), notm.start()-- the pattern's leading\s*swallows the blank lines a stripped comment leaves behind, so the reported line could sit above the include.
and the self-test pins the outcome that matters: "an include before the find_package is the offender, at its own line", with a real cmake/reg.cmake fixture that calls add_python_test. A gate whose failure path is a fixture, not a hope, is the standard I would hold every --check/--self-test pair to.
The hole: INCLUDE = r"^\s*include\(\s*([^)\s]+)\s*\)" requires the argument to be the only thing before ), so a CMake-legal trailing keyword makes the include invisible:
MATCH include(cmake/reg.cmake)
SKIP include(cmake/reg.cmake OPTIONAL)
SKIP include(cmake/reg.cmake NO_POLICY_SCOPE)
SKIP include(cmake/reg.cmake RESULT_VARIABLE _r)
MATCH include( cmake/reg.cmake )
include(<file> [OPTIONAL] [RESULT_VARIABLE <var>] [NO_POLICY_SCOPE]) is all legal CMake, and a registration include that is OPTIONAL is exactly the shape someone adds when a file may not exist -- and it would be skipped silently, with the gate reporting clean.
Why this is cheap today and not later: I checked every include() in the top-level CMakeLists.txt -- 7 of 7 match the current pattern (cmake/binary_drop.cmake, FetchContent, ${CMAKE_CURRENT_SOURCE_DIR}/onnx/ep/npu_sources.cmake, cmake/hrx.cmake, cmake/hf_tokenizers.cmake, cmake/laya-ggml.cmake, ${_onebit_test_registration_file}) and none uses a keyword. So widening the pattern to include\(\s*([^)\s]+)[^)]*\) cannot change today's verdict; it only stops a future OPTIONAL from being free. The variable-argument cases (FetchContent, ${_onebit_test_registration_file}) stay skipped by the "must be a file that exists" rule, which is correctly documented.
Also checked, and it is why the rule is quiet on this tree: find_package(Python3 COMPONENTS Interpreter) sits at CMakeLists.txt:300 and none of the four real cmake/*.cmake files mentions Python3_EXECUTABLE or add_python_test, so there is genuinely no offender at main -- the rule is a regression guard, not a fix, and the fixture in self_test is the only thing that ever exercises its failing direction. That is fine because the fixture exists; it would not be if the PR had relied on "the tree is clean".
Not blocking: nothing in the tree uses a keyword form, and the pattern is documented. But it is a one-character-class change inside the same file this PR is already touching.
|
VERDICT: READY at The issue has three PRs because it is two problems
Those are complementary and both are worth having. What is not worth having is #2012 and #2123 side by side: so #2123 is a strict stack on #2012 ( What the extension adds is the interesting partCarrying the line number through an include tree — and asserting that a comment above a wrong order does not steal the attribution. That is the same class as the Two asks
|
…n include include(<file> OPTIONAL), NO_POLICY_SCOPE and RESULT_VARIABLE <v> are legal CMake; the old pattern required the file to be the only argument, so such an include was skipped and the gate reported clean (review of #2123 at 3be7aed). Take the first word as the file; three self-test arms pin it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Persistent review updated to latest commit 7de6d9a |
|
ready for review at 7de6d9a Answers the 22:50Z review's "latent hole" at 3be7aed: The same 10 arms against the checker at 3be7aed:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 7de6d9a: READY
Re-review of 7de6d9a. Since 3be7aed (READY from me and one other reviewer) it adds 7de6d9a3, which closes the latent hole the other reviewer named there. INCLUDE now takes the first word inside include(...) as the file ([^)\s]+[^)]*). So include(<file> OPTIONAL), RESULT_VARIABLE <v> and NO_POLICY_SCOPE are still treated as includes; before, they were skipped and the gate reported clean. It merges cleanly onto main.
Checked at this head:
python3 tests/python_test_order_check.py --self-test:self-test ok, including the three new arms (OPTIONAL beforefind_package, RESULT_VARIABLE plus NO_POLICY_SCOPE before, OPTIONAL after).--checkon the tree:ok: every ${Python3_EXECUTABLE} use, and every include that binds it, follows find_package(Python3).- Mutant (the old
include\(\s*([^)\s]+)\s*\)regex back): self-test rc 1, 5 FAIL. After restoring the file the tree is clean.
CI build at 7de6d9a is green: 100% tests passed, 0 tests failed out of 262.
Refs #1979. Follow-up PR that lands #2012 and #2072 together on
main; it replaces both.Why a third PR: #2012 (by another lane) adds the rule that an
include()which binds the Python interpreter must come afterfind_package(Python3). A reviewer found a false positive in it at10a08b5, #2072 fixes that on #2012's branch, and #2012's reviewer wrote "superseded by #2072". But #2072 is based on #2012's branch, so the merge queue does not pick it up, and #2012 has had no push since 11-10. This PR is the same head as #2072 (3be7aed) with basemain. No new commits.What it contains
tests/python_test_order_check.py.find_package(Python3)no longer moves the boundary and the reported line number is the include's own. Seven self-test trees cover it.git diff --stat origin/main...3be7aed:tests/python_test_order_check.py+84/−1.git merge-tree origin/main 3be7aedis clean at main6afe233.Proof
Measurements are in #2072 (the correct-placement tree fails under #2012 alone at
CMakeLists.txt:175and passes with the fix;--self-testok). I did not re-run them for this PR, since the tree is byte-identical to #2072's head. CI on3be7aedis green.This does not close #1979: it guards one way a registration hunk goes wrong, not the staleness the issue is about.
Close #2012 and #2072 when this merges.
🤖 Generated with Claude Code