diff --git a/doc/api/api_changes/2017-06-03-ES_unique_renderer.rst b/doc/api/api_changes/2017-06-03-ES_unique_renderer.rst new file mode 100644 index 000000000000..3dd58e0a16f3 --- /dev/null +++ b/doc/api/api_changes/2017-06-03-ES_unique_renderer.rst @@ -0,0 +1,11 @@ +Unique identifier added to `RendererBase` classes +````````````````````````````````````````````````` + +Since ``id()`` is not guaranteed to be unique between objects that exist at +different times, a new private property ``_uid`` has been added to +`RendererBase` which is used along with the renderer's ``id()`` to cache +certain expensive operations. + +If a custom renderer does not subclass `RendererBase` or `MixedModeRenderer`, +it is not required to implement this ``_uid`` property, but this may produce +incorrect behavior when the renderers' ``id()`` clashes. diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 3438210023f1..7331d71f8014 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -41,6 +41,7 @@ from contextlib import contextmanager import importlib import io +import itertools import os import sys import time @@ -100,6 +101,12 @@ } +# Used to ensure that caching based on renderer id() is unique without being as +# expensive as a real UUID. 0 is used for renderers that don't derive from +# here, so start at 1. +_unique_renderer_id = itertools.count(1) + + def register_backend(format, backend, description=None): """ Register a backend for saving to a given file format. @@ -212,6 +219,10 @@ class RendererBase(object): """ def __init__(self): + # A lightweight id for unique-ification purposes. Along with id(self), + # the combination should be unique enough to use as part of a cache key. + self._uid = next(_unique_renderer_id) + self._texmanager = None self._text2path = textpath.TextToPath() diff --git a/lib/matplotlib/backends/backend_mixed.py b/lib/matplotlib/backends/backend_mixed.py index 40d7fd64398c..a93ef062f279 100644 --- a/lib/matplotlib/backends/backend_mixed.py +++ b/lib/matplotlib/backends/backend_mixed.py @@ -5,6 +5,7 @@ import six +import matplotlib.backend_bases from matplotlib.backends.backend_agg import RendererAgg from matplotlib.tight_bbox import process_figure_for_rasterizing @@ -49,6 +50,9 @@ def __init__(self, figure, width, height, dpi, vector_renderer, if raster_renderer_class is None: raster_renderer_class = RendererAgg + # See matplotlib.backend_bases.RendererBase._uid. + self._uid = next(matplotlib.backend_bases._unique_renderer_id) + self._raster_renderer_class = raster_renderer_class self._width = width self._height = height diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index 3529ea8541db..d52fc3efdf66 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -178,6 +178,8 @@ def test_grayscale_alpha(): ax.set_yticks([]) +# This tests tends to hit a TeX cache lock on AppVeyor. +@pytest.mark.flaky(reruns=3) @needs_tex def test_missing_psfont(monkeypatch): """An error is raised if a TeX font lacks a Type-1 equivalent""" diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index 8ead41d67d01..cd6e5feae14a 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -27,6 +27,8 @@ reason="This test needs a TeX installation") +# This tests tends to hit a TeX cache lock on AppVeyor. +@pytest.mark.flaky(reruns=3) @pytest.mark.parametrize('format, use_log, rcParams', [ ('ps', False, {}), needs_ghostscript(('ps', False, {'ps.usedistiller': 'ghostscript'})), diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index f913445ac6c1..544d3ef89201 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -167,9 +167,6 @@ def baseline_images(request, fontset, index): return ['%s_%s_%02d' % (request.param, fontset, index)] -# See #7911 for why these tests are flaky and #7107 for why they are not so -# easy to fix. -@pytest.mark.flaky(reruns=3) @pytest.mark.parametrize('index, test', enumerate(math_tests), ids=[str(index) for index in range(len(math_tests))]) @pytest.mark.parametrize('fontset', @@ -184,9 +181,6 @@ def test_mathtext_rendering(baseline_images, fontset, index, test): horizontalalignment='center', verticalalignment='center') -# See #7911 for why these tests are flaky and #7107 for why they are not so -# easy to fix. -@pytest.mark.flaky(reruns=3) @pytest.mark.parametrize('index, test', enumerate(font_tests), ids=[str(index) for index in range(len(font_tests))]) @pytest.mark.parametrize('fontset', diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index 76d9c1424212..03ac60a3e0d0 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -58,7 +58,7 @@ def test_tight_layout3(): @image_comparison(baseline_images=['tight_layout4'], - freetype_version=('2.4.5', '2.4.9')) + freetype_version=('2.5.5', '2.6.1')) def test_tight_layout4(): 'Test tight_layout for subplot2grid' diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index b11ae28ba0cc..caa5dcdaf73f 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -907,11 +907,12 @@ def get_prop_tup(self, renderer=None): need to know if the text has changed. """ x, y = self.get_unitless_position() + renderer = renderer or self._renderer return (x, y, self.get_text(), self._color, self._verticalalignment, self._horizontalalignment, hash(self._fontproperties), self._rotation, self._rotation_mode, - self.figure.dpi, id(renderer or self._renderer), + self.figure.dpi, id(renderer), getattr(renderer, '_uid', 0), self._linespacing )