matplotlib / matplotlib/ipympl
[Bug]: Intermittent `ValueError: The passed figure is not managed by pyplot` with ipympl (`%matplotlib widget`)
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Jupyter Notebook
- Star
- 1.7k
- Fork
- 234
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Mô tả
### 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

```
---------------------------------------------------------------------------
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
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Trước tiên, hãy chạy bản tái hiện JupyterLab được cung cấp với widget backend. Sau đó kiểm tra matplotlib.pyplot.figure(), được truy cập thông qua plt.sca(ax), cùng các trình quản lý Gcf được mô tả trong báo cáo. Được xem là hoàn tất khi việc vẽ mosaic lặp lại hoàn thành mà không có ValueError và pyplot nhận diện trình quản lý figure hiện có.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- jupyter, jupyter-notebook, matplotlib, python
- Lĩnh vực
- data-visualization, frontend
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Đình trệ
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức phù hợp với người mới
- 35/100