Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d9ad3ba

Browse files
committed
Cache paths of fonts shipped with mpl relative to the mpl data path.
This lets the font cache stay valid across multiple virtualenvs (as long as the list of fonts shipped by mpl does not change).
1 parent 634640f commit d9ad3ba

File tree

1 file changed

+49
-27
lines changed

1 file changed

+49
-27
lines changed

lib/matplotlib/font_manager.py

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from threading import Timer
4343
import warnings
4444

45+
import matplotlib as mpl
4546
from matplotlib import afm, cbook, ft2font, rcParams, get_cachedir
4647
from matplotlib.fontconfig_pattern import (
4748
parse_fontconfig_pattern, generate_fontconfig_pattern)
@@ -831,15 +832,23 @@ def copy(self):
831832
class JSONEncoder(json.JSONEncoder):
832833
def default(self, o):
833834
if isinstance(o, FontManager):
834-
return dict(o.__dict__, _class='FontManager')
835+
return dict(o.__dict__, __class__='FontManager')
835836
elif isinstance(o, FontEntry):
836-
return dict(o.__dict__, _class='FontEntry')
837+
d = dict(o.__dict__, __class__='FontEntry')
838+
try:
839+
# Cache paths of fonts shipped with mpl relative to the mpl
840+
# data path, which helps in the presence of venvs.
841+
d["fname"] = str(
842+
Path(d["fname"]).relative_to(mpl.get_data_path()))
843+
except ValueError:
844+
pass
845+
return d
837846
else:
838847
return super().default(o)
839848

840849

841850
def _json_decode(o):
842-
cls = o.pop('_class', None)
851+
cls = o.pop('__class__', None)
843852
if cls is None:
844853
return o
845854
elif cls == 'FontManager':
@@ -849,26 +858,37 @@ def _json_decode(o):
849858
elif cls == 'FontEntry':
850859
r = FontEntry.__new__(FontEntry)
851860
r.__dict__.update(o)
861+
if not os.path.isabs(r.fname):
862+
r.fname = os.path.join(mpl.get_data_path(), r.fname)
852863
return r
853864
else:
854-
raise ValueError("don't know how to deserialize _class=%s" % cls)
865+
raise ValueError("don't know how to deserialize __class__=%s" % cls)
855866

856867

857868
def json_dump(data, filename):
858-
"""Dumps a data structure as JSON in the named file.
859-
Handles FontManager and its fields."""
869+
"""
870+
Dumps a data structure as JSON in the named file.
860871
872+
Handles FontManager and its fields. File paths that are children of the
873+
Matplotlib data path (typically, fonts shipped with Matplotlib) are stored
874+
relative to that data path (to remain valid across virtualenvs).
875+
"""
861876
with open(filename, 'w') as fh:
862877
try:
863878
json.dump(data, fh, cls=JSONEncoder, indent=2)
864879
except OSError as e:
865880
warnings.warn('Could not save font_manager cache {}'.format(e))
866881

867882

883+
868884
def json_load(filename):
869-
"""Loads a data structure as JSON from the named file.
870-
Handles FontManager and its fields."""
885+
"""
886+
Loads a data structure as JSON from the named file.
871887
888+
Handles FontManager and its fields. Relative file paths are interpreted
889+
as being relative to the Matplotlib data path, and transformed into
890+
absolute paths.
891+
"""
872892
with open(filename, 'r') as fh:
873893
return json.load(fh, object_hook=_json_decode)
874894

@@ -954,30 +974,32 @@ def __init__(self, size=None, weight='normal'):
954974
_log.info('font search path %s', str(paths))
955975
# Load TrueType fonts and create font dictionary.
956976

957-
self.ttffiles = findSystemFonts(paths) + findSystemFonts()
958977
self.defaultFamily = {
959978
'ttf': 'DejaVu Sans',
960979
'afm': 'Helvetica'}
961980
self.defaultFont = {}
962981

963-
for fname in self.ttffiles:
964-
_log.debug('trying fontname %s', fname)
965-
if fname.lower().find('DejaVuSans.ttf')>=0:
966-
self.defaultFont['ttf'] = fname
967-
break
968-
else:
969-
# use anything
970-
self.defaultFont['ttf'] = self.ttffiles[0]
971-
972-
self.ttflist = createFontList(self.ttffiles)
973-
974-
self.afmfiles = (findSystemFonts(paths, fontext='afm')
975-
+ findSystemFonts(fontext='afm'))
976-
self.afmlist = createFontList(self.afmfiles, fontext='afm')
977-
if len(self.afmfiles):
978-
self.defaultFont['afm'] = self.afmfiles[0]
979-
else:
980-
self.defaultFont['afm'] = None
982+
ttffiles = findSystemFonts(paths) + findSystemFonts()
983+
self.defaultFont['ttf'] = next(
984+
(fname for fname in ttffiles
985+
if fname.lower().endswith("dejavusans.ttf")),
986+
ttffiles[0])
987+
self.ttflist = createFontList(ttffiles)
988+
989+
afmfiles = (findSystemFonts(paths, fontext='afm')
990+
+ findSystemFonts(fontext='afm'))
991+
self.afmlist = createFontList(afmfiles, fontext='afm')
992+
self.defaultFont['afm'] = afmfiles[0] if afmfiles else None
993+
994+
@property
995+
@cbook.deprecated("3.0")
996+
def ttffiles(self):
997+
return [font.fname for font in self.ttflist]
998+
999+
@property
1000+
@cbook.deprecated("3.0")
1001+
def afmfiles(self):
1002+
return [font.fname for font in self.afmlist]
9811003

9821004
def get_default_weight(self):
9831005
"""

0 commit comments

Comments
 (0)