From 27d4c0c0635441390acf2c7e67e1fdaa573496cb Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 26 Apr 2011 16:29:31 -0400 Subject: [PATCH 01/10] Updates to the image comparison testing decorator: 1) Run the test itself that generates figure(s) only once, then savefig it to each output format. This appears to have an approx 2.75x speedup on run time. 2) Cleanup better after each test: Close all figures Reset rcParams to their defaults Reset the unit registry to defaults This requires the following changes to the tests themselves: 1) The tests no longer need to call 'savefig' -- just create figures and the decorator will find them. 2) Tests that don't use the image_comparison decorator should use the new cleanup decorator to make sure that cleanup happens. --- lib/matplotlib/testing/decorators.py | 156 ++-- .../test_axes/polar_units_2.pdf | Bin 0 -> 19175 bytes .../test_axes/polar_units_2.png | Bin 0 -> 82920 bytes .../test_axes/polar_units_2.svg | 715 ++++++++++++++++++ lib/matplotlib/tests/test_axes.py | 65 +- lib/matplotlib/tests/test_backend_pdf.py | 15 +- lib/matplotlib/tests/test_backend_svg.py | 5 +- lib/matplotlib/tests/test_dates.py | 14 +- lib/matplotlib/tests/test_image.py | 11 +- lib/matplotlib/tests/test_legend.py | 2 - lib/matplotlib/tests/test_mathtext.py | 7 +- lib/matplotlib/tests/test_png.py | 2 - lib/matplotlib/tests/test_simplification.py | 19 +- lib/matplotlib/tests/test_spines.py | 1 - lib/matplotlib/tests/test_text.py | 4 - 15 files changed, 851 insertions(+), 165 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.pdf create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.png create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 5c64bf510692..bb03f3ac4a39 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -1,9 +1,11 @@ from matplotlib.testing.noseclasses import KnownFailureTest, \ KnownFailureDidNotFailTest, ImageComparisonFailure -import os, sys, shutil +import os, sys, shutil, new import nose import matplotlib import matplotlib.tests +import matplotlib.units +from matplotlib import pyplot as plt import numpy as np from matplotlib.testing.compare import comparable_formats, compare_images @@ -46,7 +48,83 @@ def failer(*args, **kwargs): return nose.tools.make_decorator(f)(failer) return known_fail_decorator -def image_comparison(baseline_images=None,extensions=None,tol=1e-3): +class CleanupTest: + @classmethod + def setup_class(cls): + cls.original_rcParams = {} + cls.original_rcParams.update(matplotlib.rcParams) + + cls.original_units_registry = {} + cls.original_units_registry.update(matplotlib.units.registry) + + @classmethod + def teardown_class(cls): + plt.close('all') + + matplotlib.rcParams.clear() + matplotlib.rcParams.update(cls.original_rcParams) + + matplotlib.units.registry.clear() + matplotlib.units.registry.update(cls.original_units_registry) + + def test(self): + self.func() + +def cleanup(func): + new_class = new.classobj( + func.__name__, + (CleanupTest,), + {'func': staticmethod(func)}) + return new_class + +class ImageComparisonTest(CleanupTest): + @classmethod + def setup_class(cls): + CleanupTest.setup_class() + + cls.func() + + def test(self): + baseline_dir, result_dir = _image_directories(self.func) + + for fignum, baseline in zip(plt.get_fignums(), self.baseline_images): + figure = plt.figure(fignum) + + for extension in self.extensions: + will_fail = not extension in comparable_formats() + if will_fail: + fail_msg = 'Cannot compare %s files on this system' % extension + else: + fail_msg = 'No failure expected' + + orig_expected_fname = os.path.join(baseline_dir, baseline) + '.' + extension + expected_fname = os.path.join(result_dir, 'expected-' + baseline) + '.' + extension + actual_fname = os.path.join(result_dir, baseline) + '.' + extension + if os.path.exists(orig_expected_fname): + shutil.copyfile(orig_expected_fname, expected_fname) + else: + will_fail = True + fail_msg = 'Do not have baseline image %s' % expected_fname + + @knownfailureif( + will_fail, fail_msg, + known_exception_class=ImageComparisonFailure) + def do_test(): + figure.savefig(actual_fname) + + if not os.path.exists(expected_fname): + raise ImageComparisonFailure( + 'image does not exist: %s' % expected_fname) + + err = compare_images(expected_fname, actual_fname, self.tol, in_decorator=True) + if err: + raise ImageComparisonFailure( + 'images not close: %(actual)s vs. %(expected)s ' + '(RMS %(rms).3f)'%err) + + yield (do_test,) + +def image_comparison(baseline_images=None, extensions=None, tol=1e-3): """ call signature:: @@ -76,56 +154,22 @@ def image_comparison(baseline_images=None,extensions=None,tol=1e-3): # default extensions to test extensions = ['png', 'pdf', 'svg'] - # The multiple layers of defs are required because of how - # parameterized decorators work, and because we want to turn the - # single test_foo function to a generator that generates a - # separate test case for each file format. def compare_images_decorator(func): - baseline_dir, result_dir = _image_directories(func) - - def compare_images_generator(): - for extension in extensions: - orig_expected_fnames = [os.path.join(baseline_dir,fname) + '.' + extension for fname in baseline_images] - expected_fnames = [os.path.join(result_dir,'expected-'+fname) + '.' + extension for fname in baseline_images] - actual_fnames = [os.path.join(result_dir, fname) + '.' + extension for fname in baseline_images] - have_baseline_images = [os.path.exists(expected) for expected in orig_expected_fnames] - have_baseline_image = np.all(have_baseline_images) - is_comparable = extension in comparable_formats() - if not is_comparable: - fail_msg = 'Cannot compare %s files on this system' % extension - elif not have_baseline_image: - fail_msg = 'Do not have baseline images %s' % expected_fnames - else: - fail_msg = 'No failure expected' - will_fail = not (is_comparable and have_baseline_image) - @knownfailureif(will_fail, fail_msg, - known_exception_class=ImageComparisonFailure ) - def decorated_compare_images(): - # set the default format of savefig - matplotlib.rc('savefig', extension=extension) - # change to the result directory for the duration of the test - old_dir = os.getcwd() - os.chdir(result_dir) - try: - result = func() # actually call the test function - finally: - os.chdir(old_dir) - for original, expected in zip(orig_expected_fnames, expected_fnames): - if not os.path.exists(original): - raise ImageComparisonFailure( - 'image does not exist: %s'%original) - shutil.copyfile(original, expected) - for actual,expected in zip(actual_fnames,expected_fnames): - # compare the images - err = compare_images( expected, actual, tol, - in_decorator=True ) - if err: - raise ImageComparisonFailure( - 'images not close: %(actual)s vs. %(expected)s ' - '(RMS %(rms).3f)'%err) - return result - yield (decorated_compare_images,) - return nose.tools.make_decorator(func)(compare_images_generator) + # We want to run the setup function (the actual test function + # that generates the figure objects) only once for each type + # of output file. The only way to achieve this with nose + # appears to be to create a test class with "setup_class" and + # "teardown_class" methods. Creating a class instance doesn't + # work, so we use new.classobj to actually create a class and + # fill it with the appropriate methods. + new_class = new.classobj( + func.__name__, + (ImageComparisonTest,), + {'func': staticmethod(func), + 'baseline_images': baseline_images, + 'extensions': extensions, + 'tol': tol}) + return new_class return compare_images_decorator def _image_directories(func): @@ -134,7 +178,7 @@ def _image_directories(func): Create the result directory if it doesn't exist. """ module_name = func.__module__ - if module_name=='__main__': + if module_name == '__main__': # FIXME: this won't work for nested packages in matplotlib.tests import warnings warnings.warn('test module run as script. guessing baseline image locations') @@ -143,13 +187,13 @@ def _image_directories(func): subdir = os.path.splitext(os.path.split(script_name)[1])[0] else: mods = module_name.split('.') - assert mods.pop(0)=='matplotlib' - assert mods.pop(0)=='tests' + assert mods.pop(0) == 'matplotlib' + assert mods.pop(0) == 'tests' subdir = os.path.join(*mods) basedir = os.path.dirname(matplotlib.tests.__file__) - baseline_dir = os.path.join(basedir,'baseline_images',subdir) - result_dir = os.path.abspath(os.path.join('result_images',subdir)) + baseline_dir = os.path.join(basedir, 'baseline_images', subdir) + result_dir = os.path.abspath(os.path.join('result_images', subdir)) if not os.path.exists(result_dir): os.makedirs(result_dir) diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..109e59e19e172dd11de187f6037f9fd10a570376 GIT binary patch literal 19175 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∋E`}T!Y0R)a(9FjZ#HH3lOcnX9XYjeeh0O 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 literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg new file mode 100644 index 000000000000..e854972fdae5 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg @@ -0,0 +1,715 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 5c67dc88de40..a5cb9c347ae9 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -24,23 +24,32 @@ def test_formatter_ticker(): fig = plt.figure() ax = plt.subplot( 111 ) ax.set_xlabel( "x-label 001" ) - fig.savefig( 'formatter_ticker_001' ) + fig = plt.figure() + ax = plt.subplot( 111 ) + ax.set_xlabel( "x-label 001" ) ax.plot( xdata, ydata1, color='blue', xunits="sec" ) - fig.savefig( 'formatter_ticker_002' ) + fig = plt.figure() + ax = plt.subplot( 111 ) + ax.set_xlabel( "x-label 001" ) + ax.plot( xdata, ydata1, color='blue', xunits="sec" ) ax.set_xlabel( "x-label 003" ) - fig.savefig( 'formatter_ticker_003' ) + fig = plt.figure() + ax = plt.subplot( 111 ) + ax.plot( xdata, ydata1, color='blue', xunits="sec" ) ax.plot( xdata, ydata2, color='green', xunits="hour" ) ax.set_xlabel( "x-label 004" ) - fig.savefig( 'formatter_ticker_004' ) # See SF bug 2846058 # https://sourceforge.net/tracker/?func=detail&aid=2846058&group_id=80706&atid=560720 + fig = plt.figure() + ax = plt.subplot( 111 ) + ax.plot( xdata, ydata1, color='blue', xunits="sec" ) + ax.plot( xdata, ydata2, color='green', xunits="hour" ) ax.set_xlabel( "x-label 005" ) ax.autoscale_view() - fig.savefig( 'formatter_ticker_005' ) @image_comparison(baseline_images=['offset_points']) def test_basic_annotate(): @@ -57,8 +66,6 @@ def test_basic_annotate(): ax.annotate( 'local max', xy=(3, 1), xycoords='data', xytext=(3, 3), textcoords='offset points' ) - fig.savefig( 'offset_points' ) - @image_comparison(baseline_images=['polar_axes']) def test_polar_annotations(): # you can specify the xypoint and the xytext in different @@ -91,8 +98,6 @@ def test_polar_annotations(): verticalalignment='baseline', ) - fig.savefig( 'polar_axes' ) - #-------------------------------------------------------------------- @image_comparison(baseline_images=['polar_coords']) def test_polar_coord_annotations(): @@ -122,7 +127,6 @@ def test_polar_coord_annotations(): ax.set_xlim( -20, 20 ) ax.set_ylim( -20, 20 ) - fig.savefig( 'polar_coords' ) @image_comparison(baseline_images=['fill_units']) def test_fill_units(): @@ -163,7 +167,6 @@ def test_fill_units(): facecolor="blue" ) fig.autofmt_xdate() - fig.savefig( 'fill_units' ) @image_comparison(baseline_images=['single_point']) def test_single_point(): @@ -174,8 +177,6 @@ def test_single_point(): plt.subplot( 212 ) plt.plot( [1], [1], 'o' ) - fig.savefig( 'single_point' ) - @image_comparison(baseline_images=['single_date']) def test_single_date(): time1=[ 721964.0 ] @@ -188,8 +189,6 @@ def test_single_date(): plt.subplot( 212 ) plt.plot( time1, data1, 'o', color='r' ) - fig.savefig( 'single_date' ) - @image_comparison(baseline_images=['shaped_data']) def test_shaped_data(): xdata = np.array([[ 0.53295185, 0.23052951, 0.19057629, 0.66724975, 0.96577916, @@ -232,8 +231,6 @@ def test_shaped_data(): plt.subplot( 414 ) plt.plot( xdata[:,1], xdata[1,:], 'o' ) - fig.savefig( 'shaped_data' ) - @image_comparison(baseline_images=['const_xy']) def test_const_xy(): fig = plt.figure() @@ -247,8 +244,6 @@ def test_const_xy(): plt.subplot( 313 ) plt.plot( np.ones( (10,) ), np.ones( (10,) ), 'o' ) - fig.savefig( 'const_xy' ) - @image_comparison(baseline_images=['polar_wrap_180', 'polar_wrap_360', ]) @@ -263,8 +258,6 @@ def test_polar_wrap(): plt.polar( [179*D2R, 181*D2R], [0.2, 0.1], "g.-" ) plt.rgrids( [0.05, 0.1, 0.15, 0.2, 0.25, 0.3] ) - fig.savefig( 'polar_wrap_180' ) - fig = plt.figure() #NOTE: resolution=1 really should be the default @@ -274,9 +267,7 @@ def test_polar_wrap(): plt.polar( [358*D2R, 2*D2R], [0.2, 0.1], "r.-" ) plt.rgrids( [0.05, 0.1, 0.15, 0.2, 0.25, 0.3] ) - fig.savefig( 'polar_wrap_360' ) - -@image_comparison(baseline_images=['polar_units']) +@image_comparison(baseline_images=['polar_units', 'polar_units_2']) def test_polar_units(): import matplotlib.testing.jpl_units as units from nose.tools import assert_true @@ -299,7 +290,7 @@ def test_polar_units(): # polar( x2, y1, color = "red", xunits="rad" ) # polar( x2, y2, color = "green" ) - fig.savefig( 'polar_units' ) + fig = plt.figure() # make sure runits and theta units work y1 = [ y*km for y in y1 ] @@ -318,8 +309,6 @@ def test_polar_rmin(): ax.set_rmax(2.0) ax.set_rmin(0.5) - fig.savefig('polar_rmin') - @image_comparison(baseline_images=['axvspan_epoch']) def test_axvspan_epoch(): from datetime import datetime @@ -339,8 +328,6 @@ def test_axvspan_epoch(): ax = plt.gca() ax.set_xlim( t0 - 5.0*dt, tf + 5.0*dt ) - fig.savefig( 'axvspan_epoch' ) - @image_comparison(baseline_images=['axhspan_epoch']) def test_axhspan_epoch(): from datetime import datetime @@ -360,8 +347,6 @@ def test_axhspan_epoch(): ax = plt.gca() ax.set_ylim( t0 - 5.0*dt, tf + 5.0*dt ) - fig.savefig( 'axhspan_epoch' ) - @image_comparison(baseline_images=['hexbin_extent']) def test_hexbin_extent(): @@ -374,7 +359,6 @@ def test_hexbin_extent(): x, y = data ax.hexbin(x, y, extent=[.1, .3, .6, .7]) - fig.savefig('hexbin_extent') @image_comparison(baseline_images=['nonfinite_limits']) def test_nonfinite_limits(): @@ -384,7 +368,6 @@ def test_nonfinite_limits(): fig = plt.figure() ax = fig.add_subplot(111) ax.plot(x, y) - fig.savefig('nonfinite_limits') @image_comparison(baseline_images=['imshow']) def test_imshow(): @@ -400,7 +383,6 @@ def test_imshow(): ax = fig.add_subplot(111) ax.imshow(r) - fig.savefig('imshow') @image_comparison(baseline_images=['imshow_clip'], tol=1e-2) def test_imshow_clip(): @@ -427,7 +409,6 @@ def test_imshow_clip(): #Plot the image clipped by the contour ax.imshow(r, clip_path=clip_path) - fig.savefig('imshow_clip') @image_comparison(baseline_images=['polycollection_joinstyle']) def test_polycollection_joinstyle(): @@ -445,8 +426,6 @@ def test_polycollection_joinstyle(): ax.set_xticks([]) ax.set_yticks([]) - fig.savefig('polycollection_joinstyle') - @image_comparison(baseline_images=['fill_between_interpolate'], tol=1e-2) def test_fill_between_interpolate(): x = np.arange(0.0, 2, 0.02) @@ -466,8 +445,6 @@ def test_fill_between_interpolate(): ax1.fill_between(x, y1, y2, where=y2>=y1, facecolor='green', interpolate=True) ax1.fill_between(x, y1, y2, where=y2<=y1, facecolor='red', interpolate=True) - fig.savefig('fill_between_interpolate') - @image_comparison(baseline_images=['symlog']) def test_symlog(): x = np.array([0,1,2,4,6,9,12,24]) @@ -480,8 +457,6 @@ def test_symlog(): ax.set_xscale=('linear') ax.set_ylim(-1,10000000) - fig.savefig('symlog') - @image_comparison(baseline_images=['pcolormesh'], tol=0.02) def test_pcolormesh(): n = 12 @@ -510,14 +485,11 @@ def test_pcolormesh(): ax.set_xticks([]) ax.set_yticks([]) - fig.savefig('pcolormesh') - @image_comparison(baseline_images=['canonical']) def test_canonical(): fig, ax = plt.subplots() ax.plot([1,2,3]) - fig.savefig('canonical') @image_comparison(baseline_images=['arc_ellipse']) @@ -557,8 +529,6 @@ def test_arc_ellipse(): ax.add_patch(e2) - fig.savefig('arc_ellipse') - @image_comparison(baseline_images=['units_strings']) def test_units_strings(): # Make sure passing in sequences of strings doesn't cause the unit @@ -568,7 +538,6 @@ def test_units_strings(): fig = plt.figure() ax = fig.add_subplot(111) ax.plot(Id, pout) - fig.savefig('units_strings') @image_comparison(baseline_images=['markevery']) def test_markevery(): @@ -583,7 +552,6 @@ def test_markevery(): ax.plot(x, y, 's', markevery=10, label='mark every 10') ax.plot(x, y, '+', markevery=(5, 20), label='mark every 5 starting at 10') ax.legend() - fig.savefig('markevery') @image_comparison(baseline_images=['markevery_line']) def test_markevery_line(): @@ -598,7 +566,6 @@ def test_markevery_line(): ax.plot(x, y, '-s', markevery=10, label='mark every 10') ax.plot(x, y, '-+', markevery=(5, 20), label='mark every 5 starting at 10') ax.legend() - fig.savefig('markevery_line') if __name__=='__main__': import nose diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index 1d9b7b0ed12f..63b2b7c2b397 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -6,9 +6,6 @@ @image_comparison(baseline_images=['pdf_use14corefonts'], extensions=['pdf']) def test_use14corefonts(): - original_rcParams = {} - original_rcParams.update(rcParams) - rcParams['backend'] = 'pdf' rcParams['pdf.use14corefonts'] = True rcParams['font.family'] = 'sans-serif' @@ -21,11 +18,7 @@ def test_use14corefonts(): and containing some French characters and the euro symbol: "Merci pépé pour les 10 €"''' - try: - plt.figure() - plt.title(title) - plt.text(0.5, 0.5, text, horizontalalignment='center', fontsize=24) - plt.axhline(0.5, linewidth=0.5) - plt.savefig('pdf_use14corefonts.pdf') - finally: - rcParams.update(original_rcParams) + plt.figure() + plt.title(title) + plt.text(0.5, 0.5, text, horizontalalignment='center', fontsize=24) + plt.axhline(0.5, linewidth=0.5) diff --git a/lib/matplotlib/tests/test_backend_svg.py b/lib/matplotlib/tests/test_backend_svg.py index c99c480981da..ba0a3ae76ec8 100644 --- a/lib/matplotlib/tests/test_backend_svg.py +++ b/lib/matplotlib/tests/test_backend_svg.py @@ -2,8 +2,9 @@ import numpy as np import cStringIO as StringIO import xml.parsers.expat -from matplotlib.testing.decorators import knownfailureif +from matplotlib.testing.decorators import knownfailureif, cleanup +@cleanup def test_visibility(): # This is SF 2856495. See # https://sourceforge.net/tracker/?func=detail&aid=2856495&group_id=80706&atid=560720 @@ -19,7 +20,7 @@ def test_visibility(): artist.set_visible(False) fd = StringIO.StringIO() - fig.savefig(fd,format='svg') + fig.savefig(fd, format='svg') fd.seek(0) buf = fd.read() diff --git a/lib/matplotlib/tests/test_dates.py b/lib/matplotlib/tests/test_dates.py index 04f491c597fa..43854ea4a6ab 100644 --- a/lib/matplotlib/tests/test_dates.py +++ b/lib/matplotlib/tests/test_dates.py @@ -1,6 +1,6 @@ import datetime import numpy as np -from matplotlib.testing.decorators import image_comparison, knownfailureif +from matplotlib.testing.decorators import image_comparison, knownfailureif, cleanup import matplotlib.pyplot as plt from nose.tools import assert_raises @@ -12,7 +12,6 @@ def test_date_empty(): fig = plt.figure() ax = fig.add_subplot(1,1,1) ax.xaxis_date() - fig.savefig('date_empty') @image_comparison(baseline_images=['date_axhspan']) def test_date_axhspan(): @@ -25,7 +24,6 @@ def test_date_axhspan(): ax.set_ylim(t0-datetime.timedelta(days=5), tf+datetime.timedelta(days=5)) fig.subplots_adjust(left=0.25) - fig.savefig('date_axhspan') @image_comparison(baseline_images=['date_axvspan']) def test_date_axvspan(): @@ -38,8 +36,6 @@ def test_date_axvspan(): ax.set_xlim(t0-datetime.timedelta(days=720), tf+datetime.timedelta(days=720)) fig.autofmt_xdate() - fig.savefig('date_axvspan') - @image_comparison(baseline_images=['date_axhline']) def test_date_axhline(): @@ -52,7 +48,6 @@ def test_date_axhline(): ax.set_ylim(t0-datetime.timedelta(days=5), tf+datetime.timedelta(days=5)) fig.subplots_adjust(left=0.25) - fig.savefig('date_axhline') @image_comparison(baseline_images=['date_axvline']) def test_date_axvline(): @@ -65,8 +60,8 @@ def test_date_axvline(): ax.set_xlim(t0-datetime.timedelta(days=5), tf+datetime.timedelta(days=5)) fig.autofmt_xdate() - fig.savefig('date_axvline') +@cleanup def test_too_many_date_ticks(): # Attempt to test SF 2715172, see # https://sourceforge.net/tracker/?func=detail&aid=2715172&group_id=80706&atid=560720 @@ -111,8 +106,6 @@ def test_RRuleLocator(): ax.autoscale_view() fig.autofmt_xdate() - fig.savefig( 'RRuleLocator_bounds' ) - @image_comparison(baseline_images=['DateFormatter_fractionalSeconds']) def test_DateFormatter(): import pylab @@ -139,9 +132,8 @@ def test_DateFormatter(): ax.autoscale_view() fig.autofmt_xdate() - fig.savefig( 'DateFormatter_fractionalSeconds' ) - #@image_comparison(baseline_images=['empty_date_bug']) +@cleanup @knownfailureif(True) def test_empty_date_with_year_formatter(): # exposes sf bug 2861426: https://sourceforge.net/tracker/?func=detail&aid=2861426&group_id=80706&atid=560720 diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 23faf6c6b28f..65b377965463 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -1,6 +1,6 @@ import numpy as np -from matplotlib.testing.decorators import image_comparison, knownfailureif +from matplotlib.testing.decorators import image_comparison, knownfailureif, cleanup import matplotlib.pyplot as plt from nose.tools import assert_raises from numpy.testing import assert_array_equal @@ -28,8 +28,6 @@ def test_image_interps(): ax3.imshow(X, interpolation='bicubic') ax3.set_ylabel('bicubic') - fig.savefig('image_interps') - @image_comparison(baseline_images=['figimage-0', 'figimage-1'], extensions=['png'], tol=1.5e-3) def test_figimage(): 'test the figimage method' @@ -47,8 +45,7 @@ def test_figimage(): fig.figimage(img[:,::-1], xo=100, yo=0, origin='lower') fig.figimage(img[::-1,::-1], xo=100, yo=100, origin='lower') - fig.savefig('figimage-%d' % int(suppressComposite), dpi=100) - +@cleanup def test_image_python_io(): fig = plt.figure() ax = fig.add_subplot(111) @@ -107,8 +104,6 @@ def test_image_clip(): im = ax.imshow(d, extent=(-pi,pi,-pi/2,pi/2)) - fig.savefig('image_clip') - @image_comparison(baseline_images=['imshow']) def test_imshow(): import numpy as np @@ -121,8 +116,6 @@ def test_imshow(): ax.set_xlim(0,3) ax.set_ylim(0,3) - fig.savefig('imshow') - if __name__=='__main__': import nose nose.runmodule(argv=['-s','--with-doctest'], exit=False) diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 2cf7702244a2..17809bc6f1dc 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -14,7 +14,6 @@ def test_legend_auto1(): ax.plot(x, 50-x, 'o', label='y=1') ax.plot(x, x-50, 'o', label='y=-1') ax.legend(loc=0) - fig.savefig('legend_auto1') @image_comparison(baseline_images=['legend_auto2']) def test_legend_auto2(): @@ -25,5 +24,4 @@ def test_legend_auto2(): b1 = ax.bar(x, x, color='m') b2 = ax.bar(x, x[::-1], color='g') ax.legend([b1[0], b2[0]], ['up', 'down'], loc=0) - fig.savefig('legend_auto2') diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 0c37f6b9d602..02d3bf9ab2a4 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -141,10 +141,9 @@ def single_test(): matplotlib.rcParams['mathtext.fontset'] = fontset fig = plt.figure(figsize=(5.25, 0.75)) fig.text(0.5, 0.5, test, horizontalalignment='center', verticalalignment='center') - fig.savefig(filename) - fig.clf() - matplotlib.rcParams['mathtext.fontset'] = 'cm' - return single_test + func = single_test + func.__name__ = filename + "_test" + return func # We inject test functions into the global namespace, rather than # using a generator, so that individual tests can be run more diff --git a/lib/matplotlib/tests/test_png.py b/lib/matplotlib/tests/test_png.py index 58213c4aeb28..0e73a88aa77b 100644 --- a/lib/matplotlib/tests/test_png.py +++ b/lib/matplotlib/tests/test_png.py @@ -25,5 +25,3 @@ def test_pngsuite(): plt.gca().get_frame().set_facecolor("#ddffff") plt.gca().set_xlim(0, len(files)) - - fig.savefig('pngsuite') diff --git a/lib/matplotlib/tests/test_simplification.py b/lib/matplotlib/tests/test_simplification.py index b38ad48673db..4a02446a0196 100644 --- a/lib/matplotlib/tests/test_simplification.py +++ b/lib/matplotlib/tests/test_simplification.py @@ -1,6 +1,6 @@ import numpy as np import matplotlib -from matplotlib.testing.decorators import image_comparison, knownfailureif +from matplotlib.testing.decorators import image_comparison, knownfailureif, cleanup import matplotlib.pyplot as plt from pylab import * @@ -27,7 +27,6 @@ def test_clipping(): ax.set_ylim((-0.20, -0.28)) ax.set_xticks([]) ax.set_yticks([]) - fig.savefig('clipping') @image_comparison(baseline_images=['overflow'], tol=1e-2) def test_overflow(): @@ -41,8 +40,6 @@ def test_overflow(): ax.set_xticks([]) ax.set_yticks([]) - fig.savefig('overflow') - @image_comparison(baseline_images=['clipping_diamond']) def test_diamond(): x = np.array([0.0, 1.0, 0.0, -1.0, 0.0]) @@ -56,8 +53,7 @@ def test_diamond(): ax.set_xticks([]) ax.set_yticks([]) - fig.savefig('clipping_diamond') - +@cleanup def test_noise(): np.random.seed(0) x = np.random.uniform(size=(5000,)) * 50 @@ -77,6 +73,7 @@ def test_noise(): assert len(simplified) == 3884 +@cleanup def test_sine_plus_noise(): np.random.seed(0) x = np.sin(np.linspace(0, np.pi * 2.0, 1000)) + np.random.uniform(size=(1000,)) * 0.01 @@ -111,8 +108,6 @@ def test_simplify_curve(): ax.set_xlim((0, 2)) ax.set_ylim((0, 2)) - fig.savefig('simplify_curve') - @image_comparison(baseline_images=['hatch_simplify']) def test_hatch(): fig = plt.figure() @@ -121,8 +116,6 @@ def test_hatch(): ax.set_xlim((0.45, 0.55)) ax.set_ylim((0.45, 0.55)) - fig.savefig('hatch_simplify') - @image_comparison(baseline_images=['fft_peaks']) def test_fft_peaks(): fig = plt.figure() @@ -132,8 +125,6 @@ def test_fft_peaks(): ax.set_xticks([]) ax.set_yticks([]) - fig.savefig('fft_peaks') - path = p1[0].get_path() transform = p1[0].get_transform() path = transform.transform_path(path) @@ -143,6 +134,7 @@ def test_fft_peaks(): assert len(simplified) == 20 +@cleanup def test_start_with_moveto(): # Should be entirely clipped away to a single MOVETO data = """ @@ -175,6 +167,7 @@ def test_start_with_moveto(): assert len(segs) == 1 assert segs[0][1] == Path.MOVETO +@cleanup @raises(OverflowError) def test_throw_rendering_complexity_exceeded(): rcParams['path.simplify'] = False @@ -208,7 +201,6 @@ def test_clipper(): ax.yaxis.set_ticks_position('left') ax.set_xlim(5, 9) - fig.savefig('clipper_edge') @image_comparison(baseline_images=['para_equal_perp']) def test_para_equal_perp(): @@ -219,7 +211,6 @@ def test_para_equal_perp(): ax = fig.add_subplot(111) ax.plot(x + 1, y + 1) ax.plot(x + 1, y + 1, 'ro') - fig.savefig('para_equal_perp') if __name__=='__main__': import nose diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index adebc44a4b84..aa0722277c4a 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -18,4 +18,3 @@ def test_spines_axes_positions(): ax.xaxis.set_ticks_position('top') ax.spines['left'].set_color('none') ax.spines['bottom'].set_color('none') - fig.savefig('spines_axes_positions') diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index 9ef688e1015e..e01c23c897a0 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -62,8 +62,6 @@ def test_font_styles(): ax.set_xticks([]) ax.set_yticks([]) - fig.savefig('font_styles') - @image_comparison(baseline_images=['multiline']) def test_multiline(): fig = plt.figure() @@ -72,5 +70,3 @@ def test_multiline(): ax.set_xticks([]) ax.set_yticks([]) - - fig.savefig('multiline') From 354dee8d3bd7d52cb025c1597522930fc2f5c3d0 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 27 Apr 2011 09:57:48 -0400 Subject: [PATCH 02/10] Fix memory leak in pyplot.close('all') --- lib/matplotlib/_pylab_helpers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/_pylab_helpers.py b/lib/matplotlib/_pylab_helpers.py index 748af4c572fb..1df053ef2585 100644 --- a/lib/matplotlib/_pylab_helpers.py +++ b/lib/matplotlib/_pylab_helpers.py @@ -78,8 +78,12 @@ def destroy_fig(fig): @staticmethod def destroy_all(): for manager in Gcf.figs.values(): - Gcf.destroy(manager.num) + manager.canvas.mpl_disconnect(manager._cidgcf) + manager.destroy() + Gcf._activeQue = [] + Gcf.figs.clear() + gc.collect() @staticmethod def has_fignum(num): From a074823ff518c0b00c973c984f221478b58e95b0 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 27 Apr 2011 09:58:42 -0400 Subject: [PATCH 03/10] Run inkscape for SVG conversion as a server -- prevents starting inkscape up multiple times. --- lib/matplotlib/testing/compare.py | 61 +++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index 0fff2a760092..ee386ae3bae6 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -83,6 +83,22 @@ def compare_float( expected, actual, relTol = None, absTol = None ): # convert files with that extension to png format. converter = { } +def make_external_conversion_command(cmd): + def convert(*args): + cmdline = cmd(*args) + oldname, newname = args + pipe = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = pipe.communicate() + errcode = pipe.wait() + if not os.path.exists(newname) or errcode: + msg = "Conversion command failed:\n%s\n" % ' '.join(cmd) + if stdout: + msg += "Standard output:\n%s\n" % stdout + if stderr: + msg += "Standard error:\n%s\n" % stderr + raise IOError, msg + return convert + if matplotlib.checkdep_ghostscript() is not None: # FIXME: make checkdep_ghostscript return the command if sys.platform == 'win32': @@ -92,13 +108,36 @@ def compare_float( expected, actual, relTol = None, absTol = None ): cmd = lambda old, new: \ [gs, '-q', '-sDEVICE=png16m', '-dNOPAUSE', '-dBATCH', '-sOutputFile=' + new, old] - converter['pdf'] = cmd - converter['eps'] = cmd + converter['pdf'] = make_external_conversion_command(cmd) + converter['eps'] = make_external_conversion_command(cmd) if matplotlib.checkdep_inkscape() is not None: - cmd = lambda old, new: \ - ['inkscape', old, '--export-png=' + new] - converter['svg'] = cmd + def make_svg_converter(): + def read_to_end(buf): + ret = '' + lastchar = '' + while True: + char = buf.readline(1) + if char == '>' and lastchar == '\n': + break + ret += char + lastchar = char + return ret + + p = subprocess.Popen(['inkscape', '--shell'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + read_to_end(p.stdout) + + def convert_svg(old, new): + p.stdin.write('%s --export-png=%s\n' % (old, new)) + p.stdin.flush() + read_to_end(p.stdout) + + return convert_svg + + converter['svg'] = make_svg_converter() def comparable_formats(): '''Returns the list of file formats that compare_images can compare @@ -116,17 +155,7 @@ def convert(filename): newname = base + '_' + extension + '.png' if not os.path.exists(filename): raise IOError, "'%s' does not exist" % filename - cmd = converter[extension](filename, newname) - pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = pipe.communicate() - errcode = pipe.wait() - if not os.path.exists(newname) or errcode: - msg = "Conversion command failed:\n%s\n" % ' '.join(cmd) - if stdout: - msg += "Standard output:\n%s\n" % stdout - if stderr: - msg += "Standard error:\n%s\n" % stderr - raise IOError, msg + converter[extension](filename, newname) return newname verifiers = { } From 9086ef1a1c86e9f96d4d01893bfc13ac73596be8 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 27 Apr 2011 15:41:03 -0400 Subject: [PATCH 04/10] Do N SVG->PNG conversions with inkscape before shutting down the inkscape server and starting a new one to prevent memory leaks in Inkscape from becoming problematic. --- lib/matplotlib/testing/compare.py | 45 ++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index ee386ae3bae6..72c0b625f132 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -112,8 +112,34 @@ def convert(*args): converter['eps'] = make_external_conversion_command(cmd) if matplotlib.checkdep_inkscape() is not None: - def make_svg_converter(): - def read_to_end(buf): + class SVGConverter: + def __init__(self): + self._count = 0 + self._process = None + + def get_process(self): + # Since Inkscape can leak a little memory, we run X + # conversions and then shut it down and start up a new + # Inkscape. + if self._count == 0: + if self._process is not None: + self._process.communicate('quit\n') + self._process = subprocess.Popen(['inkscape', '-z', '--shell'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + self.read_to_end(self._process.stdout) + self._count = 10 + self._count -= 1 + return self._process + + def __call__(self, old, new): + process = self.get_process() + process.stdin.write('%s --export-png=%s\n' % (old, new)) + process.stdin.flush() + self.read_to_end(process.stdout) + + def read_to_end(self, buf): ret = '' lastchar = '' while True: @@ -124,20 +150,7 @@ def read_to_end(buf): lastchar = char return ret - p = subprocess.Popen(['inkscape', '--shell'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - read_to_end(p.stdout) - - def convert_svg(old, new): - p.stdin.write('%s --export-png=%s\n' % (old, new)) - p.stdin.flush() - read_to_end(p.stdout) - - return convert_svg - - converter['svg'] = make_svg_converter() + converter['svg'] = SVGConverter() def comparable_formats(): '''Returns the list of file formats that compare_images can compare From 84bd64924cd0dc34ab6966bcfec50b52d6c92c1a Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 27 Apr 2011 15:41:42 -0400 Subject: [PATCH 05/10] Reduce the number of dictionary copies around each test. Call the main test setup function to reset rcParams state. --- lib/matplotlib/testing/decorators.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index bb03f3ac4a39..3fe2294741d6 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -51,18 +51,13 @@ def failer(*args, **kwargs): class CleanupTest: @classmethod def setup_class(cls): - cls.original_rcParams = {} - cls.original_rcParams.update(matplotlib.rcParams) - - cls.original_units_registry = {} - cls.original_units_registry.update(matplotlib.units.registry) + cls.original_units_registry = matplotlib.units.registry.copy() @classmethod def teardown_class(cls): plt.close('all') - matplotlib.rcParams.clear() - matplotlib.rcParams.update(cls.original_rcParams) + matplotlib.tests.setup() matplotlib.units.registry.clear() matplotlib.units.registry.update(cls.original_units_registry) From ea162783246d6b20b225f5281b2a54f0560a85eb Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 27 Apr 2011 21:42:12 -0400 Subject: [PATCH 06/10] Fix new test decorator to work with nose-1.0.0 in addition to nose-0.11.3 --- lib/matplotlib/testing/decorators.py | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 3fe2294741d6..6de2daccf57c 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -63,13 +63,16 @@ def teardown_class(cls): matplotlib.units.registry.update(cls.original_units_registry) def test(self): - self.func() + self._func() def cleanup(func): + name = func.__name__ + func = staticmethod(func) + func.__func__.__name__ = '_private' new_class = new.classobj( - func.__name__, + name, (CleanupTest,), - {'func': staticmethod(func)}) + {'_func': func}) return new_class class ImageComparisonTest(CleanupTest): @@ -77,15 +80,15 @@ class ImageComparisonTest(CleanupTest): def setup_class(cls): CleanupTest.setup_class() - cls.func() + cls._func() def test(self): - baseline_dir, result_dir = _image_directories(self.func) + baseline_dir, result_dir = _image_directories(self._func) - for fignum, baseline in zip(plt.get_fignums(), self.baseline_images): + for fignum, baseline in zip(plt.get_fignums(), self._baseline_images): figure = plt.figure(fignum) - for extension in self.extensions: + for extension in self._extensions: will_fail = not extension in comparable_formats() if will_fail: fail_msg = 'Cannot compare %s files on this system' % extension @@ -111,7 +114,7 @@ def do_test(): raise ImageComparisonFailure( 'image does not exist: %s' % expected_fname) - err = compare_images(expected_fname, actual_fname, self.tol, in_decorator=True) + err = compare_images(expected_fname, actual_fname, self._tol, in_decorator=True) if err: raise ImageComparisonFailure( 'images not close: %(actual)s vs. %(expected)s ' @@ -157,13 +160,20 @@ def compare_images_decorator(func): # "teardown_class" methods. Creating a class instance doesn't # work, so we use new.classobj to actually create a class and # fill it with the appropriate methods. + name = func.__name__ + # For nose 1.0, we need to rename the test function to + # something without the word "test", or it will be run as + # well, outside of the context of our image comparison test + # generator. + func = staticmethod(func) + func.__func__.__name__ = '_private' new_class = new.classobj( - func.__name__, + name, (ImageComparisonTest,), - {'func': staticmethod(func), - 'baseline_images': baseline_images, - 'extensions': extensions, - 'tol': tol}) + {'_func': func, + '_baseline_images': baseline_images, + '_extensions': extensions, + '_tol': tol}) return new_class return compare_images_decorator From be6af2fcbefc4df113fb073bb5955f23b73d517b Mon Sep 17 00:00:00 2001 From: Paul Ivanov Date: Thu, 28 Apr 2011 01:16:39 -0700 Subject: [PATCH 07/10] obscure way of getting to the function, but works in 2.5 see http://bugs.python.org/issue5982 for the source --- lib/matplotlib/testing/decorators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 6de2daccf57c..4beffe9fbd67 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -68,7 +68,7 @@ def test(self): def cleanup(func): name = func.__name__ func = staticmethod(func) - func.__func__.__name__ = '_private' + func.__get__(1).__name__ = '_private' new_class = new.classobj( name, (CleanupTest,), @@ -166,7 +166,7 @@ def compare_images_decorator(func): # well, outside of the context of our image comparison test # generator. func = staticmethod(func) - func.__func__.__name__ = '_private' + func.__get__(1).__name__ = '_private' new_class = new.classobj( name, (ImageComparisonTest,), From 5e50b908bd46a789cf6afc657da236b275d61ce1 Mon Sep 17 00:00:00 2001 From: Paul Ivanov Date: Thu, 28 Apr 2011 09:37:23 -0700 Subject: [PATCH 08/10] reverting testing/compare.py back to master --- lib/matplotlib/testing/compare.py | 74 +++++++------------------------ 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index 72c0b625f132..0fff2a760092 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -83,22 +83,6 @@ def compare_float( expected, actual, relTol = None, absTol = None ): # convert files with that extension to png format. converter = { } -def make_external_conversion_command(cmd): - def convert(*args): - cmdline = cmd(*args) - oldname, newname = args - pipe = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = pipe.communicate() - errcode = pipe.wait() - if not os.path.exists(newname) or errcode: - msg = "Conversion command failed:\n%s\n" % ' '.join(cmd) - if stdout: - msg += "Standard output:\n%s\n" % stdout - if stderr: - msg += "Standard error:\n%s\n" % stderr - raise IOError, msg - return convert - if matplotlib.checkdep_ghostscript() is not None: # FIXME: make checkdep_ghostscript return the command if sys.platform == 'win32': @@ -108,49 +92,13 @@ def convert(*args): cmd = lambda old, new: \ [gs, '-q', '-sDEVICE=png16m', '-dNOPAUSE', '-dBATCH', '-sOutputFile=' + new, old] - converter['pdf'] = make_external_conversion_command(cmd) - converter['eps'] = make_external_conversion_command(cmd) + converter['pdf'] = cmd + converter['eps'] = cmd if matplotlib.checkdep_inkscape() is not None: - class SVGConverter: - def __init__(self): - self._count = 0 - self._process = None - - def get_process(self): - # Since Inkscape can leak a little memory, we run X - # conversions and then shut it down and start up a new - # Inkscape. - if self._count == 0: - if self._process is not None: - self._process.communicate('quit\n') - self._process = subprocess.Popen(['inkscape', '-z', '--shell'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - self.read_to_end(self._process.stdout) - self._count = 10 - self._count -= 1 - return self._process - - def __call__(self, old, new): - process = self.get_process() - process.stdin.write('%s --export-png=%s\n' % (old, new)) - process.stdin.flush() - self.read_to_end(process.stdout) - - def read_to_end(self, buf): - ret = '' - lastchar = '' - while True: - char = buf.readline(1) - if char == '>' and lastchar == '\n': - break - ret += char - lastchar = char - return ret - - converter['svg'] = SVGConverter() + cmd = lambda old, new: \ + ['inkscape', old, '--export-png=' + new] + converter['svg'] = cmd def comparable_formats(): '''Returns the list of file formats that compare_images can compare @@ -168,7 +116,17 @@ def convert(filename): newname = base + '_' + extension + '.png' if not os.path.exists(filename): raise IOError, "'%s' does not exist" % filename - converter[extension](filename, newname) + cmd = converter[extension](filename, newname) + pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = pipe.communicate() + errcode = pipe.wait() + if not os.path.exists(newname) or errcode: + msg = "Conversion command failed:\n%s\n" % ' '.join(cmd) + if stdout: + msg += "Standard output:\n%s\n" % stdout + if stderr: + msg += "Standard error:\n%s\n" % stderr + raise IOError, msg return newname verifiers = { } From cc644a8a9b50aab4253aaf7a0d5af08b8079684e Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 28 Apr 2011 12:46:15 -0400 Subject: [PATCH 09/10] Fix indentation. --- lib/matplotlib/testing/decorators.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 6de2daccf57c..1e603a9125a3 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -108,17 +108,17 @@ def test(self): will_fail, fail_msg, known_exception_class=ImageComparisonFailure) def do_test(): - figure.savefig(actual_fname) + figure.savefig(actual_fname) - if not os.path.exists(expected_fname): - raise ImageComparisonFailure( - 'image does not exist: %s' % expected_fname) + if not os.path.exists(expected_fname): + raise ImageComparisonFailure( + 'image does not exist: %s' % expected_fname) - err = compare_images(expected_fname, actual_fname, self._tol, in_decorator=True) - if err: - raise ImageComparisonFailure( - 'images not close: %(actual)s vs. %(expected)s ' - '(RMS %(rms).3f)'%err) + err = compare_images(expected_fname, actual_fname, self._tol, in_decorator=True) + if err: + raise ImageComparisonFailure( + 'images not close: %(actual)s vs. %(expected)s ' + '(RMS %(rms).3f)'%err) yield (do_test,) From bffb15d407deaf092d122815139c3d0a9bf835f3 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 28 Apr 2011 12:51:28 -0400 Subject: [PATCH 10/10] Revert back to using inkscape once for each SVG->PNG conversion, rather than running it as a server, which mysteriously causes a deadlock on some systems. --- lib/matplotlib/testing/compare.py | 42 +++---------------------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index 72c0b625f132..e7fb494b4181 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -112,45 +112,9 @@ def convert(*args): converter['eps'] = make_external_conversion_command(cmd) if matplotlib.checkdep_inkscape() is not None: - class SVGConverter: - def __init__(self): - self._count = 0 - self._process = None - - def get_process(self): - # Since Inkscape can leak a little memory, we run X - # conversions and then shut it down and start up a new - # Inkscape. - if self._count == 0: - if self._process is not None: - self._process.communicate('quit\n') - self._process = subprocess.Popen(['inkscape', '-z', '--shell'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - self.read_to_end(self._process.stdout) - self._count = 10 - self._count -= 1 - return self._process - - def __call__(self, old, new): - process = self.get_process() - process.stdin.write('%s --export-png=%s\n' % (old, new)) - process.stdin.flush() - self.read_to_end(process.stdout) - - def read_to_end(self, buf): - ret = '' - lastchar = '' - while True: - char = buf.readline(1) - if char == '>' and lastchar == '\n': - break - ret += char - lastchar = char - return ret - - converter['svg'] = SVGConverter() + cmd = lambda old, new: \ + ['inkscape', '-z', old, '--export-png', new] + converter['svg'] = make_external_conversion_command(cmd) def comparable_formats(): '''Returns the list of file formats that compare_images can compare