From 4dd513e343c3dd518a8c11832145f87f0c8fa180 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 2 Dec 2018 00:00:23 +0100 Subject: [PATCH] Delayed import of pyparsing for fontconfig pattern matching --- lib/matplotlib/fontconfig_pattern.py | 37 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/fontconfig_pattern.py b/lib/matplotlib/fontconfig_pattern.py index c47e19bf99dc..3b1ed6c5c90e 100644 --- a/lib/matplotlib/fontconfig_pattern.py +++ b/lib/matplotlib/fontconfig_pattern.py @@ -12,8 +12,6 @@ from functools import lru_cache import re import numpy as np -from pyparsing import (Literal, ZeroOrMore, Optional, Regex, StringEnd, - ParseException, Suppress) family_punc = r'\\\-:,' family_unescape = re.compile(r'\\([%s])' % family_punc).sub @@ -23,6 +21,14 @@ value_unescape = re.compile(r'\\([%s])' % value_punc).sub value_escape = re.compile(r'([%s])' % value_punc).sub +_SIMPLE_PATTERN_CACHE = { + 'cursive': {'family': ['cursive']}, + 'sans': {'family': ['sans']}, + 'monospace': {'family': ['monospace']}, + 'sans:italic': {'family': ['sans'], 'slant': ['italic']}, + 'sans:bold': {'family': ['sans'], 'weight': ['bold']}, +} + class FontconfigPatternParser: """ @@ -60,6 +66,9 @@ class FontconfigPatternParser: } def __init__(self): + # delayed import. Only executed when needed + from pyparsing import (Literal, ZeroOrMore, Optional, Regex, StringEnd, + ParseException, Suppress) family = Regex( r'([^%s]|(\\[%s]))*' % (family_punc, family_punc) @@ -167,11 +176,33 @@ def _property(self, s, loc, tokens): return [] +@lru_cache(maxsize=1) +def _get_parser(): + """Return a cached FontconfigPatternParser instance.""" + return FontconfigPatternParser() + + # `parse_fontconfig_pattern` is a bottleneck during the tests because it is # repeatedly called when the rcParams are reset (to validate the default # fonts). In practice, the cache size doesn't grow beyond a few dozen entries # during the test suite. -parse_fontconfig_pattern = lru_cache()(FontconfigPatternParser().parse) +@lru_cache() +def parse_fontconfig_pattern(pattern): + """ + Parse the given fontconfig *pattern* and return a dictionary + of key/value pairs useful for initializing a + :class:`font_manager.FontProperties` object. + """ + + # Try a lookup on simple patterns first. This can prevent the need to + # create a full parser and to import pyparsing. If only patterns from + # _SIMPLE_PATTERN_CACHE are used in the matplotlibrc file, this will + # speed up the import of matplotlib. + parsed = _SIMPLE_PATTERN_CACHE.get(pattern) + if parsed is not None: + return parsed + + return _get_parser().parse(pattern) def _escape_val(val, escape_func):