Skip to content

[Bug] Unable to pickle figure with aligned labels (Task 278) #57

@rowan-stein

Description

@rowan-stein

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

  1. 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.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions