fix(vars): compile % with Python's sign-of-divisor semantics
All Submissions:
- Have you followed the guidelines stated in CONTRIBUTING.md file?
- Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Type of change
- Bug fix (non-breaking change which fixes an issue)
Changes To Core Features:
- Have you added an explanation of what your changes do and why you'd like us to include them?
- Have you written new tests for your core changes, as applicable?
- Have you successfully ran tests with your changes locally?
Description
number_modulo_operation compiles a % b to JavaScript's %, which takes the sign of the dividend. Python's % takes the sign of the divisor. So a modulo that is correct in Python gives a different answer on the frontend once an operand is negative:
-7 % 3is2in Python but renders as(-7 % 3), which is-1in the browser.5.5 % -2is-0.5in Python but1.5in JS.
Floor division already follows Python (Math.floor(a / b)), so x // y * y + x % y doesn't give back x for negative values. The usual wrap-around pattern (State.index - 1) % State.count also yields -1 instead of the last index.
The operation now compiles to a small arrow function that applies CPython's float-modulo rule: when the remainder is nonzero and its sign differs from the divisor's, the divisor is added. Each operand is still evaluated once, and no new import is needed.
I ran the compiled expressions through Node and compared them with Python's %. All of these match: (-7, 3), (7, -3), (-7, -3), (5.5, -2), (-5.5, 2), (0, 5), (-6, 3), (0.3, 0.1), and ±Infinity divisors.
test_modulo_takes_the_sign_of_the_divisor fails on main with '(-7 % 3)' == '((a, b) => {... r; })(-7, 3)'. The three existing test_var.py assertions that pinned the old (a % b) output are updated. The PR also includes a bugfix news fragment for reflex-base. ruff and pyright are clean on the changed files.