Skip to content

fix(mpl): disconnect toolbar callbacks on cell rerun#9531

Merged
kirangadhave merged 1 commit into
mainfrom
kg/mpl-interactive-pan-error
May 12, 2026
Merged

fix(mpl): disconnect toolbar callbacks on cell rerun#9531
kirangadhave merged 1 commit into
mainfrom
kg/mpl-interactive-pan-error

Conversation

@kirangadhave
Copy link
Copy Markdown
Member

@kirangadhave kirangadhave commented May 12, 2026

Summary

Fixes #9514. FigureCanvasBase.callbacks is a property delegating to figure._canvas_callbacks, so canvases sharing a figure share one event registry. Each mpl.interactive rerun left the old toolbar's _zoom_pan_handler on the registry, causing duplicate dispatch and a second Axes.end_pan to del an already-deleted _pan_start.

The fix disconnects the old toolbar's handlers in _MplCleanupHandle.dispose. Also drops the long-broken canvas.close() (no such method on FigureCanvasWebAgg) and replaces silent excepts with LOGGER.exception.


Another issue that surfaced was errors thrown in mpl_interactive bubbles 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

  • New unit test asserts a single toolbar handler remains on figure._canvas_callbacks after dispose + re-instantiate
  • Existing _MplCleanupHandle tests updated to match the new dispose flow
  • Manual: 4-step gesture from mpl.interactive raise internal error after move and zoom. #9514 (pan → rerun cell → zoom → pan); kernel survives

The figure-shared canvas callback registry kept stale toolbar handlers from previous mpl.interactive instances, causing a duplicate end_pan that crashed the kernel.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 12, 2026

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

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment May 12, 2026 10:08pm

Request Review

@kirangadhave kirangadhave requested a review from mscolnick May 12, 2026 22:11
@kirangadhave kirangadhave marked this pull request as ready for review May 12, 2026 22:11
@kirangadhave kirangadhave added the bug Something isn't working label May 12, 2026
@kirangadhave
Copy link
Copy Markdown
Member Author

@cubic-dev-ai

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 12, 2026

@cubic-dev-ai

@kirangadhave I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

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
Loading

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

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
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread tests/_plugins/ui/_impl/test_from_mpl_interactive.py
@kirangadhave kirangadhave merged commit fb38a25 into main May 12, 2026
50 of 52 checks passed
@kirangadhave kirangadhave deleted the kg/mpl-interactive-pan-error branch May 12, 2026 22:37
@github-actions
Copy link
Copy Markdown

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.7-dev11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mpl.interactive raise internal error after move and zoom.

2 participants