matplotlib / matplotlib/ipympl

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

Abierto
#622 13 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Lenguaje dominante
Jupyter Notebook
Estrellas
1.7k
Forks
234
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

### 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

```Python
%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](https://github.com/user-attachments/assets/fd27ca1f-0d43-40c7-b8f4-cfcd474c9a1e)

```
---------------------------------------------------------------------------
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:

```python
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.

```python
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

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Ejecuta primero la reproducción de JupyterLab proporcionada con el backend de widgets. Después inspecciona matplotlib.pyplot.figure(), al que se llega mediante plt.sca(ax), y los gestores de Gcf descritos en el informe. Se considera completado cuando el trazado repetido de mosaicos termina sin ValueError y pyplot reconoce el gestor de figuras existente.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
jupyter, jupyter-notebook, matplotlib, python
Área
data-visualization, frontend
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bien especificado
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.