Skip to content

Commit 823fd95

Browse files
committed
Delay fc-list warning by 5s.
We do not need to check filename validity in `_call_fc_list` as `findSystemFonts` already does it.
1 parent bf5aeb5 commit 823fd95

File tree

2 files changed

+52
-44
lines changed

2 files changed

+52
-44
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
`afm.get_fontconfig_fonts` returns a list of paths and does not check for existence
2+
```````````````````````````````````````````````````````````````````````````````````
3+
4+
`afm.get_fontconfig_fonts` used to return a set of paths encoded as a
5+
``{key: 1, ...}`` dict, and checked for the existence of the paths. It now
6+
returns a list and dropped the existence check, as the same check is performed
7+
by the caller (`afm.findSystemFonts`) as well.

lib/matplotlib/font_manager.py

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,19 @@
4747
see license/LICENSE_TTFQUERY.
4848
"""
4949

50-
import json
51-
import os, sys, warnings
5250
from collections import Iterable
51+
import json
52+
import os
53+
import sys
54+
from threading import Timer
55+
import warnings
56+
5357
import matplotlib
54-
from matplotlib import afm
55-
from matplotlib import ft2font
56-
from matplotlib import rcParams, get_cachedir
58+
from matplotlib import afm, cbook, ft2font, rcParams, get_cachedir
5759
from matplotlib.cbook import is_string_like
58-
import matplotlib.cbook as cbook
5960
from matplotlib.compat import subprocess
60-
from matplotlib.fontconfig_pattern import \
61-
parse_fontconfig_pattern, generate_fontconfig_pattern
61+
from matplotlib.fontconfig_pattern import (
62+
parse_fontconfig_pattern, generate_fontconfig_pattern)
6263

6364
try:
6465
from functools import lru_cache
@@ -262,39 +263,39 @@ def OSXInstalledFonts(directories=None, fontext='ttf'):
262263
files.extend(list_fonts(path, fontext))
263264
return files
264265

265-
def get_fontconfig_fonts(fontext='ttf'):
266+
267+
@lru_cache()
268+
def _call_fc_list():
269+
"""Cache and list the font filenames known to `fc-list`.
266270
"""
267-
Grab a list of all the fonts that are being tracked by fontconfig
268-
by making a system call to ``fc-list``. This is an easy way to
269-
grab all of the fonts the user wants to be made available to
270-
applications, without needing knowing where all of them reside.
271+
# Delay the warning by 5s.
272+
timer = Timer(5, lambda: warnings.warn(
273+
'Matplotlib is building the font cache using fc-list. '
274+
'This may take a moment.'))
275+
timer.start()
276+
try:
277+
out = subprocess.check_output(['fc-list', '--format=%{file}'])
278+
except (OSError, subprocess.CalledProcessError):
279+
return []
280+
finally:
281+
timer.cancel()
282+
fnames = []
283+
for fname in out.split(b'\n'):
284+
try:
285+
fname = six.text_type(fname, sys.getfilesystemencoding())
286+
except UnicodeDecodeError:
287+
continue
288+
fnames.append(fname)
289+
return fnames
290+
291+
292+
def get_fontconfig_fonts(fontext='ttf'):
293+
"""List the font filenames known to `fc-list` having the given extension.
271294
"""
272295
fontext = get_fontext_synonyms(fontext)
296+
return [fname for fname in _call_fc_list()
297+
if os.path.splitext(fname)[1][1:] in fontext]
273298

274-
fontfiles = {}
275-
try:
276-
warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
277-
pipe = subprocess.Popen(['fc-list', '--format=%{file}\\n'],
278-
stdout=subprocess.PIPE,
279-
stderr=subprocess.PIPE)
280-
output = pipe.communicate()[0]
281-
except (OSError, IOError):
282-
# Calling fc-list did not work, so we'll just return nothing
283-
return fontfiles
284-
285-
if pipe.returncode == 0:
286-
# The line breaks between results are in ascii, but each entry
287-
# is in in sys.filesystemencoding().
288-
for fname in output.split(b'\n'):
289-
try:
290-
fname = six.text_type(fname, sys.getfilesystemencoding())
291-
except UnicodeDecodeError:
292-
continue
293-
if (os.path.splitext(fname)[1][1:] in fontext and
294-
os.path.exists(fname)):
295-
fontfiles[fname] = 1
296-
297-
return fontfiles
298299

299300
def findSystemFonts(fontpaths=None, fontext='ttf'):
300301
"""
@@ -304,7 +305,7 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
304305
available. A list of TrueType fonts are returned by default with
305306
AFM fonts as an option.
306307
"""
307-
fontfiles = {}
308+
fontfiles = set()
308309
fontexts = get_fontext_synonyms(fontext)
309310

310311
if fontpaths is None:
@@ -316,26 +317,26 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
316317
for f in win32InstalledFonts(fontdir):
317318
base, ext = os.path.splitext(f)
318319
if len(ext)>1 and ext[1:].lower() in fontexts:
319-
fontfiles[f] = 1
320+
fontfiles.add(f)
320321
else:
321322
fontpaths = X11FontDirectories
322323
# check for OS X & load its fonts if present
323324
if sys.platform == 'darwin':
324325
for f in OSXInstalledFonts(fontext=fontext):
325-
fontfiles[f] = 1
326+
fontfiles.add(f)
326327

327328
for f in get_fontconfig_fonts(fontext):
328-
fontfiles[f] = 1
329+
fontfiles.add(f)
329330

330331
elif isinstance(fontpaths, six.string_types):
331332
fontpaths = [fontpaths]
332333

333334
for path in fontpaths:
334335
files = list_fonts(path, fontexts)
335336
for fname in files:
336-
fontfiles[os.path.abspath(fname)] = 1
337+
fontfiles.add(os.path.abspath(fname))
337338

338-
return [fname for fname in six.iterkeys(fontfiles) if os.path.exists(fname)]
339+
return [fname for fname in fontfiles if os.path.exists(fname)]
339340

340341
def weight_as_number(weight):
341342
"""
@@ -833,7 +834,7 @@ def set_family(self, family):
833834
family = rcParams['font.family']
834835
if is_string_like(family):
835836
family = [six.text_type(family)]
836-
elif (not is_string_like(family) and isinstance(family, Iterable)):
837+
elif not is_string_like(family) and isinstance(family, Iterable):
837838
family = [six.text_type(f) for f in family]
838839
self._family = family
839840
set_name = set_family

0 commit comments

Comments
 (0)