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

Skip to content

Commit 37a7aa6

Browse files
committed
Fix memory leak in ft2font/mathtext.
1 parent 5a4c578 commit 37a7aa6

2 files changed

Lines changed: 35 additions & 36 deletions

File tree

lib/matplotlib/backends/backend_agg.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
131131
x = int(x) + ox
132132
y = int(y) - oy
133133
self._renderer.draw_text_image(font_image, x, y + 1, angle, gc)
134+
# XXX: This is to work around a bug in Python 3 where it
135+
# apparently leaks references to frames
136+
del font_image
134137

135138
def draw_text(self, gc, x, y, s, prop, angle, ismath):
136139
"""

lib/matplotlib/mathtext.py

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,15 @@ def get_results(self, box, used_characters):
212212
(bbox[3] - bbox[1]) - orig_depth,
213213
(bbox[3] - bbox[1]) - orig_height)
214214
ship(-bbox[0], -bbox[1], box)
215-
return (self.ox,
216-
self.oy,
217-
self.width,
218-
self.height + self.depth,
219-
self.depth,
220-
self.image,
221-
used_characters)
215+
result = (self.ox,
216+
self.oy,
217+
self.width,
218+
self.height + self.depth,
219+
self.depth,
220+
self.image,
221+
used_characters)
222+
self.image = None
223+
return result
222224

223225
def get_hinting_type(self):
224226
if rcParams['text.hinting']:
@@ -266,7 +268,6 @@ def render_rect_filled(self, x1, y1, x2, y2):
266268

267269
def get_results(self, box, used_characters):
268270
ship(0, -self.depth, box)
269-
#print self.depth
270271
return (self.width,
271272
self.height + self.depth,
272273
self.depth,
@@ -529,7 +530,9 @@ def get_results(self, box):
529530
Get the data needed by the backend to render the math
530531
expression. The return value is backend-specific.
531532
"""
532-
return self.mathtext_backend.get_results(box, self.get_used_characters())
533+
result = self.mathtext_backend.get_results(box, self.get_used_characters())
534+
self.destroy()
535+
return result
533536

534537
def get_sized_alternatives_for_symbol(self, fontname, sym):
535538
"""
@@ -2308,16 +2311,6 @@ def __init__(self):
23082311
)
23092312
) + StringEnd()
23102313

2311-
self.clear()
2312-
2313-
def clear(self):
2314-
"""
2315-
Clear any state before parsing.
2316-
"""
2317-
self._expr = None
2318-
self._state_stack = None
2319-
self._em_width_cache = {}
2320-
23212314
def parse(self, s, fonts_object, fontsize, dpi):
23222315
"""
23232316
Parse expression *s* using the given *fonts_object* for
@@ -2326,15 +2319,18 @@ def parse(self, s, fonts_object, fontsize, dpi):
23262319
Returns the parse tree of :class:`Node` instances.
23272320
"""
23282321
self._state_stack = [self.State(fonts_object, 'default', 'rm', fontsize, dpi)]
2322+
self._em_width_cache = {}
23292323
try:
2330-
self._expression.parseString(s)
2324+
result = self._expression.parseString(s)
23312325
except ParseException as err:
23322326
raise ValueError("\n".join([
23332327
"",
23342328
err.line,
23352329
" " * (err.column - 1) + "^",
23362330
str(err)]))
2337-
return self._expr
2331+
self._state_stack = None
2332+
self._em_width_cache = {}
2333+
return result[0]
23382334

23392335
# The state of the parser is maintained in a stack. Upon
23402336
# entering and leaving a group { } or math/non-math, the stack
@@ -2391,8 +2387,7 @@ def push_state(self):
23912387

23922388
def finish(self, s, loc, toks):
23932389
#~ print "finish", toks
2394-
self._expr = Hlist(toks)
2395-
return [self._expr]
2390+
return [Hlist(toks)]
23962391

23972392
def math(self, s, loc, toks):
23982393
#~ print "math", toks
@@ -2920,12 +2915,16 @@ def parse(self, s, dpi = 72, prop = None):
29202915
The results are cached, so multiple calls to :meth:`parse`
29212916
with the same expression should be fast.
29222917
"""
2918+
# There is a bug in Python 3.x where it leaks frame references,
2919+
# and therefore can't handle this caching
29232920
if prop is None:
29242921
prop = FontProperties()
2925-
cacheKey = (s, dpi, hash(prop))
2926-
result = self._cache.get(cacheKey)
2927-
if result is not None:
2928-
return result
2922+
2923+
if sys.version_info[0] < 3:
2924+
cacheKey = (s, dpi, hash(prop))
2925+
result = self._cache.get(cacheKey)
2926+
if result is not None:
2927+
return result
29292928

29302929
if self._output == 'ps' and rcParams['ps.useafm']:
29312930
font_output = StandardPsFonts(prop)
@@ -2949,15 +2948,12 @@ def parse(self, s, dpi = 72, prop = None):
29492948

29502949
box = self._parser.parse(s, font_output, fontsize, dpi)
29512950
font_output.set_canvas_size(box.width, box.height, box.depth)
2952-
result = font_output.get_results(box)
2953-
self._cache[cacheKey] = result
2954-
# Free up the transient data structures
2955-
self._parser.clear()
2956-
2957-
# Fix cyclical references
2958-
font_output.destroy()
2959-
2960-
return result
2951+
if sys.version_info[0] >= 3:
2952+
return font_output.get_results(box)
2953+
else:
2954+
result = font_output.get_results(box)
2955+
self._cache[cacheKey] = result
2956+
return result
29612957

29622958
def to_mask(self, texstr, dpi=120, fontsize=14):
29632959
"""

0 commit comments

Comments
 (0)