Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MNT: set default canvas when un-pickling #16189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1999,7 +1999,7 @@ def __setstate__(self, state):

# re-initialise some of the unstored state information
self._axobservers = []
self.canvas = None
FigureCanvasBase(self) # Set self.canvas.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it would be vaguely nicer to set state["canvas"] (and also state["_axobservers"], state["_layoutbox"]) to the right values in __getstate__ rather than popping them out of the dict and having to restore them here, but I'm not going to insist on that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The types in these values tend to not be things that pickle well. canvas can be a Qt object and the observers can be lambdas (and yes, we could use cloud pickle and go down the route that dask does to pickle/unpickle almost anything, but that is probably not worth the complexity or something we want to encourage).

The other nice thing about not directly pickling the canvas objects is that when re-hydrated they will come up in the correct framework of the importer. This fix only matters if you are unpickling a Figure that was unknown to pyplot when it was pickled (and inline de-registers the figures after they show) then this code path will matter.

From a philosophical I think that the canvas object is not part of the identity of the Figure. A given Figure can only we associated with one canvas at a time (due to the circular references) but we can (and do!) switch the canvas out "live", hence even if we could, it probably should not be included verbatim in the pickle.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I know you can't pickle Qt canvasses (and agree they're not really part of figure state); I meant to pickle a FigureCanvasBase instead (I guess you only need to do so if restore_to_pylab is false). Not a really big deal, though.

self._layoutbox = None

if restore_to_pylab:
Expand Down
11 changes: 11 additions & 0 deletions lib/matplotlib/tests/test_pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from matplotlib.dates import rrulewrapper
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
import matplotlib.figure as mfigure


def test_simple():
Expand Down Expand Up @@ -194,3 +195,13 @@ def test_shared():
@pytest.mark.parametrize("cmap", cm.cmap_d.values())
def test_cmap(cmap):
pickle.dumps(cmap)


def test_unpickle_canvas():
fig = mfigure.Figure()
assert fig.canvas is not None
out = BytesIO()
pickle.dump(fig, out)
out.seek(0)
fig2 = pickle.load(out)
assert fig2.canvas is not None