Skip to content

feat(vars): add SetVar for set and frozenset vars - #7642

Open
FarhanAliRaza wants to merge 5 commits into
mainfrom
farhan/friendly-hypatia-9dyews
Open

FarhanAliRaza wants to merge 5 commits into
mainfrom
farhan/friendly-hypatia-9dyews

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

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_set is unchanged (and frozenset gains the same serializer, so it is now a valid state var type) and nothing in applyDelta changes. An array also round-trips through event arguments, which a real JS Set would not (JSON.stringify(new Set(["a"])) is "{}"). The issue's "rehydrate as Set on the frontend" prerequisite is dropped for that reason.
  • Operations compile to small pySet* helpers in state.js (next to pyFlatMap) that key elements by value, so a set[tuple[int, int]] gets Python answers rather than array-identity comparison: contains/has, length/size, union, intersection, difference, symmetric_difference, issubset, issuperset, isdisjoint, and to_array() which is the JS identity typed as Sequence[T] (optional sets stay optional). Set-returning operations keep frozenset-ness and carry the element type through.
  • Routing. set is removed from ArrayVar.python_types; figure_out_type, Var.to, var_operation, Field.__get__ and ComputedVar.__get__ route set/frozenset to SetVar, so State.tags.contains("x") is a BooleanVar statically with no cast. rx.foreach accepts a SetVar by calling to_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 a console.deprecate pointing at .to_array(), and are marked @deprecated for 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 so LiteralSetVar shares them.

Tests

  • tests/units/reflex_base/vars/test_set.py: literal creation and static types (typing.assert_type under 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: backend add/discard re-render every operation and a foreach; a frozenset field; 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/src are clean. The unit suite passes apart from tests/units/reflex_cli/v2, which fails identically on main in this environment (the hosting CLI rejects the 0.0.1.dev version string).

Docs and example apps checked for breakage

  • docs/enterprise/mantine/pill.md uses tags: set[str] with rx.foreach only: still works through to_array().
  • docs/guides/ai_applications.md uses frozenset in a backend dataclass only: unaffected.
  • No docs page indexes or maps a set var, so nothing hits the deprecation.
  • docs/vars/var-operations.md gains a "Set Operations" section and docs/api-reference/var_system.md lists SetVar.

Out of scope, as the issue says

  • Whether a set var satisfies a Sequence[...]-typed prop (typehint_issubclass) is unchanged.

https://claude.ai/code/session_01FTaqSdpd95XTrwgxAHExRd

A set-typed state var or literal was an ArrayVar, so it exposed sequence
operations (indexing, map, slicing) that a set does not have, and a
frozenset was rejected by LiteralVar.create outright.

Add SetVar and LiteralSetVar for set[T] and frozenset[T]. The wire value
stays the JSON array that serialize_set already produces, which also
round-trips through event arguments, so no frontend rehydration is
needed: each operation compiles to JavaScript over that array, the way
DateTimeVar compiles over a string. contains, length, union,
intersection, difference, symmetric_difference, issubset, issuperset and
isdisjoint return SetVar or BooleanVar with the element type carried
through; to_array is the JS identity typed as a Sequence, and rx.foreach
calls it.

Remove set from ArrayVar's python types and route set and frozenset to
SetVar in figure_out_type, Var.to, var_operation and the Field and
ComputedVar overloads. The sequence operations still work on a set var
for one release with a deprecation warning pointing at to_array().

A set literal renders its elements sorted when they compare, so the
compiled output no longer depends on the process hash seed.

Closes #7623

Claude-Session: https://claude.ai/code/session_01FTaqSdpd95XTrwgxAHExRd
@FarhanAliRaza
FarhanAliRaza requested review from a team and Alek99 as code owners October 22, 2026 20:09
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T20:12:33.440142Z 4fc60c7 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fc60c7bda

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/reflex-base/src/reflex_base/vars/set.py
Comment thread packages/reflex-base/src/reflex_base/vars/set.py Outdated
@greptile-apps

greptile-apps Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues remain.

Summary

Adds SetVar and LiteralSetVar for Python sets and frozen sets while keeping their frontend values as JSON arrays.

  • Set vars support set operations and keep array-style calls working for now.
  • Set and frozenset values use SetVar types and work with foreach.

Reviews (4) · Last reviewed commit: "docs: record the set var array-operation..." · Reviewed by Greptile

Comment thread packages/reflex-base/src/reflex_base/vars/set.py Outdated
Comment thread packages/reflex-base/src/reflex_base/vars/set.py Outdated
Comment thread packages/reflex-base/src/reflex_base/vars/set.py
Comment thread packages/reflex-base/src/reflex_base/vars/set.py Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 14 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/vars/set.py Outdated
Comment thread packages/reflex-base/src/reflex_base/vars/set.py
Comment thread tests/units/reflex_base/vars/test_set.py Outdated
Comment thread docs/api-reference/var_system.md
Comment thread docs/vars/var-operations.md
Comment thread packages/reflex-base/src/reflex_base/vars/base.py
Comment thread packages/reflex-base/news/+set-var.feature.md Outdated
@codspeed

codspeed Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning

⚠️ 22 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 148 untouched benchmarks


Comparing farhan/friendly-hypatia-9dyews (7374337) with main (aeaac72)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (c43ffc6) during the generation of this report, so aeaac72 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

Address review findings on the SetVar change:

- Move the set operations into pySet* helpers in state.js that key
  elements by value, so a set of tuples (nested arrays on the frontend)
  gets Python answers from contains, union, intersection and the rest
  instead of array-identity comparison.
- Register a serializer for frozenset, so a frozenset state field or
  computed var is valid and reaches the frontend as an array.
- Keep an optional set optional in to_array(), so rx.foreach still
  applies its null guard.
- Let the deprecated `set + set` and `array + set` fallbacks compile by
  viewing set operands as arrays.
- Set the deprecation version to 0.10.1, the next release after v0.10.0.
- Check VarData propagation through a set literal in the tests.

Claude-Session: https://claude.ai/code/session_01FTaqSdpd95XTrwgxAHExRd
Comment thread packages/reflex-base/src/reflex_base/.templates/web/utils/state.js Outdated
Comment thread packages/reflex-base/src/reflex_base/.templates/web/utils/state.js Outdated
symmetric_difference with a plain array operand kept that array's
duplicates; build it as a union so the result is a set. Key strings and
objects with different prefixes in pySetKey, so a string can never share
a key with a tuple's JSON.

Claude-Session: https://claude.ai/code/session_01FTaqSdpd95XTrwgxAHExRd
@masenf masenf added the breaking Public API facing change considered for minor release bump pre-v1.0 label Oct 22, 2026
@masenf

masenf commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

marking as a [breaking] change, because before you could index into set-typed state vars in the frontend code. not sure if that was ever "correct", but it's a behavior change we need to expose prominently

Copy link
Copy Markdown
Contributor Author

Agreed. 7374337 moves the changelog fragment for this to the breaking type (in reflex-base and mirrored in the root reflex news), stating that a set var is no longer a sequence in frontend code and that indexing, map, join, + and the rest still compile with a deprecation warning until 1.0, with .to_array() as the replacement.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Public API facing change considered for minor release bump pre-v1.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a SetVar backed by the JS Set object

2 participants