Data-bound chart components 3/8: plans and @reflex_xy.data - #463
Data-bound chart components 3/8: plans and @reflex_xy.data#463FarhanAliRaza wants to merge 3 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Greptile SummaryThis PR introduces the server-side foundations for data-bound Reflex charts.
Confidence Score: 5/5The PR appears safe to merge based on the eligible follow-up findings. No blocking failure remains in the eligible follow-up review scope.
|
| Filename | Overview |
|---|---|
| python/reflex_xy/data_vars.py | Adds typed data computed variables, column validation, deterministic token minting, publication, release semantics, and async dispatch. |
| python/reflex_xy/plan.py | Adds immutable content-addressed chart plans with zero-row validation, recorded column bindings, and fresh-chart binding. |
| python/reflex_xy/registry.py | Adds versioned column entries, dependent-plan indexing and rebuilding, generation ordering guards, error delivery, and TTL sweeping. |
| python/reflex_xy/tokens.py | Adds data-token and composite-plan-token grammars while preserving deterministic state-token parsing. |
| python/reflex_xy/init.py | Exposes the new data-variable decorator and computed-variable types through the public package API. |
| spec/design/reflex-integration.md | Documents data variables, plans, composite identities, column-entry lifecycle, ordering, and format stability. |
| tests/reflex_adapter/test_data_var.py | Covers typed schema propagation, dependency tracking, publication, release, generation ordering, validation, and TTL behavior. |
| tests/reflex_adapter/test_plan.py | Covers plan validation, stable addressing, immutable snapshots, fresh binding, diagnostics, and Tailwind inventory collection. |
Reviews (3): Last reviewed commit: "fix(reflex): generation-gated column rep..." | Re-trigger Greptile
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
All reported issues were addressed across 8 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
4b1f770 to
9fd4362
Compare
|
Review addressed in 9fd4362:
Spec: reflex-integration.md "Republish ordering" / "Plan immutability". |
The two halves the data-bound component tier is made of, with no wire or
component surface yet.
plan.py — a ChartPlan is a validated, data-free chart structure. Building
one constructs the real xy tree from string channels, binds zero-row
placeholder columns for every referenced channel through the production
resolution path (a recording table, so the column list cannot drift from
what binding will look up), and runs .figure() once: the full mark/config
validation gate, in milliseconds, with no data ingestion. The canonical
JSON (plan_version: 1) is content-addressed into a digest and registered in
a process-local map; binding is columns + plan -> a fresh Chart -> figure,
with mismatch errors that name both sides. PLAN_VERSION and a golden digest
are pinned so accidental format churn fails a test rather than silently
invalidating every mounted client's digest.
data_vars.py — @reflex_xy.data is the exact sibling of @reflex_xy.figure:
same builder-targeted dependency tracking, pre-session short-circuit,
underscore refusal, async dispatch, and None-releases semantics, but its
value is a DataHandle and what it publishes is columns. The method's return
annotation is the schema channel: a TypedDict parametrizes the handle, so
the layer above can read column names without executing user code.
tokens.py grows two families beside xyv1: xyd1 for data tokens (same
grammar and rebuild contract) and the composite xyp1|<digest>|<data token>
that names a bound figure — plan digest for the structure, data token for
the columns, both halves independently recoverable.
registry.py stores column entries as their own kind: immutable generations
with no locks and no pins (a republish replaces the whole entry), swept
under the existing TTL because the data method is the rebuild recipe. The
data-token -> {digest} index and the on_error seam land here; the namespace
wires them in the next change.
Spec: reflex-integration.md §3.6 (data vars, plans, column entries, format
stability), file map.
Two review P-findings on the plan/data tier: - publish_columns rebuilds run outside the registry mutex, so two republishes of one data token could finish in reverse order and leave subscribers on stale pixels. Each rebuild now carries its ColumnEntry generation and every outcome (figure publish, failure release + err frame) is gated on that generation still being current, atomically with the registry mutation (publish's locked body factored into _publish_locked for that). - build_plan hashes and registers a deep snapshot of children and chart props: mutating a reused mark node after the factory call can no longer change binding behavior behind an unchanged digest.
data_vars.py duplicated vars.py's _builder_target verbatim; a fix landing in one copy would silently miss the other. The data tier now imports the figure tier's helper — one body for the one behavior, matching the 'exact sibling' contract the module documents.
9fd4362 to
794dae6
Compare
There was a problem hiding this comment.
Greptile has paused reviews on this repository — it used its 100 free open-source review credits for this billing period. Reviews resume automatically on September 3. To continue before then, an organization admin can keep reviews running past the free credits — those bill as normal usage.
Stacked on #462. Base is
stack/2-typed-handles.The two halves the data-bound tier is made of. No wire and no component surface yet — those are PRs 4 and 5.
plan.pyA
ChartPlanis a validated, data-free chart structure. Building one constructs the real xy tree from string channels, binds zero-row placeholder columns for every referenced channel through the production resolution path (a recording table, so the column list cannot drift from what binding will look up), and runs.figure()once — the full mark/config validation gate, in milliseconds, with no data ingestion.The canonical JSON (
plan_version: 1) is content-addressed into a digest and registered in a process-local map. Binding is the reverse: columns + plan → a freshChart(never reused) →.figure(), with mismatch errors that name both sides.PLAN_VERSIONand a golden digest are pinned, so accidental format churn fails a test rather than silently invalidating every mounted client's digest.data_vars.py@reflex_xy.datais the exact sibling of@reflex_xy.figure— same builder-targeted dependency tracking, pre-session short-circuit, underscore refusal, async dispatch,None-releases semantics — but its value is aDataHandleand what it publishes is columns.The method's return annotation is the schema channel: a
TypedDictparametrizes the handle, so PR5's factories can read column names without executing user code.tokens.py,registry.pyTwo families join
xyv1:xyd1for data tokens (same grammar and rebuild contract) and the compositexyp1|<digest>|<data token>naming a bound figure — plan digest for structure, data token for columns, both halves independently recoverable.Column entries are their own kind of registry entry: immutable generations with no locks and no pins (a republish replaces the whole entry), swept under the existing TTL because the data method is the rebuild recipe. The
data token → {digest}index and theon_errorseam land here; PR4 wires them to the namespace.Spec
reflex-integration.md§3.6 (data vars, plans, column entries, format stability), file map.Test plan
uv run pytest tests/reflex_adapter tests/test_validation_timing.py— 200 passedpre-commit run --all-files,ruff check,ruff format --check,ty check— clean