From 64479b4e02ca64fd6bafdbfab6c740ef5c50a6a7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 24 Jul 2016 02:35:38 -0700 Subject: [PATCH] Break reference cycle Line2D <-> Line2D._lineFunc. Upon drawing, Line2D objects would store a reference to one of their own bound methods as their `_lineFunc` argument. This would lead to them being gc'ed not when going out of scope, but only when the "true" gc kicks in; additionally this led to some pickle-related bugs (#3627). One can easily sidestep this problem by not storing this bound method. To check the behavior, try (py3.4+ only): ``` import gc import weakref from matplotlib import pyplot as plt def f(): fig, ax = plt.subplots() img = ax.imshow([[0, 1], [2, 3]]) weakref.finalize(img, print, "gc'ing image") l, = plt.plot([0, 1]) weakref.finalize(l, print, "gc'ing line") fig.canvas.draw() img.remove() l.remove() f() print("we have left the function") gc.collect() print("and cleaned up our mess") ``` Before the patch, the AxesImage is gc'ed when the function exits but the Line2D only upon explicit garbage collection. After the patch, both are collected immediately. --- lib/matplotlib/lines.py | 13 ++----------- lib/matplotlib/tests/test_pickle.py | 14 -------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 1f52371c67f7..e20b79af286c 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -445,12 +445,6 @@ def __init__(self, xdata, ydata, self.set_data(xdata, ydata) - def __getstate__(self): - state = super(Line2D, self).__getstate__() - # _linefunc will be restored on draw time. - state.pop('_lineFunc', None) - return state - def contains(self, mouseevent): """ Test whether the mouse event occurred on the line. The pick @@ -784,7 +778,7 @@ def draw(self, renderer): if funcname != '_draw_nothing': tpath, affine = transf_path.get_transformed_path_and_affine() if len(tpath.vertices): - self._lineFunc = getattr(self, funcname) + line_func = getattr(self, funcname) gc = renderer.new_gc() self._set_gc_clip(gc) @@ -807,7 +801,7 @@ def draw(self, renderer): if self.get_sketch_params() is not None: gc.set_sketch_params(*self.get_sketch_params()) - self._draw_lines(renderer, gc, tpath, affine.frozen()) + line_func(renderer, gc, tpath, affine.frozen()) gc.restore() if self._marker and self._markersize > 0: @@ -1250,9 +1244,6 @@ def set_dashes(self, seq): else: self.set_linestyle((0, seq)) - def _draw_lines(self, renderer, gc, path, trans): - self._lineFunc(renderer, gc, path, trans) - def _draw_solid(self, renderer, gc, path, trans): gc.set_linestyle('solid') gc.set_dashes(self._dashOffset, self._dashSeq) diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 7c86da82ae1c..5727c1316281 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -229,20 +229,6 @@ def test_image(): pickle.dump(fig, BytesIO()) -@cleanup -def test_grid(): - from matplotlib.backends.backend_agg import new_figure_manager - manager = new_figure_manager(1000) - fig = manager.canvas.figure - ax = fig.add_subplot(1, 1, 1) - ax.grid() - # Drawing the grid triggers instance methods to be attached - # to the Line2D object (_lineFunc). - manager.canvas.draw() - - pickle.dump(ax, BytesIO()) - - @cleanup def test_polar(): ax = plt.subplot(111, polar=True) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy