From 3876d6d6bbaa0c81500a9cab3c7cde9ddf8fd560 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 21:10:52 +0000 Subject: [PATCH 1/2] fix(vars): accept a frozenset as an array literal `LiteralVar.create` rendered a `set` as an array but rejected a `frozenset` with "Unsupported type". Register `frozenset` alongside `set` on `ArrayVar`, so a frozen set renders the same way and array props accept it. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01RqJ6TNt8AXzbDVm5kLb2nD --- packages/reflex-base/news/+frozenset-literal.bugfix.md | 1 + packages/reflex-base/src/reflex_base/vars/sequence.py | 2 +- tests/units/test_var.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 packages/reflex-base/news/+frozenset-literal.bugfix.md diff --git a/packages/reflex-base/news/+frozenset-literal.bugfix.md b/packages/reflex-base/news/+frozenset-literal.bugfix.md new file mode 100644 index 00000000000..62353bec1a8 --- /dev/null +++ b/packages/reflex-base/news/+frozenset-literal.bugfix.md @@ -0,0 +1 @@ +`LiteralVar.create` and var props now accept a `frozenset`, rendered as an array like a `set`. diff --git a/packages/reflex-base/src/reflex_base/vars/sequence.py b/packages/reflex-base/src/reflex_base/vars/sequence.py index 1ded6c57ab1..52e2b59873e 100644 --- a/packages/reflex-base/src/reflex_base/vars/sequence.py +++ b/packages/reflex-base/src/reflex_base/vars/sequence.py @@ -63,7 +63,7 @@ VALUE_TYPE = TypeVar("VALUE_TYPE") -class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(Sequence, set)): +class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(Sequence, set, frozenset)): """Base class for immutable array vars.""" def join(self, sep: StringVar | str = "") -> StringVar: diff --git a/tests/units/test_var.py b/tests/units/test_var.py index fe0639d53ac..82832e18fd8 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -436,6 +436,7 @@ def test_datetime_equality_with_other_type_uses_default_comparison(): [ (v([1, 2, 3]), "[1, 2, 3]"), (v({1, 2, 3}), "[1, 2, 3]"), + (v(frozenset({1, 2, 3})), "[1, 2, 3]"), (v(["1", "2", "3"]), '["1", "2", "3"]'), ( Var(_js_expr="foo")._var_set_state("state").to(list), From da9cc7f68ed59d59d779b531794c3da037403033 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 21:24:04 +0000 Subject: [PATCH 2/2] fix(vars): infer a frozenset literal's element type like a set's Registering `frozenset` on `ArrayVar` let a frozen set render, but `figure_out_type` returned the bare class and the array index helper did not know `frozenset[...]`, so a callback over such a literal received an untyped element and `.map(lambda item: item.upper())` raised `UntypedVarError`. Infer `frozenset[T]` like `set[T]` and read the element type out of it when indexing, so `rx.foreach` and array operations over a frozen set are typed. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01RqJ6TNt8AXzbDVm5kLb2nD --- .../reflex-base/news/+frozenset-literal.bugfix.md | 2 +- packages/reflex-base/src/reflex_base/vars/base.py | 9 ++++++--- .../reflex-base/src/reflex_base/vars/sequence.py | 1 + tests/units/test_var.py | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/reflex-base/news/+frozenset-literal.bugfix.md b/packages/reflex-base/news/+frozenset-literal.bugfix.md index 62353bec1a8..e76dd1a0bee 100644 --- a/packages/reflex-base/news/+frozenset-literal.bugfix.md +++ b/packages/reflex-base/news/+frozenset-literal.bugfix.md @@ -1 +1 @@ -`LiteralVar.create` and var props now accept a `frozenset`, rendered as an array like a `set`. +`LiteralVar.create` now accepts a `frozenset`, rendered as an array like a `set`, with its element type inferred so `rx.foreach` and array operations over it are typed. diff --git a/packages/reflex-base/src/reflex_base/vars/base.py b/packages/reflex-base/src/reflex_base/vars/base.py index 0671d80c917..df7f04df6a0 100644 --- a/packages/reflex-base/src/reflex_base/vars/base.py +++ b/packages/reflex-base/src/reflex_base/vars/base.py @@ -2185,7 +2185,7 @@ def figure_out_type(value: Any) -> types.GenericType: Returns: The type of the value. """ - if isinstance(value, (list, set, tuple, Mapping, Var)): + if isinstance(value, (list, set, frozenset, tuple, Mapping, Var)): if isinstance(value, Var): return value._var_type if has_args(value_type := type(value)): @@ -2194,8 +2194,11 @@ def figure_out_type(value: Any) -> types.GenericType: if not value: return Sequence[NoReturn] return Sequence[unionize(*{figure_out_type(v) for v in value[:100]})] - if isinstance(value, set): - return set[unionize(*{figure_out_type(v) for v in value})] + if isinstance(value, (set, frozenset)): + element_type = unionize(*{figure_out_type(v) for v in value}) + if isinstance(value, frozenset): + return frozenset[element_type] + return set[element_type] if isinstance(value, tuple): if not value: return tuple[NoReturn, ...] diff --git a/packages/reflex-base/src/reflex_base/vars/sequence.py b/packages/reflex-base/src/reflex_base/vars/sequence.py index 52e2b59873e..6798fa0cc9c 100644 --- a/packages/reflex-base/src/reflex_base/vars/sequence.py +++ b/packages/reflex-base/src/reflex_base/vars/sequence.py @@ -1920,6 +1920,7 @@ def _determine_value_of_array_index( Iterable, list, set, + frozenset, collections.abc.Sequence, collections.abc.Iterable, ]: diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 82832e18fd8..b7f81370f27 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -51,6 +51,7 @@ ConcatVarOperation, LiteralArrayVar, LiteralStringVar, + StringVar, ) import reflex as rx @@ -421,6 +422,19 @@ def test_date_comparison_uses_compare_datetime(): assert str(lhs == rhs) == '(compareDatetime("2024-01-01", "2024-01-02") === 0)' +def test_frozenset_literal_types_its_elements(): + """A frozen set literal infers its element type like a set does.""" + # The static array bound is Sequence, which no set satisfies; see the PR. + var = cast(ArrayVar, v(frozenset({"a"}))) + + assert var._var_type == frozenset[str] + assert isinstance(var[0], StringVar) + assert re.fullmatch( + r'\["a"\]\.map\(\(\((\w+)\) => \1\.toUpperCase\(\)\)\)', + str(var.map(lambda item: item.upper())), + ) + + def test_datetime_equality_with_other_type_uses_default_comparison(): value = v(datetime(2024, 1, 1)) expected_equality = (