Skip to content

Close anywidget comms on cell re-execution and deletion#8159

Merged
manzt merged 1 commit intomainfrom
push-zsokqyzwnkyo
Feb 6, 2026
Merged

Close anywidget comms on cell re-execution and deletion#8159
manzt merged 1 commit intomainfrom
push-zsokqyzwnkyo

Conversation

@manzt
Copy link
Collaborator

@manzt manzt commented Feb 6, 2026

When a cell containing an anywidget is re-executed or deleted, the runtime invalidates cell state and removes UI elements, but no ModelClose message was ever sent to the frontend.

The MarimoComm.__del__ fallback never fires because MarimoCommManager.comms holds a strong reference to every comm, preventing garbage collection. This leaks frontend objects along with their event listeners.

These changes adds a CommLifecycleItem that registers with the cell lifecycle registry when a widget comm is created. When the cell is invalidated (re-execution, deletion, or error), dispose() calls comm.close(), which broadcasts ModelClose to the frontend and unregisters from the comm manager.

@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Feb 6, 2026 7:32pm

Request Review

@manzt manzt added the bug Something isn't working label Feb 6, 2026
mscolnick
mscolnick previously approved these changes Feb 6, 2026
When a cell containing an anywidget is re-executed or deleted, the
runtime invalidates cell state and removes UI elements, but no
ModelClose message was ever sent to the frontend.

The `MarimoComm.__del__` fallback never fires because
`MarimoCommManager.comms` holds a strong reference to every comm,
preventing garbage collection. This leaks frontend objects along with
their event listeners.

These changes adds a `CommLifecycleItem` that registers with the cell
lifecycle registry when a widget comm is created. When the cell is
invalidated (re-execution, deletion, or error), `dispose()` calls
`comm.close()`, which broadcasts ModelClose to the frontend and
unregisters from the comm manager.
@manzt manzt merged commit 04059d6 into main Feb 6, 2026
41 of 43 checks passed
@manzt manzt deleted the push-zsokqyzwnkyo branch February 6, 2026 21:57
manzt added a commit that referenced this pull request Feb 6, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
manzt added a commit that referenced this pull request Feb 8, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
manzt added a commit that referenced this pull request Feb 9, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
manzt added a commit that referenced this pull request Feb 10, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
manzt added a commit that referenced this pull request Feb 10, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
manzt added a commit that referenced this pull request Feb 10, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
manzt added a commit that referenced this pull request Feb 10, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
manzt added a commit that referenced this pull request Feb 10, 2026
The anywidget refactors in #8156, #8159, and #8163 separated model
lifecycle from widget binding and moved to a dedicated ModelCommand for
frontend-to-backend communication.

However, unlike UpdateUIElementCommand which goes through
SetUIElementRequestManager's drain-and-merge path, ModelCommand was
processed immediately — each rapid model update (e.g. dragging a map
widget) triggered an individual cell re-execution.

These changes puts `ModelCommand` on the same shared queue as
`UpdateUIElementCommand` so both go through the same batching pipeline.
When multiple model updates arrive in quick succession, they are now
drained and merged (last-write-wins per model ID on state keys),
matching the existing UI element behavior.

The handler for model messages also now enqueues the resulting
`UpdateUIElementCommand` back through the control queue instead of
calling `set_ui_element_value` directly, so the downstream cell
re-execution also benefits from batching.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

2 participants