From 957271f83f80aff2878c4ce0785d0fa2eb54b229 Mon Sep 17 00:00:00 2001 From: Michael Hinton Date: Fri, 13 Sep 2024 14:52:37 -0700 Subject: [PATCH 1/2] Resolve configdir to handle potential issues with inaccessible symlinks There are some use cases where a user might have a symlinked home directory (e.g. a corporate home directory may be symlinked to a disk with limited space, and is only accessible to other users via a real path to the underlying disk). Resolving configdir before any mkdir or access checks should avoid this problem. Co-authored-by: Greg Lucas --- lib/matplotlib/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index b20af9108bd0..a8d876c30afa 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -519,13 +519,15 @@ def _get_xdg_cache_dir(): def _get_config_or_cache_dir(xdg_base_getter): configdir = os.environ.get('MPLCONFIGDIR') if configdir: - configdir = Path(configdir).resolve() + configdir = Path(configdir) elif sys.platform.startswith(('linux', 'freebsd')): # Only call _xdg_base_getter here so that MPLCONFIGDIR is tried first, # as _xdg_base_getter can throw. configdir = Path(xdg_base_getter(), "matplotlib") else: configdir = Path.home() / ".matplotlib" + # Resolve the path to handle potential issues with inaccessible symlinks. + configdir = configdir.resolve() try: configdir.mkdir(parents=True, exist_ok=True) except OSError: From 8837d9ad97881cb4c4095a04711b32209420cb0b Mon Sep 17 00:00:00 2001 From: Michael Hinton Date: Fri, 13 Sep 2024 23:18:51 -0700 Subject: [PATCH 2/2] Print warning when mkdir fails instead of silently ignoring the error For example, a user could have no more disk space, causing mkdir to fail, but not realize it because the error is silently discarded, and the subsequent warning would not indicate the actual issue. --- lib/matplotlib/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index a8d876c30afa..23643a53d811 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -530,26 +530,27 @@ def _get_config_or_cache_dir(xdg_base_getter): configdir = configdir.resolve() try: configdir.mkdir(parents=True, exist_ok=True) - except OSError: - pass + except OSError as exc: + _log.warning("mkdir -p failed for path %s: %s", configdir, exc) else: if os.access(str(configdir), os.W_OK) and configdir.is_dir(): return str(configdir) + _log.warning("%s is not a writable directory", configdir) # If the config or cache directory cannot be created or is not a writable # directory, create a temporary one. try: tmpdir = tempfile.mkdtemp(prefix="matplotlib-") except OSError as exc: raise OSError( - f"Matplotlib requires access to a writable cache directory, but the " - f"default path ({configdir}) is not a writable directory, and a temporary " + f"Matplotlib requires access to a writable cache directory, but there " + f"was an issue with the default path ({configdir}), and a temporary " f"directory could not be created; set the MPLCONFIGDIR environment " f"variable to a writable directory") from exc os.environ["MPLCONFIGDIR"] = tmpdir atexit.register(shutil.rmtree, tmpdir) _log.warning( - "Matplotlib created a temporary cache directory at %s because the default path " - "(%s) is not a writable directory; it is highly recommended to set the " + "Matplotlib created a temporary cache directory at %s because there was " + "an issue with the default path (%s); it is highly recommended to set the " "MPLCONFIGDIR environment variable to a writable directory, in particular to " "speed up the import of Matplotlib and to better support multiprocessing.", tmpdir, configdir)