diff --git a/doc/api/api_changes/removed-attributes.rst b/doc/api/api_changes/removed-attributes.rst new file mode 100644 index 000000000000..88af1212a6c7 --- /dev/null +++ b/doc/api/api_changes/removed-attributes.rst @@ -0,0 +1,4 @@ +Removed attributes +`````````````````` +The unused `FONT_SCALE` and `fontd` attributes of the `RendererSVG` class have +been removed. diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 48ac40051f24..a1e775656277 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -31,7 +31,6 @@ from matplotlib import verbose, rcParams, __version__ from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, RendererBase, cursors) -from matplotlib.cbook import maxdict from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index b42f9f1f312f..98a0369f8128 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -19,7 +19,7 @@ _Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer -from matplotlib.cbook import is_writable_file_like, maxdict +from matplotlib.cbook import is_writable_file_like from matplotlib.colors import rgb2hex from matplotlib.figure import Figure from matplotlib.font_manager import findfont, FontProperties, get_font @@ -258,9 +258,6 @@ def generate_css(attrib={}): _capstyle_d = {'projecting' : 'square', 'butt' : 'butt', 'round': 'round',} class RendererSVG(RendererBase): - FONT_SCALE = 100.0 - fontd = maxdict(50) - def __init__(self, width, height, svgwriter, basename=None, image_dpi=72): self.width = width self.height = height diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index e57122150552..7cc4326232b6 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -101,8 +101,7 @@ def _is_nth_color(c): def is_color_like(c): """Return whether *c* can be interpreted as an RGB(A) color.""" - # Special-case nth color syntax because it cannot be parsed during - # setup. + # Special-case nth color syntax because it cannot be parsed during setup. if _is_nth_color(c): return True try: diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index f9b6d9fb2377..9f3bdd02679c 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -983,6 +983,7 @@ def _normalize_font_family(family): return family +@cbook.deprecated("2.2") class TempCache(object): """ A class to store temporary caches that are (a) not saved to disk @@ -1268,6 +1269,20 @@ def findfont(self, prop, fontext='ttf', directory=None, `_ documentation for a description of the font finding algorithm. """ + # Pass the relevant rcParams (and the font manager, as `self`) to + # _findfont_cached so to prevent using a stale cache entry after an + # rcParam was changed. + rc_params = tuple(tuple(rcParams[key]) for key in [ + "font.serif", "font.sans-serif", "font.cursive", "font.fantasy", + "font.monospace"]) + return self._findfont_cached( + prop, fontext, directory, fallback_to_default, rebuild_if_missing, + rc_params) + + @lru_cache() + def _findfont_cached(self, prop, fontext, directory, fallback_to_default, + rebuild_if_missing, rc_params): + if not isinstance(prop, FontProperties): prop = FontProperties(prop) fname = prop.get_file() @@ -1280,11 +1295,7 @@ def findfont(self, prop, fontext='ttf', directory=None, else: fontlist = self.ttflist - if directory is None: - cached = _lookup_cache[fontext].get(prop) - if cached is not None: - return cached - else: + if directory is not None: directory = os.path.normcase(directory) best_score = 1e64 @@ -1342,11 +1353,9 @@ def findfont(self, prop, fontext='ttf', directory=None, else: raise ValueError("No valid font could be found") - if directory is None: - _lookup_cache[fontext].set(prop, result) return result -_is_opentype_cff_font_cache = {} +@lru_cache() def is_opentype_cff_font(filename): """ Returns True if the given font is a Postscript Compact Font Format @@ -1354,14 +1363,10 @@ def is_opentype_cff_font(filename): PDF backends that can not subset these fonts. """ if os.path.splitext(filename)[1].lower() == '.otf': - result = _is_opentype_cff_font_cache.get(filename) - if result is None: - with open(filename, 'rb') as fd: - tag = fd.read(4) - result = (tag == b'OTTO') - _is_opentype_cff_font_cache[filename] = result - return result - return False + with open(filename, 'rb') as fd: + return fd.read(4) == b"OTTO" + else: + return False fontManager = None _fmcache = None @@ -1424,11 +1429,6 @@ def findfont(prop, fontext='ttf'): fontManager = None - _lookup_cache = { - 'ttf': TempCache(), - 'afm': TempCache() - } - def _rebuild(): global fontManager diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index 53f2c6352fc5..e1985f5cbc74 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -25,6 +25,11 @@ import unicodedata from warnings import warn +try: + from functools import lru_cache +except ImportError: # Py2 + from backports.functools_lru_cache import lru_cache + from numpy import inf, isinf import numpy as np @@ -3257,8 +3262,8 @@ def __init__(self, output): Create a MathTextParser for the given backend *output*. """ self._output = output.lower() - self._cache = maxdict(50) + @lru_cache(50) def parse(self, s, dpi = 72, prop = None): """ Parse the given math expression *s* at the given *dpi*. If @@ -3270,16 +3275,10 @@ def parse(self, s, dpi = 72, prop = None): The results are cached, so multiple calls to :meth:`parse` with the same expression should be fast. """ - # There is a bug in Python 3.x where it leaks frame references, - # and therefore can't handle this caching + if prop is None: prop = FontProperties() - cacheKey = (s, dpi, hash(prop)) - result = self._cache.get(cacheKey) - if result is not None: - return result - if self._output == 'ps' and rcParams['ps.useafm']: font_output = StandardPsFonts(prop) else: @@ -3302,9 +3301,7 @@ def parse(self, s, dpi = 72, prop = None): box = self._parser.parse(s, font_output, fontsize, dpi) font_output.set_canvas_size(box.width, box.height, box.depth) - result = font_output.get_results(box) - self._cache[cacheKey] = result - return result + return font_output.get_results(box) def to_mask(self, texstr, dpi=120, fontsize=14): """ diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 86263112a9e3..277f46b4bcc1 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -20,6 +20,11 @@ import math from weakref import WeakValueDictionary +try: + from functools import lru_cache +except ImportError: # Py2 + from backports.functools_lru_cache import lru_cache + import numpy as np from . import _path, rcParams @@ -942,27 +947,17 @@ def wedge(cls, theta1, theta2, n=None): """ return cls.arc(theta1, theta2, n, True) - _hatch_dict = maxdict(8) - - @classmethod - def hatch(cls, hatchpattern, density=6): + @staticmethod + @lru_cache(8) + def hatch(hatchpattern, density=6): """ Given a hatch specifier, *hatchpattern*, generates a Path that can be used in a repeated hatching pattern. *density* is the number of lines per unit square. """ from matplotlib.hatch import get_path - - if hatchpattern is None: - return None - - hatch_path = cls._hatch_dict.get((hatchpattern, density)) - if hatch_path is not None: - return hatch_path - - hatch_path = get_path(hatchpattern, density) - cls._hatch_dict[(hatchpattern, density)] = hatch_path - return hatch_path + return (get_path(hatchpattern, density) + if hatchpattern is not None else None) def clip_to_bbox(self, bbox, inside=True): """ diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py index f845a9051443..dfedd2c2b88b 100644 --- a/lib/matplotlib/textpath.py +++ b/lib/matplotlib/textpath.py @@ -33,9 +33,6 @@ class TextToPath(object): DPI = 72 def __init__(self): - """ - Initialization - """ self.mathtext_parser = MathTextParser('path') self.tex_font_map = None @@ -492,8 +489,7 @@ def _revalidate_path(self): necessary. """ - if (self._invalid or - (self._cached_vertices is None)): + if self._invalid or self._cached_vertices is None: tr = Affine2D().scale( self._size / text_to_path.FONT_SCALE, self._size / text_to_path.FONT_SCALE).translate(*self._xy)