Skip to content

Data-bound chart components 7/7: demo app on the new API - #467

Open
FarhanAliRaza wants to merge 1 commit into
stack/6-compile-probefrom
stack/7-demos-and-gallery
Open

Data-bound chart components 7/7: demo app on the new API#467
FarhanAliRaza wants to merge 1 commit into
stack/6-compile-probefrom
stack/7-demos-and-gallery

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Stacked on #466. Base is stack/6-compile-probe. Top of the stack — check this branch out to see the whole feature.

Change

Ports examples/reflex to the tier the stack built, so the showcase is the API we recommend rather than the one it replaces:

  • a composed 1M drillable scatter with hover/click/select;
  • an on_view_change data var republishing in-view columns into a fixed histogram plan;
  • a flat scatter whose slider republishes columns under a stable handle;
  • an rx.cond toggle between a composed board and rx.foreach small multiples over a list[DataHandle] var.

The escape hatch stays represented on purpose. The cross-filtered histogram whose structure reads state keeps @reflex_xy.figure, alongside both fixed-data tiers and the FastAPI cross-host A/B — so the example shows where each tier is the right answer rather than implying the new one subsumes them.

reflex_ws_smoke.py drives the ported app; test_example_apps.py compiles it.

Spec

reflex-integration.md file map (examples/reflex inventory).

Test plan

  • uv run pytest tests/reflex_adapter tests/test_validation_timing.py tests/test_example_apps.py — 250 passed, 1 skipped
  • pre-commit run --all-files, ruff check, ruff format --check, ty check — clean
  • Browser E2E (scripts/reflex_ws_smoke.py) against the running demo not re-run in this session — worth doing before merge.

Review in cubic

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b4c3a9d-a1e3-46a1-a236-c16dc1a247e1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

The PR ports the Reflex showcase to the new data-bound chart API while retaining the figure-var escape hatch and fixed-data tiers.

  • Adds composed and flat data-bound charts backed by typed @reflex_xy.data methods.
  • Demonstrates view-driven republishing, stable handles, and conditional rx.foreach chart mounting.
  • Updates the browser smoke probe, compile tests, documentation, and integration specification for the new examples.

Confidence Score: 4/5

The PR should not merge until the outstanding unbounded allocation path in the bound-scatter event handler is fixed.

A client-controlled event value is still stored without server-side bounds validation and then used as the size of multiple NumPy arrays, so a crafted event can exhaust memory or terminate the backend worker.

Files Needing Attention: examples/reflex/xy_reflex_demo/xy_reflex_demo.py

Important Files Changed

Filename Overview
examples/reflex/xy_reflex_demo/xy_reflex_demo.py Ports the demo to typed data-bound charts and adds stable-handle and conditional/foreach examples; the previously reported unbounded allocation path remains.
scripts/reflex_ws_smoke.py Updates expected chart subscriptions and adds a conditional/foreach mounting check.
tests/test_example_apps.py Updates source and composition assertions for the new data-bound demo API.
spec/design/reflex-integration.md Documents client-side composite-token assembly, bounded resynchronization, and the revised Reflex example inventory.
README.md Adds a state-driven chart example using a typed data method.
examples/reflex/README.md Revises the showcase guide and interaction checks for the data-bound API.

Reviews (2): Last reviewed commit: "docs(reflex): move the demo app onto the..." | Re-trigger Greptile

Comment on lines +456 to +458
@rx.event
def set_bound_points(self, value: list[int | float]):
self.bound_points = int(value[0])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security Unbounded event-controlled array allocation

When a client submits a crafted set_bound_points event above the slider's displayed maximum, the handler stores it without server-side validation and bound_cloud uses it to allocate several full-length NumPy arrays, causing excessive CPU and memory consumption or terminating the backend worker through OOM.

How this was verified: The client-supplied value flows directly from the event handler into multiple NumPy allocation sizes without an intervening server-side bound.

Suggested change
@rx.event
def set_bound_points(self, value: list[int | float]):
self.bound_points = int(value[0])
@rx.event
def set_bound_points(self, value: list[int | float]):
self.bound_points = max(10_000, min(1_000_000, int(value[0])))

Knowledge Base Used: reflex-xy: the Reflex integration package

@codspeed-hq

codspeed-hq Bot commented Aug 5, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 109 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing stack/7-demos-and-gallery (f48df99) with stack/6-compile-probe (1411f82)2

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on stack/6-compile-probe (2641bf4) during the generation of this report, so 99e4327 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@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.

2 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="examples/reflex/xy_reflex_demo/xy_reflex_demo.py">

<violation number="1" location="examples/reflex/xy_reflex_demo/xy_reflex_demo.py:3">
P3: The module docstring reads 'One page of eight sections', but the page actually renders nine sections (§1–§9; this PR added §8 bound_view and §9 cond_foreach_view). The in-file list right below the claim already enumerates nine items, so the leading count is inconsistent with both the contents and the rendered page. Suggest updating 'eight' to 'nine'.</violation>

<violation number="2" location="examples/reflex/xy_reflex_demo/xy_reflex_demo.py:457">
P2: `set_bound_points` stores the client-supplied value directly into `bound_points` with no server-side bound check, and `bound_cloud` uses that value as the allocation size for several full NumPy arrays (`rng.normal(size=self.bound_points)`, etc.). Since Reflex event payloads aren't constrained to the slider's displayed min/max, a crafted event could request an arbitrarily large point count and cause excessive memory/CPU usage. Consider clamping `value[0]` to the slider's valid range before assigning to `bound_points`.</violation>
</file>

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

Fix all with cubic | Re-trigger cubic

return {"x": x, "y": y, "mag": np.hypot(x, y)}

@rx.event
def set_bound_points(self, value: list[int | float]):

@cubic-dev-ai cubic-dev-ai Bot Aug 5, 2026

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.

P2: set_bound_points stores the client-supplied value directly into bound_points with no server-side bound check, and bound_cloud uses that value as the allocation size for several full NumPy arrays (rng.normal(size=self.bound_points), etc.). Since Reflex event payloads aren't constrained to the slider's displayed min/max, a crafted event could request an arbitrarily large point count and cause excessive memory/CPU usage. Consider clamping value[0] to the slider's valid range before assigning to bound_points.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/reflex/xy_reflex_demo/xy_reflex_demo.py, line 457:

<comment>`set_bound_points` stores the client-supplied value directly into `bound_points` with no server-side bound check, and `bound_cloud` uses that value as the allocation size for several full NumPy arrays (`rng.normal(size=self.bound_points)`, etc.). Since Reflex event payloads aren't constrained to the slider's displayed min/max, a crafted event could request an arbitrarily large point count and cause excessive memory/CPU usage. Consider clamping `value[0]` to the slider's valid range before assigning to `bound_points`.</comment>

<file context>
@@ -419,6 +441,54 @@ def on_view(self, event: reflex_xy.ViewChangeEvent):
+        return {"x": x, "y": y, "mag": np.hypot(x, y)}
+
+    @rx.event
+    def set_bound_points(self, value: list[int | float]):
+        self.bound_points = int(value[0])
+
</file context>
Fix with cubic

1. **Live figure var + events.** A 1M-point drillable scatter from an
``@reflex_xy.figure`` state method; its data rides the app websocket while
Reflex state holds only the token. Hover, click, and box-select arrive as
One page of eight sections; each has a "Code" accordion showing its own source

@cubic-dev-ai cubic-dev-ai Bot Aug 5, 2026

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.

P3: The module docstring reads 'One page of eight sections', but the page actually renders nine sections (§1–§9; this PR added §8 bound_view and §9 cond_foreach_view). The in-file list right below the claim already enumerates nine items, so the leading count is inconsistent with both the contents and the rendered page. Suggest updating 'eight' to 'nine'.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/reflex/xy_reflex_demo/xy_reflex_demo.py, line 3:

<comment>The module docstring reads 'One page of eight sections', but the page actually renders nine sections (§1–§9; this PR added §8 bound_view and §9 cond_foreach_view). The in-file list right below the claim already enumerates nine items, so the leading count is inconsistent with both the contents and the rendered page. Suggest updating 'eight' to 'nine'.</comment>

<file context>
@@ -1,22 +1,31 @@
-1. **Live figure var + events.** A 1M-point drillable scatter from an
-   ``@reflex_xy.figure`` state method; its data rides the app websocket while
-   Reflex state holds only the token. Hover, click, and box-select arrive as
+One page of eight sections; each has a "Code" accordion showing its own source
+via `inspect.getsource`. Charts use the data-bound component API — structure
+declared in the page, compiled to a validated plan at ``reflex run``, columns
</file context>
Suggested change
One page of eight sections; each has a "Code" accordion showing its own source
One page of nine sections; each has a "Code" accordion showing its own source
Fix with cubic

Ports examples/reflex to the tier the stack built, so the showcase is the
API we recommend rather than the one it replaces: a composed 1M drillable
scatter, an on_view_change data var republishing in-view columns into a
fixed histogram plan, a flat scatter whose slider republishes columns under
a stable handle, and an rx.cond toggle between a composed board and
rx.foreach small multiples over a list[DataHandle] var.

The escape hatch stays represented on purpose — the cross-filtered
histogram whose *structure* reads state keeps @reflex_xy.figure — alongside
both fixed-data tiers and the FastAPI cross-host A/B, so the example shows
where each tier is the right answer rather than implying the new one
subsumes them.

reflex_ws_smoke.py drives the ported app; test_example_apps.py compiles it.

Spec: reflex-integration.md file map (examples/reflex inventory).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant