Skip to content

Commit 40f642f

Browse files
committed
MNT: be more careful about disk I/O failures when writing font cache
The locker works by writing a file to disk. This can also fail so make sure we can still import in that case. Closes #28538
1 parent 6b7b999 commit 40f642f

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

lib/matplotlib/font_manager.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -965,11 +965,11 @@ def json_dump(data, filename):
965965
This function temporarily locks the output file to prevent multiple
966966
processes from overwriting one another's output.
967967
"""
968-
with cbook._lock_path(filename), open(filename, 'w') as fh:
969-
try:
968+
try:
969+
with cbook._lock_path(filename), open(filename, 'w') as fh:
970970
json.dump(data, fh, cls=_JSONEncoder, indent=2)
971-
except OSError as e:
972-
_log.warning('Could not save font_manager cache %s', e)
971+
except OSError as e:
972+
_log.warning('Could not save font_manager cache %s', e)
973973

974974

975975
def json_load(filename):

lib/matplotlib/tests/test_font_manager.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
json_dump, json_load, get_font, is_opentype_cff_font,
1717
MSUserFontDirectories, _get_fontconfig_fonts, ttfFontProperty)
1818
from matplotlib import cbook, ft2font, pyplot as plt, rc_context, figure as mfigure
19-
from matplotlib.testing import subprocess_run_helper
19+
from matplotlib.testing import subprocess_run_helper, subprocess_run_for_testing
2020

2121

2222
has_fclist = shutil.which('fc-list') is not None
@@ -287,6 +287,28 @@ def test_fontcache_thread_safe():
287287
subprocess_run_helper(_test_threading, timeout=10)
288288

289289

290+
def test_lockfilefailure(tmp_path):
291+
# The logic here:
292+
# 1. get a temp directory from pytest
293+
# 2. import matplotlib which makes sure it exists
294+
# 3. get the cache dir (where we check it is writable)
295+
# 4. make it not writable
296+
# 5. try to write into it via font manager
297+
proc = subprocess_run_for_testing(
298+
[
299+
sys.executable,
300+
"-c",
301+
"import matplotlib;"
302+
"import os;"
303+
"p = matplotlib.get_cachedir();"
304+
"os.chmod(p, 0o555);"
305+
"import matplotlib.font_manager;"
306+
],
307+
env={**os.environ, 'MPLCONFIGDIR': str(tmp_path)},
308+
check=True
309+
)
310+
311+
290312
def test_fontentry_dataclass():
291313
fontent = FontEntry(name='font-name')
292314

0 commit comments

Comments
 (0)