Repository: matplotlib/matplotlib

Issue:
# [ENH]: move .matplotlib folder from %USERPROFILE% on Windows

### Problem

The folder contains "fontlist-v330.json" in my case.

### Proposed solution

Move the directory to a subdirectory in %APPDATA%, where it belongs (as the name suggests).

### Additional context and prior art

_No response_

Implementation patch:
diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py
index 6b7296e85dca..b4f3cc7d21df 100644
--- a/lib/matplotlib/__init__.py
+++ b/lib/matplotlib/__init__.py
@@ -536,6 +536,28 @@ def _get_config_or_cache_dir(xdg_base_getter):
             configdir = Path(xdg_base_getter(), "matplotlib")
         except RuntimeError:  # raised if Path.home() is not available
             pass
+    elif sys.platform == 'win32':
+        # On Windows, prefer %LOCALAPPDATA%\matplotlib which is the proper
+        # location for non-roaming application data (cache and config).
+        # See: https://docs.microsoft.com/en-us/windows/apps/design/app-settings/store-and-retrieve-app-data
+        #
+        # However, for backwards compatibility, if the old location
+        # (%USERPROFILE%\.matplotlib) exists, continue using it so existing
+        # users don't lose their config.
+        try:
+            old_configdir = Path.home() / ".matplotlib"
+            if old_configdir.is_dir():
+                configdir = old_configdir
+            else:
+                localappdata = os.environ.get('LOCALAPPDATA')
+                if localappdata:
+                    configdir = Path(localappdata) / "matplotlib"
+                else:
+                    configdir = old_configdir
+        except RuntimeError:  # raised if Path.home() is not available
+            localappdata = os.environ.get('LOCALAPPDATA')
+            if localappdata:
+                configdir = Path(localappdata) / "matplotlib"
     else:
         try:
             configdir = Path.home() / ".matplotlib"
@@ -586,8 +608,9 @@ def get_configdir():
 
     1. If the MPLCONFIGDIR environment variable is supplied, choose that.
     2. On Linux, follow the XDG specification and look first in
-       ``$XDG_CONFIG_HOME``, if defined, or ``$HOME/.config``.  On other
-       platforms, choose ``$HOME/.matplotlib``.
+       ``$XDG_CONFIG_HOME``, if defined, or ``$HOME/.config``.  On Windows,
+       use ``%LOCALAPPDATA%\\matplotlib``.  On other platforms, choose
+       ``$HOME/.matplotlib``.
     3. If the chosen directory exists and is writable, use that as the
        configuration directory.
     4. Else, create a temporary directory, and use it as the configuration
@@ -602,7 +625,8 @@ def get_cachedir():
     Return the string path of the cache directory.
 
     The procedure used to find the directory is the same as for
-    `get_configdir`, except using ``$XDG_CACHE_HOME``/``$HOME/.cache`` instead.
+    `get_configdir`, except using ``$XDG_CACHE_HOME``/``$HOME/.cache`` instead
+    on Linux.  On Windows, uses ``%LOCALAPPDATA%\\matplotlib`` (same as config).
     """
     return _get_config_or_cache_dir(_get_xdg_cache_dir)
 


Test patch:
diff --git a/lib/matplotlib/tests/test_matplotlib.py b/lib/matplotlib/tests/test_matplotlib.py
index d0a3f8c617e1..f17dc2e42354 100644
--- a/lib/matplotlib/tests/test_matplotlib.py
+++ b/lib/matplotlib/tests/test_matplotlib.py
@@ -94,3 +94,49 @@ def test_get_executable_info_timeout(mock_check_output):
 
     with pytest.raises(matplotlib.ExecutableNotFoundError, match='Timed out'):
         matplotlib._get_executable_info.__wrapped__('inkscape')
+
+
+@pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test")
+def test_configdir_uses_localappdata_on_windows(tmp_path):
+    """Test that on Windows, config/cache dir uses LOCALAPPDATA for fresh installs."""
+    localappdata = tmp_path / "AppData/Local"
+    localappdata.mkdir(parents=True)
+    # Set USERPROFILE to tmp_path so the old location check finds nothing
+    fake_home = tmp_path / "home"
+    fake_home.mkdir()
+
+    proc = subprocess_run_for_testing(
+        [sys.executable, "-c",
+         "import matplotlib; print(matplotlib.get_configdir())"],
+        env={**os.environ, "LOCALAPPDATA": str(localappdata),
+             "USERPROFILE": str(fake_home), "MPLCONFIGDIR": ""},
+        capture_output=True, text=True, check=True)
+
+    configdir = proc.stdout.strip()
+    # On Windows with no existing old config, should use LOCALAPPDATA\matplotlib
+    assert configdir == str(localappdata / "matplotlib")
+
+
+@pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test")
+def test_configdir_uses_userprofile_on_windows_if_exists(tmp_path):
+    """
+    Test that on Windows, config/cache dir uses %USERPROFILE% if .matplotlib
+    exists.
+    """
+    localappdata = tmp_path / "AppData/Local"
+    localappdata.mkdir(parents=True)
+    fake_home = tmp_path / "home"
+    fake_home.mkdir()
+    old_configdir = fake_home / ".matplotlib"
+    old_configdir.mkdir()
+
+    proc = subprocess_run_for_testing(
+        [sys.executable, "-c",
+         "import matplotlib; print(matplotlib.get_configdir())"],
+        env={**os.environ, "LOCALAPPDATA": str(localappdata),
+             "USERPROFILE": str(fake_home), "MPLCONFIGDIR": ""},
+        capture_output=True, text=True, check=True)
+
+    configdir = proc.stdout.strip()
+    # On Windows with existing old config, should continue using it
+    assert configdir == str(old_configdir)