Skip to content

Commit c893d56

Browse files
committed
Merge pull request #3258 from mdboom/ft2font-memleak
Fix various memory leaks discovered through valgrind
2 parents fef699a + 42fa2b6 commit c893d56

File tree

5 files changed

+49
-38
lines changed

5 files changed

+49
-38
lines changed

lib/matplotlib/testing/decorators.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import six
55

66
import functools
7+
import gc
78
import os
89
import sys
910
import shutil
@@ -65,20 +66,25 @@ def failer(*args, **kwargs):
6566
return known_fail_decorator
6667

6768

69+
def _do_cleanup(original_units_registry):
70+
plt.close('all')
71+
gc.collect()
72+
73+
matplotlib.tests.setup()
74+
75+
matplotlib.units.registry.clear()
76+
matplotlib.units.registry.update(original_units_registry)
77+
warnings.resetwarnings() # reset any warning filters set in tests
78+
79+
6880
class CleanupTest(object):
6981
@classmethod
7082
def setup_class(cls):
7183
cls.original_units_registry = matplotlib.units.registry.copy()
7284

7385
@classmethod
7486
def teardown_class(cls):
75-
plt.close('all')
76-
77-
matplotlib.tests.setup()
78-
79-
matplotlib.units.registry.clear()
80-
matplotlib.units.registry.update(cls.original_units_registry)
81-
warnings.resetwarnings() # reset any warning filters set in tests
87+
_do_cleanup(cls.original_units_registry)
8288

8389
def test(self):
8490
self._func()
@@ -93,13 +99,7 @@ def setUpClass(cls):
9399

94100
@classmethod
95101
def tearDownClass(cls):
96-
plt.close('all')
97-
98-
matplotlib.tests.setup()
99-
100-
matplotlib.units.registry.clear()
101-
matplotlib.units.registry.update(cls.original_units_registry)
102-
warnings.resetwarnings() # reset any warning filters set in tests
102+
_do_cleanup(cls.original_units_registry)
103103

104104

105105
def cleanup(func):
@@ -109,13 +109,8 @@ def wrapped_function(*args, **kwargs):
109109
try:
110110
func(*args, **kwargs)
111111
finally:
112-
plt.close('all')
113-
114-
matplotlib.tests.setup()
112+
_do_cleanup(original_units_registry)
115113

116-
matplotlib.units.registry.clear()
117-
matplotlib.units.registry.update(original_units_registry)
118-
warnings.resetwarnings() #reset any warning filters set in tests
119114
return wrapped_function
120115

121116

@@ -157,8 +152,6 @@ def test(self):
157152
baseline_dir, result_dir = _image_directories(self._func)
158153

159154
for fignum, baseline in zip(plt.get_fignums(), self._baseline_images):
160-
figure = plt.figure(fignum)
161-
162155
for extension in self._extensions:
163156
will_fail = not extension in comparable_formats()
164157
if will_fail:
@@ -182,6 +175,8 @@ def test(self):
182175
will_fail, fail_msg,
183176
known_exception_class=ImageComparisonFailure)
184177
def do_test():
178+
figure = plt.figure(fignum)
179+
185180
if self._remove_text:
186181
self.remove_text(figure)
187182

src/_backend_agg.cpp

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,7 @@ RendererAgg::_draw_path_collection_generic
15571557

15581558
if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0)
15591559
{
1560+
Py_XDECREF(transforms_arr);
15601561
return Py::Object();
15611562
}
15621563

@@ -1708,6 +1709,8 @@ RendererAgg::_draw_path_collection_generic
17081709
}
17091710
}
17101711

1712+
Py_XDECREF(transforms_arr);
1713+
17111714
return Py::Object();
17121715
}
17131716

@@ -2383,11 +2386,7 @@ RendererAgg::tostring_rgba_minimized(const Py::Tuple& args)
23832386

23842387
int newwidth = 0;
23852388
int newheight = 0;
2386-
#if PY3K
2387-
Py::Bytes data;
2388-
#else
2389-
Py::String data;
2390-
#endif
2389+
PyObject *data;
23912390

23922391
if (xmin < xmax && ymin < ymax)
23932392
{
@@ -2406,18 +2405,12 @@ RendererAgg::tostring_rgba_minimized(const Py::Tuple& args)
24062405
// the _AsString() API.
24072406
unsigned int* dst;
24082407

2409-
#if PY3K
2410-
data = Py::Bytes(static_cast<const char*>(NULL), (int) newsize);
2411-
dst = reinterpret_cast<unsigned int*>(PyBytes_AsString(data.ptr()));
2412-
#else
2413-
data = Py::String(static_cast<const char*>(NULL), (int) newsize);
2414-
dst = reinterpret_cast<unsigned int*>(PyString_AsString(data.ptr()));
2415-
#endif
2416-
2417-
if (dst == NULL)
2408+
data = PyBytes_FromStringAndSize(NULL, newsize);
2409+
if (data == NULL)
24182410
{
24192411
throw Py::MemoryError("RendererAgg::tostring_minimized could not allocate memory");
24202412
}
2413+
dst = (unsigned int *)PyBytes_AsString(data);
24212414

24222415
unsigned int* src = (unsigned int*)pixBuffer;
24232416
for (int y = ymin; y < ymax; ++y)
@@ -2436,7 +2429,8 @@ RendererAgg::tostring_rgba_minimized(const Py::Tuple& args)
24362429
bounds[3] = Py::Int(newheight);
24372430

24382431
Py::Tuple result(2);
2439-
result[0] = data;
2432+
result[0] = Py::Object(data, false);
2433+
Py_DECREF(data);
24402434
result[1] = bounds;
24412435

24422436
return result;

src/_image.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ _image_module::frombuffer(const Py::Tuple& args)
12641264

12651265
args.verify_length(4);
12661266

1267-
PyObject *bufin = new_reference_to(args[0]);
1267+
PyObject *bufin = args[0].ptr();
12681268
size_t x = (long)Py::Int(args[1]);
12691269
size_t y = (long)Py::Int(args[2]);
12701270

src/ft2font.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,10 @@ FT2Font::~FT2Font()
985985
{
986986
FT_Done_Glyph(glyphs[i]);
987987
}
988+
989+
if (stream.descriptor.pointer != NULL) {
990+
PyMem_Free(stream.descriptor.pointer);
991+
}
988992
}
989993

990994
int

test_only.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from distutils.core import setup
2+
3+
import os
4+
5+
baseline_images = [
6+
'baseline_images/%s/*' % x
7+
for x in os.listdir('lib/matplotlib/tests/baseline_images')]
8+
9+
baseline_images += [
10+
'mpltest.ttf',
11+
'test_rcparams.rc'
12+
]
13+
14+
setup(name='matplotlib.tests',
15+
packages=['matplotlib.tests'],
16+
package_dir={'matplotlib.tests': 'lib/matplotlib/tests'},
17+
package_data={'matplotlib.tests': baseline_images}
18+
)

0 commit comments

Comments
 (0)