Skip to content

Delayed import of pyparsing for fontconfig pattern matching #12915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions lib/matplotlib/fontconfig_pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
"""
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand Down