fix(vars): compare array vars element-wise with Python semantics
Fixes #7654
Problem
Array ordering comparisons (<, <=, >, >=) on array vars compile to a direct JS comparison between the two arrays. JS does not compare arrays element-wise; it coerces both sides to comma-joined strings, so [1, 10, 0] < [1, 9, 0] evaluates "1,10,0" < "1,9,0" and returns true, where Python compares element-wise and returns False.
Fix
The four array ordering operations now compile through a new compareArrays frontend helper ($/utils/helpers/array.js, same pattern as compareDatetime) that mirrors Python list comparison:
- pairwise element comparison; the first unequal pair decides the result
- when every shared element is equal, the shorter array sorts first
- nested arrays compare recursively
- booleans compare as numbers, matching Python where
boolsubclassesint([True] == [1],[True] < [2]) - a
NaNoperand pair yieldsNaN, matching Python where every ordering comparison againstfloat("nan")is false - an incomparable element pair (mismatched or unsupported types, where Python raises
TypeError) also yieldsNaN, so every ordering result is false instead of a silent string comparison
Equality (== / !=) is unchanged and keeps its existing comparison path.
Scope and remaining divergences
This covers scalar (number/string/bool) and nested-array elements, which is what serialized array vars contain in practice. Remaining known divergences from Python, unchanged by this PR:
- string comparison is by UTF-16 code units, so astral-plane characters can order differently than Python's code-point ordering
- elements serialized to strings before comparison (e.g. dates inside arrays) compare as their serialized form, not their original type
- plain-object elements are treated as incomparable (ordering is false); Python compares dicts for equality and raises on ordering
Tests
tests/units/test_var.py: JS emission for all four operators plus thecompareArraysimport wiringtests/units/vars/array_helpers.test.mjs: runtime suites against the real template module (numeric ordering, prefix rule, strings/booleans, nested arrays,NaNoperands, bool/number mixing, incomparable types)tests/units/vars/test_array_helpers_template.py: pytest wrapper that executes the runtime suites (skips when Node.js is unavailable)- Verified in a compiled app in a real browser: the unpatched build renders the string-coerced result, the patched build renders the Python-consistent result
Test limits: ruff check, ruff format, codespell, biome, pyright on the touched files, the touched pytest modules, and node --test were run directly and pass; the uv-managed pre-commit env hooks and full CI have not run on this branch.
@masenf - following the compareDatetime precedent here; would appreciate a look when you have a moment.