From fc16bed7cacc61cc90fc9e8a4f836d1061adfb82 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Fri, 31 Jan 2025 11:21:51 -0800 Subject: [PATCH] Handle python `range` passed to rx.Var.create (#4716) * Handle python `range` passed to rx.Var.create Fix the range function to use Math.ceil to handle jagged steps. Update test cases. * Set ToVarOperation.__qualname__ for better repr --- reflex/vars/base.py | 12 ++++++++++-- reflex/vars/sequence.py | 2 +- tests/integration/test_var_operations.py | 10 ++++++++++ tests/units/test_var.py | 8 ++++---- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 6f4eddf42..05215c632 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -445,7 +445,12 @@ class Var(Generic[VAR_TYPE]): _default_var_type: ClassVar[GenericType] = default_type - ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation' + new_to_var_operation_name = f"To{cls.__name__.removesuffix('Var')}Operation" + ToVarOperation.__qualname__ = ( + ToVarOperation.__qualname__.removesuffix(ToVarOperation.__name__) + + new_to_var_operation_name + ) + ToVarOperation.__name__ = new_to_var_operation_name _var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types)) @@ -1385,7 +1390,7 @@ class LiteralVar(Var): TypeError: If the value is not a supported type for LiteralVar. """ from .object import LiteralObjectVar - from .sequence import LiteralStringVar + from .sequence import ArrayVar, LiteralStringVar if isinstance(value, Var): if _var_data is None: @@ -1441,6 +1446,9 @@ class LiteralVar(Var): _var_data=_var_data, ) + if isinstance(value, range): + return ArrayVar.range(value.start, value.stop, value.step) + raise TypeError( f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}." ) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 5e9f6468e..f7a9958f5 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -1593,7 +1593,7 @@ def array_range_operation( The range of numbers. """ return var_operation_return( - js_expression=f"Array.from({{ length: ({stop!s} - {start!s}) / {step!s} }}, (_, i) => {start!s} + i * {step!s})", + js_expression=f"Array.from({{ length: Math.ceil(({stop!s} - {start!s}) / {step!s}) }}, (_, i) => {start!s} + i * {step!s})", var_type=List[int], ) diff --git a/tests/integration/test_var_operations.py b/tests/integration/test_var_operations.py index a09c8612e..9b952c575 100644 --- a/tests/integration/test_var_operations.py +++ b/tests/integration/test_var_operations.py @@ -600,6 +600,11 @@ def VarOperations(): ), id="foreach_in_match", ), + # Literal range var in a foreach + rx.box(rx.foreach(range(42, 80, 27), rx.text.span), id="range_in_foreach1"), + rx.box(rx.foreach(range(42, 80, 3), rx.text.span), id="range_in_foreach2"), + rx.box(rx.foreach(range(42, 20, -6), rx.text.span), id="range_in_foreach3"), + rx.box(rx.foreach(range(42, 43, 5), rx.text.span), id="range_in_foreach4"), ) @@ -799,6 +804,11 @@ def test_var_operations(driver, var_operations: AppHarness): ("memo_comp_nested", "345"), # foreach in a match ("foreach_in_match", "first\nsecond\nthird"), + # literal range in a foreach + ("range_in_foreach1", "4269"), + ("range_in_foreach2", "42454851545760636669727578"), + ("range_in_foreach3", "42363024"), + ("range_in_foreach4", "42"), ] for tag, expected in tests: diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 899075cdb..6458c2e29 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1076,19 +1076,19 @@ def test_array_operations(): assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].slice().reverse()" assert ( str(ArrayVar.range(10)) - == "Array.from({ length: (10 - 0) / 1 }, (_, i) => 0 + i * 1)" + == "Array.from({ length: Math.ceil((10 - 0) / 1) }, (_, i) => 0 + i * 1)" ) assert ( str(ArrayVar.range(1, 10)) - == "Array.from({ length: (10 - 1) / 1 }, (_, i) => 1 + i * 1)" + == "Array.from({ length: Math.ceil((10 - 1) / 1) }, (_, i) => 1 + i * 1)" ) assert ( str(ArrayVar.range(1, 10, 2)) - == "Array.from({ length: (10 - 1) / 2 }, (_, i) => 1 + i * 2)" + == "Array.from({ length: Math.ceil((10 - 1) / 2) }, (_, i) => 1 + i * 2)" ) assert ( str(ArrayVar.range(1, 10, -1)) - == "Array.from({ length: (10 - 1) / -1 }, (_, i) => 1 + i * -1)" + == "Array.from({ length: Math.ceil((10 - 1) / -1) }, (_, i) => 1 + i * -1)" )