-
Notifications
You must be signed in to change notification settings - Fork 0
Description
[Bug]: Unable to pickle figure with aligned labels
Task reference: ID 278
Bug summary
Unable to pickle figure after calling `align_labels()`.
Code for reproduction
import matplotlib.pyplot as plt
import pickle
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
time=[0,1,2,3,4]
speed=[40000,4300,4500,4700,4800]
acc=[10,11,12,13,14]
ax1.plot(time,speed)
ax1.set_ylabel('speed')
ax2.plot(time,acc)
ax2.set_ylabel('acc')
fig.align_labels() ## pickling works after removing this line
pickle.dumps(fig)
plt.show()Actual outcome
TypeError: cannot pickle 'weakref.ReferenceType' object
Expected outcome
Pickling successful.
Additional information
Windows, Matplotlib 3.7.0. Backend: not specified.
Researcher specification (root cause and fix plan)
Root cause: After `fig.align_labels()`, Matplotlib stores alignment groups in `Figure._align_label_groups` using `cbook.Grouper`, whose internal mapping contains `weakref.ReferenceType` objects. During pickling, `Figure.getstate` includes `_align_label_groups`, which causes `pickle.dumps(fig)` to raise `TypeError: cannot pickle 'weakref.ReferenceType' object`.
Affected code paths (branch matplotlib__matplotlib-25332):
- `lib/matplotlib/figure.py`: `Figure._align_label_groups` and `align_xlabels`/`align_ylabels`/`align_labels` populate groups.
- `lib/matplotlib/cbook.py`: `Grouper` stores weak refs.
- `lib/matplotlib/artist.py`: `Artist.getstate` does not filter `_align_label_groups` out of Figure state.
Fix approach: Serialize alignment groups in a picklable form and reconstruct them on unpickling.
Proposed changes:
- In `Figure.getstate` (`lib/matplotlib/figure.py`):
- Build a picklable `_serialized_align_label_groups` dict of axis indices for both 'x' and 'y' groups (each group as a list of indices into `Figure.axes`).
- Remove `_align_label_groups` from the pickled state to avoid weakrefs.
- In `Figure.setstate` (`lib/matplotlib/figure.py`):
- Reconstruct `self._align_label_groups` as fresh `cbook.Grouper` instances.
- Re-join axes according to `_serialized_align_label_groups` by mapping indices back to `self.axes`.
- Initialize empty groupers if no serialized alignment info is present.
Tests to add:
- New test ensuring `pickle.dumps(fig)` succeeds after `fig.align_labels()`, and that alignment is preserved upon `pickle.loads`.
Backward compatibility:
- Figures pickled with this change will include `_serialized_align_label_groups`; older Matplotlib versions may not reconstruct alignment, which is acceptable. Current alignment behavior is preserved within the same version.