[Bug]: Intermittent `ValueError: The passed figure is not managed by pyplot` with ipympl (`%matplotlib widget`)

Aperta
#622 13 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
35/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Ferma
Stack tecnologico
jupyter, jupyter-notebook, matplotlib, python

Direzione di ricerca

Esegui prima la riproduzione JupyterLab fornita con il backend dei widget. Poi esamina matplotlib.pyplot.figure(), raggiunto tramite plt.sca(ax), e i gestori Gcf descritti nel report. Il lavoro è completato quando il plotting ripetuto dei mosaici termina senza ValueError e pyplot riconosce il gestore della figura esistente.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Bug summary

When using Matplotlib with ipympl (%matplotlib widget) in JupyterLab, I intermittently get:

ValueError: The passed figure is not managed by pyplot

This happens with standard pyplot usage (plt.figure, plt.subplots(num=fig), plt.sca(ax)), without explicitly constructing canvases.

Code for reproduction
%matplotlib widget
import time
import matplotlib.pyplot as plt

def try_once():
    fig_num = 1
    plt.close(fig_num)
    fig = plt.figure(fig_num)

    fig, axs = plt.subplots(10, 10, num=fig, squeeze=False)

    for ax in axs.flat:
        plt.sca(ax)  # <-- intermittently raises
        plt.plot([1, 2, 3])
        fig.canvas.draw_idle()
        time.sleep(0.001)

for i in range(300):
    try:
        try_once()
    except Exception as e:
        print('FAILED at iteration', i, '->', repr(e))
        raise
Actual outcome

Image

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[2], line 20
     18 for i in range(300):
     19     try:
---> 20         try_once()
     21     except Exception as e:
     22         print('FAILED at iteration', i, '->', repr(e))

Cell In[2], line 13, in try_once()
     10 fig, axs = plt.subplots(10, 10, num=fig, squeeze=False)
     12 for ax in axs.flat:
---> 13     plt.sca(ax)  # <-- intermittently raises
     14     plt.plot([1, 2, 3])
     15     fig.canvas.draw_idle()

File ~/ipympl-debug/issue/.venv/lib/python3.12/site-packages/matplotlib/pyplot.py:1376, in sca(ax)
   1372 # Mypy sees ax.figure as potentially None,
   1373 # but if you are calling this, it won't be None
   1374 # Additionally the slight difference between `Figure` and `FigureBase` mypy catches
   1375 fig = ax.get_figure(root=False)
-> 1376 figure(fig)  # type: ignore[arg-type]
   1377 fig.sca(ax)

File ~/ipympl-debug/issue/.venv/lib/python3.12/site-packages/matplotlib/pyplot.py:995, in figure(num, figsize, dpi, facecolor, edgecolor, frameon, FigureClass, clear, **kwargs)
    993 root_fig = num.get_figure(root=True)
    994 if root_fig.canvas.manager is None:
--> 995     raise ValueError("The passed figure is not managed by pyplot")
    996 elif (any(param is not None for param in [figsize, dpi, facecolor, edgecolor])
    997       or not frameon or kwargs) and root_fig.canvas.manager.num in allnums:
    998     _api.warn_external(
    999         "Ignoring specified arguments in this call because figure "
   1000         f"with num: {root_fig.canvas.manager.num} already exists")

ValueError: The passed figure is not managed by pyplot
Expected outcome

Mosaicked plots will be shown without errors.

Additional information
Observed state at failure

At the moment the exception occurs:

  • root_fig.canvas.manager is None
  • but the corresponding FigureManager still exists in matplotlib._pylab_helpers.Gcf (so pyplot still manages the figure)

So pyplot’s “managed figure” check is making a false negative.

Where the exception comes from

plt.sca(ax) calls pyplot.figure(fig) internally.
When passed a Figure, pyplot.figure() currently checks only:

root_fig = num.get_figure(root=True)
if root_fig.canvas.manager is None:
    raise ValueError("The passed figure is not managed by pyplot")

In this bug, canvas.manager is temporarily/incorrectly None even though the manager still exists in Gcf.

Proposed fix

If root_fig.canvas.manager is None, fall back to a Gcf lookup to find the manager whose m.canvas.figure is root_fig, and reattach it.

manager = root_fig.canvas.manager
if manager is None:
    for m in _pylab_helpers.Gcf.get_all_fig_managers():
        if getattr(getattr(m, "canvas", None), "figure", None) is root_fig:
            manager = m
            root_fig.canvas.manager = m
            break
if manager is None:
    raise ValueError(...)

This same logic eliminates the failures in my environment (0/60) when applied as a small monkey patch around pyplot.figure().

This resembles matplotlib/matplotlib#19380 (same structural cause: FigureCanvasBase.__init__ sets self.manager=None after replacing figure.canvas).

Operating system

Ubuntu 24

Matplotlib Version

3.10.8

ipympl Version

0.10.0

Matplotlib Backend

widget

Python version

Python 3.12.3

Jupyter version

jupyter lab --version: 4.5.4

Installation

uv

Lingua principale
Jupyter Notebook
Stelle
1.7k
Fork
234
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di matplotlib/ipympl

Tutte le issue di matplotlib/ipympl

Issue simili

Altre issue su Data Visualization

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.