From 1b63beb675d46b6250d30aaa570de4722b582955 Mon Sep 17 00:00:00 2001 From: Darsh Joshi Date: Wed, 14 Oct 2026 11:32:15 -0400 Subject: [PATCH 1/4] fix: parenthesize a unary base in the compiled ** expression --- packages/reflex-base/src/reflex_base/vars/number.py | 11 ++++++++++- tests/units/test_var.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/reflex-base/src/reflex_base/vars/number.py b/packages/reflex-base/src/reflex_base/vars/number.py index 4dd45fae741..cacb83577d1 100644 --- a/packages/reflex-base/src/reflex_base/vars/number.py +++ b/packages/reflex-base/src/reflex_base/vars/number.py @@ -6,6 +6,7 @@ import decimal import json import math +import re from collections.abc import Callable from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, overload @@ -647,6 +648,9 @@ def number_modulo_operation(lhs: NumberVar, rhs: NumberVar): return f"({lhs!s} % {rhs!s})" +_STARTS_WITH_UNARY_OPERATOR = re.compile(r"[-+!~]|(?:typeof|void|delete|await)\b") + + @binary_number_operation def number_exponent_operation(lhs: NumberVar, rhs: NumberVar): """Exponentiate two numbers. @@ -658,7 +662,12 @@ def number_exponent_operation(lhs: NumberVar, rhs: NumberVar): Returns: The number exponent operation. """ - return f"({lhs!s} ** {rhs!s})" + base = str(lhs) + # JavaScript rejects a unary expression as the base of `**` (`-2 ** 2` is a + # SyntaxError), so a negative literal or a negated var must be parenthesized. + if _STARTS_WITH_UNARY_OPERATOR.match(base): + base = f"({base})" + return f"({base} ** {rhs!s})" @var_operation diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 5838cace45d..cbbb10f4f90 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -400,6 +400,17 @@ def test_datetime_comparison_uses_timestamps(operation, operator): ) +def test_exponent_parenthesizes_a_unary_base(): + """JavaScript rejects a unary expression as the base of `**` (`-2 ** 2` is a SyntaxError).""" + x = Var(_js_expr="x").to(int) + assert str(LiteralNumberVar.create(-2) ** 3) == "((-2) ** 3)" + assert str(LiteralNumberVar.create(-2.5) ** x) == "((-2.5) ** x)" + assert str((-x) ** 2) == "((-(x)) ** 2)" + assert str(-(x**2)) == "-((x ** 2))" + assert str(x**-1) == "(x ** -1)" + assert str(2**-x) == "(2 ** -(x))" + + def test_datetime_comparison_preserves_microseconds(): lhs = cast( DateTimeVar, From 47af71360a9cb04e98314a11af238521248a6f6f Mon Sep 17 00:00:00 2001 From: Darsh Joshi Date: Wed, 14 Oct 2026 11:37:09 -0400 Subject: [PATCH 2/4] Add a news fragment --- packages/reflex-base/news/7565.bugfix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/reflex-base/news/7565.bugfix.md diff --git a/packages/reflex-base/news/7565.bugfix.md b/packages/reflex-base/news/7565.bugfix.md new file mode 100644 index 00000000000..cf1cdad5078 --- /dev/null +++ b/packages/reflex-base/news/7565.bugfix.md @@ -0,0 +1 @@ +Raising a negative number or a negated var to a power, such as `(-State.x) ** 2`, now compiles to valid JavaScript instead of failing the frontend build with "A unary expression with the '-' operator cannot be used as the left operand of an exponentiation expression". From d297ce9fae4df5364b1e91f22f797821045e5b54 Mon Sep 17 00:00:00 2001 From: Darsh Joshi Date: Sun, 18 Oct 2026 04:25:04 -0400 Subject: [PATCH 3/4] Move the exponent test next to the other reflex_base.vars tests --- tests/units/reflex_base/vars/test_number.py | 15 +++++++++++++ tests/units/test_var.py | 24 ++++++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 tests/units/reflex_base/vars/test_number.py diff --git a/tests/units/reflex_base/vars/test_number.py b/tests/units/reflex_base/vars/test_number.py new file mode 100644 index 00000000000..b8041f3d93c --- /dev/null +++ b/tests/units/reflex_base/vars/test_number.py @@ -0,0 +1,15 @@ +"""Tests for reflex_base.vars.number.""" + +from reflex_base.vars.base import Var +from reflex_base.vars.number import LiteralNumberVar + + +def test_exponent_parenthesizes_a_unary_base(): + """JavaScript rejects a unary expression as the base of `**` (`-2 ** 2` is a SyntaxError).""" + x = Var(_js_expr="x").to(int) + assert str(LiteralNumberVar.create(-2) ** 3) == "((-2) ** 3)" + assert str(LiteralNumberVar.create(-2.5) ** x) == "((-2.5) ** x)" + assert str((-x) ** 2) == "((-(x)) ** 2)" + assert str(-(x**2)) == "-((x ** 2))" + assert str(x**-1) == "(x ** -1)" + assert str(2**-x) == "(2 ** -(x))" diff --git a/tests/units/test_var.py b/tests/units/test_var.py index cbbb10f4f90..fe0639d53ac 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -55,7 +55,7 @@ import reflex as rx from reflex.environment import PerformanceMode -from reflex.state import BaseState +from reflex.state import BaseState, code_uses_state_contexts pytest.importorskip("pydantic") @@ -400,17 +400,6 @@ def test_datetime_comparison_uses_timestamps(operation, operator): ) -def test_exponent_parenthesizes_a_unary_base(): - """JavaScript rejects a unary expression as the base of `**` (`-2 ** 2` is a SyntaxError).""" - x = Var(_js_expr="x").to(int) - assert str(LiteralNumberVar.create(-2) ** 3) == "((-2) ** 3)" - assert str(LiteralNumberVar.create(-2.5) ** x) == "((-2.5) ** x)" - assert str((-x) ** 2) == "((-(x)) ** 2)" - assert str(-(x**2)) == "-((x ** 2))" - assert str(x**-1) == "(x ** -1)" - assert str(2**-x) == "(2 ** -(x))" - - def test_datetime_comparison_preserves_microseconds(): lhs = cast( DateTimeVar, @@ -1598,6 +1587,17 @@ def test_retrieval(): ) +@pytest.mark.parametrize( + ("javascript_code", "expected"), + [ + ("const state = useStateContext('state')", True), + ("const state = useContext(ThemeContext)", False), + ], +) +def test_code_uses_state_contexts(javascript_code: str, expected: bool): + assert code_uses_state_contexts(javascript_code) is expected + + def test_fstring_concat(): original_var_with_data = LiteralVar.create( "imagination", _var_data=VarData(state="fear") From 21503186ea4850c5f91d803ed3f83311b03352b1 Mon Sep 17 00:00:00 2001 From: Darsh Joshi Date: Sun, 18 Oct 2026 17:50:49 -0400 Subject: [PATCH 4/4] Keep test_var.py as it is on the base branch --- tests/units/test_var.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index fe0639d53ac..5838cace45d 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -55,7 +55,7 @@ import reflex as rx from reflex.environment import PerformanceMode -from reflex.state import BaseState, code_uses_state_contexts +from reflex.state import BaseState pytest.importorskip("pydantic") @@ -1587,17 +1587,6 @@ def test_retrieval(): ) -@pytest.mark.parametrize( - ("javascript_code", "expected"), - [ - ("const state = useStateContext('state')", True), - ("const state = useContext(ThemeContext)", False), - ], -) -def test_code_uses_state_contexts(javascript_code: str, expected: bool): - assert code_uses_state_contexts(javascript_code) is expected - - def test_fstring_concat(): original_var_with_data = LiteralVar.create( "imagination", _var_data=VarData(state="fear")