feat(vars): add SetVar for set and frozenset vars
FA
1d ago+1537+1537+0123456789012345678901234567890123456789-32-32-01234567890123456789
Closes #7623
Type of change
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
- This change requires a documentation update
Changes To Core Features
What changed
A set-typed state var or literal was an ArrayVar, so it exposed sequence operations a set does not have, and frozenset was rejected by LiteralVar.create. #7622 widened ArrayVar instead and was reverted in #7632.
This adds SetVar / LiteralSetVar in reflex_base/vars/set.py for set[T] and frozenset[T], following the DateTimeVar pattern: a typed var over an untyped wire value.
- Wire format stays a JSON array.
serialize_setis unchanged (andfrozensetgains the same serializer, so it is now a valid state var type) and nothing inapplyDeltachanges. An array also round-trips through event arguments, which a real JSSetwould not (JSON.stringify(new Set(["a"]))is"{}"). The issue's "rehydrate asSeton the frontend" prerequisite is dropped for that reason. - Operations compile to small
pySet*helpers instate.js(next topyFlatMap) that key elements by value, so aset[tuple[int, int]]gets Python answers rather than array-identity comparison:contains/has,length/size,union,intersection,difference,symmetric_difference,issubset,issuperset,isdisjoint, andto_array()which is the JS identity typed asSequence[T](optional sets stay optional). Set-returning operations keepfrozenset-ness and carry the element type through. - Routing.
setis removed fromArrayVar.python_types;figure_out_type,Var.to,var_operation,Field.__get__andComputedVar.__get__routeset/frozensettoSetVar, soState.tags.contains("x")is aBooleanVarstatically with no cast.rx.foreachaccepts aSetVarby callingto_array(). - Deprecation path. Indexing,
map,filter,join,reverse,pluck,reduce,flat_map,+(either side) and*still work on a set var for one release with aconsole.deprecatepointing at.to_array(), and are marked@deprecatedfor type checkers. - A set literal renders its elements sorted when they compare, so compiled output no longer depends on the process hash seed.
LiteralArrayVar's element var-data and JSON helpers are extracted soLiteralSetVarshares them.
Tests
tests/units/reflex_base/vars/test_set.py: literal creation and static types (typing.assert_typeunder pyright), the rendered JS and helper import of every operation, tuple-element sets, operand validation,to_array(including optional sets), the deprecated fallbacks and concatenation in both directions, state fields (set,frozenset, optional) and computed vars,rx.foreach, VarData propagation.tests/integration/tests_playwright/test_set_operations.py: backendadd/discardre-render every operation and aforeach; afrozensetfield; a set of tuples compared by value; an unset optional set renders no children; a set is sent back through an event argument and received intact (as a list, which is existing behavior for event args).- The helper JS was checked in Node against the same cases before being wired in.
uv run ruff check .,uv run ruff format .,uv run pyright reflex tests packages/reflex-base/src/reflex_base/vars packages/reflex-components-core/srcare clean. The unit suite passes apart fromtests/units/reflex_cli/v2, which fails identically onmainin this environment (the hosting CLI rejects the0.0.1.devversion string).
Docs and example apps checked for breakage
docs/enterprise/mantine/pill.mdusestags: set[str]withrx.foreachonly: still works throughto_array().docs/guides/ai_applications.mdusesfrozensetin a backend dataclass only: unaffected.- No docs page indexes or maps a set var, so nothing hits the deprecation.
docs/vars/var-operations.mdgains a "Set Operations" section anddocs/api-reference/var_system.mdlistsSetVar.
Out of scope, as the issue says
- Whether a set var satisfies a
Sequence[...]-typed prop (typehint_issubclass) is unchanged.