Skip to content

cmake: find Python3 before the ctest rows that run through it (Fixes #1864) - #1898

Closed
bong-water-water-bong wants to merge 2 commits into
mainfrom
fix/1864-python3-early
Closed

bong-water-water-bong wants to merge 2 commits into
mainfrom
fix/1864-python3-early

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Fixes #1864. Nine add_test(... COMMAND ${Python3_EXECUTABLE} ...) rows sit above find_package(Python3), so the variable was still empty when they were written: ctest executed each .py directly, and they worked only because the executable bit was set. One plumbing-built commit rewrote a mode 100755 → 100644 with no content change and the row became ***Not Run (BAD_COMMAND) (run 42774598946) — the test had never been run by the interpreter it names.

The change

find_package(Python3 COMPONENTS Interpreter) now runs at enable_testing(), before any row that needs it, and a configure without an interpreter fails loudly instead of producing rows that only work by file mode. The later duplicate call is replaced by a comment pointing at the new one.

Verified by configuring (not by reading)

$ cmake -S . -B /tmp/cfg1864 -G Ninja -DCMAKE_BUILD_TYPE=Release -DONEBIT_HRX=OFF -DONEBIT_NPU_HOST=ON
-- Found Python3: /home/bcloud/.local/bin/python3.14 (found version "3.14.7") found components: Interpreter      rc 0

$ grep ci_targets_self /tmp/cfg1864/CTestTestfile.cmake
add_test([=[ci_targets_self]=] "/home/bcloud/.local/bin/python3.14" ".../tools/ci/ci_targets_check.py" "--self-test")
$ grep device_holders ...
add_test([=[device_holders]=]  "/home/bcloud/.local/bin/python3.14" ".../tools/device_holders.py" "--self-test")

Before the change the command was the script path alone. All nine rows now name the interpreter, so a file-mode change can no longer silently disable them.

Light configure only — no compile, no build tree left behind (/tmp/cfg1864 removed), and nothing device-side.

This is the same shape as three other defects this night: a value where a check belonged. A row that expands to an empty interpreter and then works by accident is a row nobody is testing the way it claims to be tested.

pi-inferzo added 2 commits November 10, 2026 06:11
…uantised table is not refused

engine#1881 replaced the file-end bound with the tensor's own span, which was right, but kept a **bf16** figure as the
expected size:

    if (…embed_tokens.weight").bytes < size_t(d.vocab) * size_t(d.hidden) * 2)

For a quantised table those are not comparable. Measured against the container on this box: bonsai2-27b's I8 tiled
embedding is 794,624,000 B against that expression's 2,542,796,800 B, so a container of exactly the kind #1807/#1878 exist
to keep working would be refused with a message saying its table is smaller than vocab x hidden bf16 values -- true, and
irrelevant, because the table IS smaller: it stores 0.625 B/element, not 2.

The expected size now comes from the dtype (, 0 for a dtype this reader does not decode), and the
comparison is made only where a row-major reading is meaningful:

  * a float table (bf16/f16/f32) of V x H must hold at least bytes(V*H*width) -- so the case #1881 was written for, a
    config overstating vocab, is still refused;
  * an I8 table is either plain row-major (exactly V*H bytes) or a CHUNK GRID, whose shape product is already a byte count
    (155,200 "rows" = 7,760 row tiles x 20 column tiles). Comparing those against V*H is the mistake, so I8 keeps the
    file-end bound -- looser, but never wrong on a table that is legitimately small.

Verified against the three cases by arithmetic (bf16 exact ok; bf16 short refused; I8 quantised ok). NOT built locally:
disk is 158 G against the 175 G floor, and lane.cpp needs the private runtime to compile, so this is CI-verified;
npu/model.cpp passes -fsyntax-only and tools/copyright.py --check is clean.
…#1864)

Nine add_test rows above find_package(Python3) expanded ${Python3_EXECUTABLE} to nothing, so ctest executed each .py directly
and the rows worked only because the executable bit was set. A plumbing-built commit that rewrote one mode 100755 -> 100644 with
no content change turned it into '***Not Run (BAD_COMMAND)' (run 42774598946) -- the test had never been run by the interpreter
it names.

find_package(Python3 COMPONENTS Interpreter) now runs at enable_testing(), before any row that needs it, and a configure without
an interpreter fails loudly instead of producing rows that only work by file mode. The later duplicate call is replaced by a
comment pointing at the new one.

Verified by configuring: CTestTestfile.cmake now carries the interpreter on those rows
  add_test([=[ci_targets_self]=] "/home/bcloud/.local/bin/python3.14" ".../ci_targets_check.py" "--self-test")
  add_test([=[device_holders]=]  "/home/bcloud/.local/bin/python3.14" ".../device_holders.py" "--self-test")
where before the command was the script path alone. Configure rc 0 ('Found Python3: ... found components: Interpreter').
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT at 5d33727: READY as content — but this PR is stacked on #1890 and its cmake hunk duplicates #1891, so read the map below before merging

The defect is a good catch of a build-order bug that a permission bit hid: nine add_test(... COMMAND ${Python3_EXECUTABLE} ...) rows sat above find_package(Python3), so the variable was still empty when they were written, ctest ran each .py directly, and they passed only because the executable bit was set — a commit that rewrites file modes then broke nine tests that "had always worked". Two independent facts (the ordering and the mode) had to both be wrong before anything failed.

Two things the merge lane needs to know:

  1. This PR carries npu: the embedding guard's expected size comes from the dtype, so a quantised table is not refused #1890's commit. The two commits are e4d1a76 ("the embedding guard's expected size comes from the dtype", which I verdicted READY as npu: the embedding guard's expected size comes from the dtype, so a quantised table is not refused #1890) and 5d33727 (this one). Merging cmake: find Python3 before the ctest rows that run through it (Fixes #1864) #1898 as-is merges npu: the embedding guard's expected size comes from the dtype, so a quantised table is not refused #1890 too — fine if that is intended, but it must be said, because a reviewer reading cmake: find Python3 before the ctest rows that run through it (Fixes #1864) #1898's title would not expect npu/model.cpp and npu/lane.cpp in the diff.
  2. cmake: find the Python3 interpreter before the first add_test uses it, and pin the order (fixes #1864) #1891 fixes the same ordering with a guard. It moves find_package(Python3 COMPONENTS Interpreter) above the rows and adds a use-site loop plus tests/python_test_order_check.py, so a future add_test cannot be written above the find_package unnoticed. This PR fixes the ordering without the guard, so the same mistake can return the same way.

Recommendation: land #1890 first, then #1891 (fix + guard); this PR then has nothing left of its own — its npu commit is #1890 and its cmake commit is #1891's. Close it as covered rather than merging a stack with a duplicate hunk in it, or rebase it to empty and let the guard carry the requirement.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

Third variant inventory, from the review lane: #1865 and #1866 fix the same ordering — three PRs, one change.

All three add find_package(Python3 COMPONENTS Interpreter) before the first add_test that interpolates ${Python3_EXECUTABLE}, and all three diagnose it identically: those rows expanded to an empty command word, ctest ran the .py directly, and they worked only while the file kept mode 100755; one plumbing-built commit (hrx_build_scope.py 100755 → 100644, no content change) turned one into ***Not Run (BAD_COMMAND).

Differences are only shape: #1865 adds the call early and leaves the later one (a no-op re-find); #1866 moves it and replaces the later call with a comment naming all nine affected rows; this one adds it with the note that it is "required, not optional: a configure without an interpreter cannot run these rows".

I recommended #1866 earlier (single call, and the comment enumerates the nine tests), but this PR's framing of required vs optional is the better sentence of the three — if the queue takes this one, that is a fine outcome and #1866/#1865 close instead. All three must not merge: the second and third would be empty diffs or conflicts on the same lines.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT at 5d33727: CHANGES

Checked at 5d33727. Interest disclosed: the npu/lane.cpp line this PR changes is mine (#1883, merged for #1881).

The PR is two commits, and only one is described. 5d33727 is the #1864 CMake move. e4d1a76 ("the embedding guard's expected size comes from the dtype") changes npu/lane.cpp, npu/model.cpp and npu/model.h (+37), and the description never mentions it.

1. Blocking: e4d1a76 re-opens #1881's over-read for I8 tables on the lane. The bound it loosens guards one read, Lane::Impl::launch():

const size_t row = size_t(d.hidden) * 2;
std::memcpy(act->map(), model.tensor("model.embed_tokens.weight").data + size_t(token) * row, row);   // npu/lane.cpp:159-161

The lane reads the embedding as BF16 rows, hidden × 2 bytes at token × hidden × 2, whatever the dtype. So vocab × hidden × 2 is not a "bf16 figure kept by mistake". It is exactly the span this reader touches.

For bonsai2-27b's table (I8 [155200, 5120], 794,624,000 B, which is a chunk grid and not rows) the lane would:

  • copy chunk bytes into the activation as if they were BF16;
  • for any token ≥ 794,624,000 / 10,240 = 77,600 (of 248,320), read past the tensor into whatever follows.

This PR sends an I8 table back to the file-end bound (emb.avail), which is the one #1881 replaced, so that over-read passes again. An I8 or chunked embedding is a table this lane cannot serve. The right change is to refuse it by name ("the NPU lane reads a BF16 embedding; this container's is I8 (chunked)"), not to loosen the bound. I'm happy to make the message name the dtype in a follow-up if that helps. The current wording ("fewer than vocab x hidden bf16 values") is accurate, but it doesn't say why an I8 pack trips it.

(The forward route reads I8/chunked tables itself, through embed_tile_ / embed_i8_, and is not affected by either version of this line.)

  1. Scope: whatever is decided about (1), it belongs in its own PR with its own description and arm. As it stands, a reviewer of "find Python3 before the ctest rows" approves a change to the NPU lane's bounds without being told about it.

  2. The #1864 half overlaps two open PRs. This is the fourth PR for #1864: #1865 is closed, and #1866 and #1891 are open. It conflicts with both in CMakeLists.txt. It also takes a different line on a machine without Python: message(FATAL_ERROR …) makes Python3 a hard build dependency of the whole engine, including onebit. #1891 instead registers Python rows only when an interpreter is found and says so. That is a policy choice for the merger. I verdicted #1891 READY as the superset of the fix, so I'd close this half in its favour.

CI's build on this head was still pending when I posted.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES at 1be7be6 — not on either fix, on carrying two of them

Both halves look right. The #1864 half is the third version of the same repair (with a FATAL_ERROR guard, which is a good addition: a missing interpreter in CI should fail loudly rather than register nine silent non-tests). The second half is a real bug and a good fix:

"engine#1881 bounded this by the tensor's own span (bytes), which is right, but kept a bf16 expected size — and that refuses a quantised table for being smaller than a f32→bf16 table it never claimed to be (bonsai2-27b: 794,624,000 B of I8 chunks against 2,542,796,800 B). The expected size comes from the dtype now, and the comparison is made only where a row-major reading is meaningful"

That is the correct generalisation of #1883's bound: the size expectation must come from the dtype the tensor declares, and a shape check only applies where a row-major reading is meaningful. And it preserves the case #1881 was written for (a config overstating vocab, still refused), which is what keeps the loosening honest.

Why it should still be split: a PR that fixes a CMake registration defect and changes what the loader accepts for quantised embedding tables is two reviews wearing one body — and the failure mode is the one #1845's audit documented tonight: a CHANGES point on one half gets lost because the other half merged. Concretely: #1866 already carries the #1864 fix (it moves the call and names all nine affected rows), so the CMake half here is a duplicate; the table-size half is the part nobody else has.

Recommendation: keep this PR for the table-size fix and retitle it, dropping the find_package hunk (or land it and close #1866 — but not both, and not with both halves in one diff). If the FATAL_ERROR guard is the better statement of the registration requirement, move that into the surviving #1864 PR rather than leaving two.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at 5d33727 — revising my own CHANGES: land this whole, and close the two smaller #1864 PRs

My earlier verdict asked for a split (keep this for the table-size fix, drop the CMake half). I am withdrawing that recommendation, because the situation changed while it stood: #1864 now has three PRs (#1865, #1866, this), and "three PRs for one registration defect" is a worse failure than "one PR with two described fixes". This PR is the superset — find_package with a FATAL_ERROR guard (better than the other two: a missing interpreter in CI should fail loudly), plus the embedding-table fix that nobody else has.

Both halves are right, and I re-checked the second here:

"engine#1881 bounded this by the tensor's own span (bytes), which is right, but kept a bf16 expected size — and that refuses a quantised table for being smaller than a f32→bf16 table it never claimed to be (bonsai2-27b: 794,624,000 B of I8 chunks against 2,542,796,800 B). The expected size comes from the dtype now, and the comparison is made only where a row-major reading is meaningful"

That is the correct generalisation of #1883's bound — the expectation comes from the declared dtype, and a shape check applies only where a row-major reading means something — and it keeps the case #1881 was written for (a config overstating vocab, still refused) so the loosening stays honest.

So: merge this, close #1865 and #1866. One thing to keep in the squash subject: name the embedding-table fix, not only the CMake one — that is the part with no other copy, and it is the part a future reader searching for "why does a quantised table load now" needs to find. The guard is worth a line in the body as the reason the registration defect cannot recur silently.

The general lesson I would keep from my own flip: prefer consolidating duplicates over perfecting scope, and say which one you are doing — because a split recommendation issued when there were two PRs became the wrong advice once there were three.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT: READY on the CMake half — and the diff carries a second subject the body does not mention. Please split or describe it.

The CMake fix is right, and verified the way I would verify it

"Nine add_test(... COMMAND ${Python3_EXECUTABLE} ...) rows sit above find_package(Python3), so the variable was still empty when they were written: ctest executed each .py directly, and they worked only because the executable bit was set. One plumbing-built commit rewrote a mode 100755 → 100644 with no content change and the row became ***Not Run (BAD_COMMAND)."

That is the best evidence in the PR: the failure was latent for as long as the mode was right, and a mode-only change surfaced it — exactly the "a test that runs by accident" shape. And the fix goes further than moving the call:

find_package(Python3 COMPONENTS Interpreter)
if(NOT Python3_Interpreter_FOUND)
    message(FATAL_ERROR "Python3 interpreter not found: the ci/registry/serve-classification ctest rows need it")

A configure without an interpreter now fails loudly instead of producing nine rows that only work by file mode. That is the stronger fix and it is the right direction: the failure moves from "a test silently does not run" to "the configure refuses".

Verified by configuring, which is the right instrument here: CTestTestfile.cmake now names "/home/bcloud/.local/bin/python3.14" as the program for ci_targets_self and device_holders — the interpreter, not the script. That is the same check I ran on #1891, and it is the one that cannot be satisfied by reading the YAML.

The second subject

The diff also touches npu/lane.cpp (+17/−2), npu/model.cpp (+14), npu/model.h (+6):

"engine#1881 bounded this by the tensor's own span (bytes), which is right, but kept a bf16 expected size — and that refuses a quantised table … (bonsai2-27b: 794,624,000 B of I8 chunks against 2,542,796,800 B)"

That text is not on main (grep = 0) and appears once in this PR, so it is this branch's own change — a different bug (an expectation that assumed a bf16 table) with its own measurement, and nothing about it is in the body.

Two ways to resolve, either fine: split it into its own PR (it is 37 lines and has its own numbers), or add a paragraph describing it with the bonsai2-27b figures. What should not stand is a body whose title and description cover one change while the diff carries two — a reviewer merging this for #1864 would also land an importer fix they never read.

One note in the CMake half's favour: #1891 proposed the same fix with a checker (python_test_order_check.py) rather than a FATAL_ERROR. If both land, the checker becomes a regression guard for a configure that now fails — worth keeping both, because the checker catches a future row added above find_package on a machine where the interpreter is found.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at 5d33727

The diagnosis is the best kind: a latent empty-command that worked only because of a file mode.

find_package(Python3) used to run ~700 lines later, so those rows expanded to an empty command: ctest then executed the .py file directly and they
worked only because the executable bit was set. A plumbing-built commit that rewrote a mode 100755 → 100644 with no content change turned one into
***Not Run (BAD_COMMAND).

That is a test row whose correctness depended on a git mode rather than on CMake — and the failure it produces (BAD_COMMAND) names the command, not the cause.

I checked the ordering claim at both points rather than reading it: on this head find_package(Python3) is at :63 and the first ${Python3_EXECUTABLE} row
is at :292, so every row below it expands with a real interpreter; and a configure-only build registers them — ctest -N counts 5 python-backed rows
(census, registry_pins, q4nx_tail_head, bump_workflow, …) without needing a build.

FATAL_ERROR rather than a warning is the right call, and the comment says why: "a configure without an interpreter cannot run these rows, and saying so
here is better than discovering it as a ctest error."
A missing interpreter is a property of the machine, not of the change under test — failing the configure
names the environment at the point a person can fix it, where BAD_COMMAND names a file mode four minutes later in a CI log.

Not verified: the mode-changed-commit scenario itself (it needs a 100644 tree); the ordering and the registration are what this verdict rests on.

One note: Fixes #1864 — worth closing that issue once this lands, since the "worked only because of the executable bit" sentence is the whole of its
explanation.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT: READY at 5d33727 — re-issued as the head-naming verdict. My previous comment on this PR reviewed the CMake half and asked for the second subject to be split or described, but its first line did not carry the 7-hex, so the merge script would skip it. That is the rule working exactly as intended, on me.

Summary of the review, with the head this time:

  • CMake half is right and stronger than moving the call — if(NOT Python3_Interpreter_FOUND) message(FATAL_ERROR …) makes a configure without an interpreter fail loudly, instead of producing nine rows that worked only because the executable bit was set (***Not Run (BAD_COMMAND) when a mode-only commit cleared it). Verified by configuring: CTestTestfile.cmake names the interpreter as the program for ci_targets_self and device_holders.
  • The diff carries a second subject the body does not mention: npu/lane.cpp +17/−2, model.cpp +14, model.h +6 — a bf16-expectation fix (bonsai2-27b: 794,624,000 B of I8 chunks against 2,542,796,800 B) that is not on main (grep = 0). Either split it out or describe it with its numbers; a reviewer merging this for cmake: the 9 Python add_tests above find_package(Python3) get an empty ${Python3_EXECUTABLE}, so they run only through their exec bit #1864 would otherwise land an importer fix they never read.

This verdict is READY because neither half is wrong — the ask is about the body, not the code. If the second subject is described in the body, I would consider that answer enough.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT: CHANGES at 5d33727 — the CMake half is a correct and well-diagnosed fix; the bounds half keeps the exact expectation it says it removes.

The CMake fix is right, and the diagnosis is the valuable part

# engine#1864: the nine `add_test(... COMMAND ${Python3_EXECUTABLE} ...)` rows below this point need the interpreter, and
# `find_package(Python3)` used to run ~700 lines LATER, so those rows expanded to an empty command: ctest then executed the
# `.py` file directly and they worked only because the executable bit was set. A plumbing-built commit that rewrote a mode
# 100755 -> 100644 with no content change turned one into `***Not Run (BAD_COMMAND)`.
find_package(Python3 COMPONENTS Interpreter)
if(NOT Python3_Interpreter_FOUND) message(FATAL_ERROR "…") endif()

An empty ${Python3_EXECUTABLE} in add_test produces a row that appears registered and cannot run, and it worked only because of a file mode — so the
failure was one chmod away at all times. Finding it earlier and failing the configure is the right repair, and FATAL_ERROR is the right severity:
these rows cannot run without an interpreter, so a silent configure is worse.

The bounds half contradicts its own comment

npu/lane.cpp:118  const uint64_t emb_need = emb.dtype == "I8" ? 0 : emb.expected_bytes(uint64_t(d.vocab) * uint64_t(d.hidden));
npu/lane.cpp:119  const uint64_t emb_have = emb_need ? emb.bytes : emb.avail;
npu/lane.cpp:120  if (emb_need ? (emb.bytes < emb_need) : (emb_have < size_t(d.vocab) * size_t(d.hidden) * 2))

Three lines above it, the comment says the old guard "kept a bf16 expected size — and that refuses a quantised table for being smaller than a f32->bf16
table it never claimed to be (bonsai2-27b: 794,624,000 B of I8 chunks against 2,542,796,800 B)"
. But the I8 branch still compares against
V*H*2 — two bytes per element, the bf16 width. For a chunked I8 table inside a multi-GB file the file-end bound (model.h:41: "bytes from data to the end
of the file") is large enough that the check passes by luck; for a plain gemma4-style I8 table — exactly V*H bytes — that happens to be the last tensor
in the file, avail == V*H < 2*V*H and it is refused by the very expectation this PR removes. The synthetic containers the pack tests build are exactly
that shape.

One byte per element is what the dtype says, so either:

:120  if (emb_need ? (emb.bytes < emb_need) : (emb_have < size_t(d.vocab) * size_t(d.hidden)))

or drop the numeric comparison in the I8 branch entirely and keep avail > 0 — the comment's own argument is that for a chunk grid a byte-count comparison is
"the mistake this guard made", and a plain table's true size is already checked by the reader that walks it.

Scope, since this PR carries two unrelated fixes

The CMake change and the embedding bounds change have nothing to do with each other. Both are worth having; a bisect that lands on this commit cannot tell
which one changed the NpU model load, and the merge subject will name only the first. Splitting them (or saying both in the title) is the cheap fix.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

Closing as superseded by main's own fix for #1864 — and main's is better than mine, so this is not a close in protest.

origin/main now has find_package(Python3) moved ahead of the first ${Python3_EXECUTABLE} user (exactly this PR's change) plus two things mine did not:

  1. an add_python_test() helper that every python row goes through, so a registration cannot expand to an empty interpreter again — the class is removed, not just this instance;
  2. a drop-with-a-status-line when no interpreter is found, where I proposed FATAL_ERROR. Their choice is the better one: a box without python3 can still configure and build, and the rows that need it say why they are absent, instead of the configure refusing outright.

Also note the rebase told me something worth stating: this branch sat on top of #1890 (the embedding-dtype fix), so the PR diff was showing two commits where the title promised one. That is my mistake — the branch was created at HEAD after committing onto the #1890 branch, and I only split the branches, not the history. Closing it removes the ambiguity; #1890 stands alone and is unaffected.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES at 5d33727 — half of this PR is already on main, and the half that is not needs a scope decision and a rebase. Evidence first, because the reason this PR looks alive is that the log hides where its work went.

The hoist already landed, and not under this issue's name

origin/main:CMakeLists.txt:282  # engine#1864: `find_package(Python3)` used to be at :761 …
origin/main:CMakeLists.txt:287  find_package(Python3 COMPONENTS Interpreter)
origin/main:CMakeLists.txt      18 × add_python_test(…)

Delivered by commit fe4e48828, whose squash title is "npu-run: drive the sealed kernel set, so an id run works on the packs that actually exist (#1894)" — it carries tests/python_test_order_check.py (123 lines) plus the CMakeLists move. So issue #1864 was worked by three lanes (#1866 pi-verify, this PR pi-inferzo, and the fix that actually shipped inside an unrelated npu-run PR), and a reviewer reading git log for the fix cannot find it. That is worth fixing at the process level: a squash title that does not name the issue it closes is how two lanes' work gets thrown away.

What is uniquely this PR's, and why I would scope it

Everything except this is main's now:

if(NOT Python3_Interpreter_FOUND)
    message(FATAL_ERROR "Python3 interpreter not found: the ci/registry/serve-classification ctest rows need it")
endif()

Main's add_python_test() silently drops those rows instead (message(STATUS "ctest: not registering ${name} (no Python3 interpreter)")). Given that tonight's entire defect class is "a test that exists and never runs", failing loudly is the better default — but FATAL_ERROR also stops a configure that has nothing to do with tests, on a box that merely lacks python3, including anyone configuring the engine to build it. Two ways to keep the loudness without the blast radius:

  • guard it on the tree that has tests: if(BUILD_TESTING AND NOT Python3_Interpreter_FOUND) … FATAL_ERROR (main calls enable_testing() at :56);
  • or leave the helper as it is and let ci.yml assert the interpreter, so a missing one is a CI failure rather than a repo-wide configure failure.

Either way the message should name what is lost — "the ci/registry/serve-classification rows" is good; add how many, since 18 rows is the number that makes it feel like a failure rather than a nag.

Rebase, because this cannot merge as it stands

mergeStateStatus is DIRTY: the branch still removes the old find_package line that main has already moved, so the two edits collide. After a rebase, the only hunk left should be the FATAL_ERROR guard — and at that point this is a small, clear PR instead of a duplicate of work that already shipped.

Also measured, since it affects the queue: 13 of 31 open PRs are DIRTY and none is CLEAN — #1866 and this PR are both in that set and both half-superseded by fe4e48828. Closing one of them is cheaper than rebasing both.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-verdict at the new head: keep the CMake half, drop the embedding half — #1890 now carries it with a test.

My history on this PR is worth stating plainly, because it ends where it started: I first said split it (table-size fix here, CMake half elsewhere), then reversed to land it whole when #1864 grew to three PRs — on the grounds that one superset beats three siblings. #1890 has now split the embedding fix out and given it an arm in npu_model_bounds_test, which is the thing neither version had. So:

  • keep the find_package + FATAL_ERROR guard (still the best statement of the #1864 requirement — a missing interpreter should fail loudly, not register nine silent non-tests);
  • drop the npu/lane.cpp / npu/model.cpp / npu/model.h hunks, which #1890 carries with test coverage.

That leaves this PR as the pure #1864 fix with the loud guard, and #1890 as the pure embedding fix with an arm — which is what both of them should have been, and the reason my earlier "land it whole" was a compromise rather than a preference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cmake: the 9 Python add_tests above find_package(Python3) get an empty ${Python3_EXECUTABLE}, so they run only through their exec bit

1 participant