Fast renderer: force_update() sets widgets back to their element values - #69
Closed
maartenbreddels wants to merge 3 commits into
Closed
maartenbreddels wants to merge 3 commits into
maartenbreddels wants to merge 3 commits into
Conversation
With the default renderer, a forced full walk (force_update(), update(), an explicit rc.render(...) or the first render) applies every widget element's kwargs again, so a trait changed from the frontend or from Python goes back to the element's value. The fast renderer walked the whole tree in that case, but still skipped the widget update for an element that was the same object as last time, so the changed value stayed. Code that calls force_update() to resync widgets did not work. A forced walk now also forces the widget updates in the reconcile phase. Renders triggered by state changes are not forced, so they keep skipping unchanged subtrees and widgets. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review found three problems in the first version: - render() got a keyword argument, so code that patches or overrides render(self, element, container=None) failed on every state change. - A forced render asked for its walk before taking the render lock, so the end of a render on another thread could undo it. - A forced render that raised before reconciliation left the forced flag on, so the next state-triggered render also applied all kwargs again. A render caused by a state change is now marked per thread instead, the walk is requested under the lock, and the flag is cleared when render() ends. Two tests cover the lost request and the flag after a failure. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A child component with key "" reconciles its root element with the same keys as the root element of the render context. The forced flag was cleared there, so widgets after that child were not updated. Only the root context now clears it. The test for a forced render that waits for a state render on another thread now waits until that render really blocks on the lock, instead of sleeping, so it cannot pass without testing the race. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
maartenbreddels
force-pushed
the
fix/fast-force-update-resyncs-widgets
branch
from
September 29, 2026 09:33
de48208 to
c10f2da
Compare
Contributor
Author
|
Closing this. It works, and the crossreview approved it, but the benefit is too small for the risk and the cost.
Instead, we will document this as a known difference in The branch |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the audit of
REACTON_FAST=1against the default renderer. This is one small PR per finding; this one is D1c.Problem
With the default renderer, a forced full walk applies every widget element's kwargs again. A forced walk is
force_update(),update(), an explicitrc.render(...), or the first render. So a trait changed from the frontend, or from Python, goes back to the element's value. The fast renderer did walk the whole tree in that case, but it still skipped the widget update when the element was the same object as last time. So the changed value stayed, and code that callsforce_update()to resync widgets did not work. The README claimed force_update was "faithful to the old behavior"; this was the gap.Fix
_reconsolidate_forced_walk)._possible_rerendermarks a state-triggered render with a per-thread flag.render()keeps its public signature, so code that patches or overridesrender(self, element, container=None)keeps working.render()clears the forced flag in itsfinally, so a forced render that raised does not force the next state render.Tests
Six new tests. Each one fails on the code it guards against and passes in both modes:
force_update()andrc.render(rc.element)set out-of-band values back.force_update()is not forced.""does not end the forced walk early.Full suite: 213 passed (default), 216 passed (fast). The thread test passed 30 of 30 runs.
Review
Crossreview with Astra (GPT-6), Opus and GLM. All three approve.
render()keyword broke code that patchesrender()."". The fix (a root-context check) and the synchronized thread test were then confirmed by Astra as a small fix.Left out on purpose, because the problems existed before this PR:
update()called while a render is running is not forced.force_update()from another thread while a render is running is dropped.The first version was written by a codex worker; the review fixes are mine.
🤖 Generated with Claude Code