From 40f642f758534d27f9fc0e281993a39af6977bcc Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 11 Jul 2024 15:56:10 -0400 Subject: [PATCH] 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 --- lib/matplotlib/font_manager.py | 8 ++++---- lib/matplotlib/tests/test_font_manager.py | 24 ++++++++++++++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 813bee6eb623..d9560ec0cc0f 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -965,11 +965,11 @@ def json_dump(data, filename): This function temporarily locks the output file to prevent multiple processes from overwriting one another's output. """ - with cbook._lock_path(filename), open(filename, 'w') as fh: - try: + try: + with cbook._lock_path(filename), open(filename, 'w') as fh: json.dump(data, fh, cls=_JSONEncoder, indent=2) - except OSError as e: - _log.warning('Could not save font_manager cache %s', e) + except OSError as e: + _log.warning('Could not save font_manager cache %s', e) def json_load(filename): diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 2dc530bf984b..c6fc422ca613 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -16,7 +16,7 @@ json_dump, json_load, get_font, is_opentype_cff_font, MSUserFontDirectories, _get_fontconfig_fonts, ttfFontProperty) from matplotlib import cbook, ft2font, pyplot as plt, rc_context, figure as mfigure -from matplotlib.testing import subprocess_run_helper +from matplotlib.testing import subprocess_run_helper, subprocess_run_for_testing has_fclist = shutil.which('fc-list') is not None @@ -287,6 +287,28 @@ def test_fontcache_thread_safe(): subprocess_run_helper(_test_threading, timeout=10) +def test_lockfilefailure(tmp_path): + # The logic here: + # 1. get a temp directory from pytest + # 2. import matplotlib which makes sure it exists + # 3. get the cache dir (where we check it is writable) + # 4. make it not writable + # 5. try to write into it via font manager + proc = subprocess_run_for_testing( + [ + sys.executable, + "-c", + "import matplotlib;" + "import os;" + "p = matplotlib.get_cachedir();" + "os.chmod(p, 0o555);" + "import matplotlib.font_manager;" + ], + env={**os.environ, 'MPLCONFIGDIR': str(tmp_path)}, + check=True + ) + + def test_fontentry_dataclass(): fontent = FontEntry(name='font-name')