fix(mpl): disconnect toolbar callbacks on cell rerun#9531
Merged
Conversation
The figure-shared canvas callback registry kept stale toolbar handlers from previous mpl.interactive instances, causing a duplicate end_pan that crashed the kernel.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Member
Author
Contributor
|
@kirangadhave I have started the AI code review. It will take a few minutes to complete. |
Contributor
There was a problem hiding this comment.
No issues found across 2 files
Architecture diagram
sequenceDiagram
participant Cell as Marimo Cell Runtime
participant MplUI as mpl_interactive UIElement
participant Handle as _MplCleanupHandle
participant Canvas as FigureCanvasWebAgg
participant Reg as figure._canvas_callbacks
participant Toolbar as NavigationToolbar2WebAgg
participant WS as WebSocket
Note over Cell,WS: Cell rerun lifecycle — shared figure event registry
Cell->>MplUI: Create (first run)
MplUI->>Canvas: get canvas
Canvas->>Reg: callback registry (shared per figure)
MplUI->>Toolbar: __init__ (register _zoom_pan_handler)
Toolbar->>Reg: register "button_press_event" handler
Toolbar->>Reg: register "button_release_event" handler
MplUI->>WS: open WebSocket
Note over Cell,WS: Cell rerun — dispose old toolbar, then create new
Cell->>Handle: dispose()
Handle->>Canvas: get toolbar reference
Handle->>Handle: _disconnect_owner_callbacks(canvas, toolbar)
Handle->>Canvas: access callbacks property
Canvas->>Reg: return shared registry
Handle->>Reg: iterate registered handlers
Handle->>Reg: match handlers owned by old toolbar
Handle->>Reg: disconnect(cid) for each match
Reg-->>Handle: handlers removed
Handle->>WS: remove_web_socket()
WS-->>Handle: disconnected
Handle->>Canvas: restore original figure DPI/size
Handle->>MplUI: close comm
Cell->>MplUI: Create new instance (rerun)
MplUI->>Canvas: get canvas (same figure)
Canvas->>Reg: shared registry (old handlers now removed)
MplUI->>Toolbar: __init__ (register _zoom_pan_handler)
Toolbar->>Reg: register "button_press_event" handler
Toolbar->>Reg: register "button_release_event" handler
Reg-->>Toolbar: only one handler pair registered
Note over Cell,Reg: Result: single handler, no duplicate dispatch
alt Error during disconnect
Handle->>Handle: catch Exception
Handle->>Handle: LOGGER.exception(...)
end
Contributor
There was a problem hiding this comment.
1 issue found across 2 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="tests/_plugins/ui/_impl/test_from_mpl_interactive.py">
<violation number="1" location="tests/_plugins/ui/_impl/test_from_mpl_interactive.py:310">
P2: Avoid patching `MagicMock`'s class here; it leaks the failing `callbacks` property into later tests.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Cell as Marimo Cell Runtime
participant MplUI as mpl_interactive UIElement
participant Handle as _MplCleanupHandle
participant Canvas as FigureCanvasWebAgg
participant Reg as figure._canvas_callbacks
participant Toolbar as NavigationToolbar2WebAgg
participant WS as WebSocket
Note over Cell,WS: Cell rerun lifecycle — shared figure event registry
Cell->>MplUI: Create (first run)
MplUI->>Canvas: get canvas
Canvas->>Reg: callback registry (shared per figure)
MplUI->>Toolbar: __init__ (register _zoom_pan_handler)
Toolbar->>Reg: register "button_press_event" handler
Toolbar->>Reg: register "button_release_event" handler
MplUI->>WS: open WebSocket
Note over Cell,WS: Cell rerun — dispose old toolbar, then create new
Cell->>Handle: dispose()
Handle->>Canvas: get toolbar reference
Handle->>Handle: _disconnect_owner_callbacks(canvas, toolbar)
Handle->>Canvas: access callbacks property
Canvas->>Reg: return shared registry
Handle->>Reg: iterate registered handlers
Handle->>Reg: match handlers owned by old toolbar
Handle->>Reg: disconnect(cid) for each match
Reg-->>Handle: handlers removed
Handle->>WS: remove_web_socket()
WS-->>Handle: disconnected
Handle->>Canvas: restore original figure DPI/size
Handle->>MplUI: close comm
Cell->>MplUI: Create new instance (rerun)
MplUI->>Canvas: get canvas (same figure)
Canvas->>Reg: shared registry (old handlers now removed)
MplUI->>Toolbar: __init__ (register _zoom_pan_handler)
Toolbar->>Reg: register "button_press_event" handler
Toolbar->>Reg: register "button_release_event" handler
Reg-->>Toolbar: only one handler pair registered
Note over Cell,Reg: Result: single handler, no duplicate dispatch
alt Error during disconnect
Handle->>Handle: catch Exception
Handle->>Handle: LOGGER.exception(...)
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
mscolnick
approved these changes
May 12, 2026
|
🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.7-dev11 |
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.
Summary
Fixes #9514.
FigureCanvasBase.callbacksis a property delegating tofigure._canvas_callbacks, so canvases sharing a figure share one event registry. Eachmpl.interactivererun left the old toolbar's_zoom_pan_handleron the registry, causing duplicate dispatch and a secondAxes.end_pantodelan already-deleted_pan_start.The fix disconnects the old toolbar's handlers in
_MplCleanupHandle.dispose. Also drops the long-brokencanvas.close()(no such method onFigureCanvasWebAgg) and replaces silent excepts withLOGGER.exception.Another issue that surfaced was errors thrown in
mpl_interactivebubbles up and kills the kernel, requiring a restart. This would likely apply to any comm callback whose handler can raise. I am tracking a fix for that in separately.Test plan
figure._canvas_callbacksafter dispose + re-instantiate_MplCleanupHandletests updated to match the new dispose flowmpl.interactiveraise internal error after move and zoom. #9514 (pan → rerun cell → zoom → pan); kernel survives