diff --git a/README.md b/README.md
index 7cd5d88e..e2627bd7 100644
--- a/README.md
+++ b/README.md
@@ -45,14 +45,26 @@ user_config_path("MyApp", "MyCompany") # returns pathlib.Path
## Directory types
+**Application directories** — scoped to your app name and version:
+
- **Data**: Persistent application data (`user_data_dir`, `site_data_dir`)
- **Config**: Configuration files and settings (`user_config_dir`, `site_config_dir`)
+- **Preference**: User preferences, distinct from config on macOS (`user_preference_dir`)
- **Cache**: Cached data that can be regenerated (`user_cache_dir`, `site_cache_dir`)
- **State**: Non-essential runtime state like window positions (`user_state_dir`, `site_state_dir`)
- **Logs**: Log files (`user_log_dir`, `site_log_dir`)
- **Runtime**: Runtime files like sockets and PIDs (`user_runtime_dir`, `site_runtime_dir`)
-Each type has both `user_*` (per-user, writable) and `site_*` (system-wide, read-only for users) variants.
+App dirs have both `user_*` (per-user, writable) and `site_*` (system-wide, read-only) variants where applicable.
+
+**User media directories** — standard user-facing folders, not scoped to app name:
+
+- **Documents** (`user_documents_dir`), **Downloads** (`user_downloads_dir`)
+- **Pictures** (`user_pictures_dir`), **Videos** (`user_videos_dir`), **Music** (`user_music_dir`)
+- **Desktop** (`user_desktop_dir`), **Projects** (`user_projects_dir`)
+- **Public share** (`user_publicshare_dir`), **Templates** (`user_templates_dir`)
+- **Fonts** (`user_fonts_dir`) — user-writable font installation directory
+- **Executable** (`user_bin_dir`, `site_bin_dir`), **Applications** (`user_applications_dir`, `site_applications_dir`)
## Documentation
diff --git a/docs/api.rst b/docs/api.rst
index 927d11a1..9c298768 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -150,6 +150,42 @@ See also: :ref:`platforms:``user_projects_dir```
.. autofunction:: platformdirs.user_projects_path
+User public share directory
+===========================
+
+See also: :ref:`platforms:``user_publicshare_dir```
+
+.. autofunction:: platformdirs.user_publicshare_dir
+
+.. autofunction:: platformdirs.user_publicshare_path
+
+User templates directory
+========================
+
+See also: :ref:`platforms:``user_templates_dir```
+
+.. autofunction:: platformdirs.user_templates_dir
+
+.. autofunction:: platformdirs.user_templates_path
+
+User fonts directory
+====================
+
+See also: :ref:`platforms:``user_fonts_dir```
+
+.. autofunction:: platformdirs.user_fonts_dir
+
+.. autofunction:: platformdirs.user_fonts_path
+
+User preference directory
+=========================
+
+See also: :ref:`platforms:``user_preference_dir```
+
+.. autofunction:: platformdirs.user_preference_dir
+
+.. autofunction:: platformdirs.user_preference_path
+
********************
Shared directories
********************
diff --git a/docs/explanation.rst b/docs/explanation.rst
index 36f98c7e..ccdc09db 100644
--- a/docs/explanation.rst
+++ b/docs/explanation.rst
@@ -17,23 +17,74 @@ purpose. Application authors write platform-agnostic code while end users get pa
Choosing the right directory
******************************
-``platformdirs`` provides different directory types for different kinds of data. Choose based on the data's purpose and
-lifetime.
+The first question is always: who owns this data? **App-internal data** — databases, caches, config files, logs — goes
+in an app dir scoped to your app name. **User-facing data** — files the user would browse to directly — goes in a media
+dir that sits alongside their documents, music, and photos.
+
+Within app dirs, the next question is whether the data is essential. If it can be regenerated, use ``cache`` (fast
+lookups) or ``runtime`` (session-only sockets and PIDs). If it is important but not critical, use ``state`` (window
+positions, recent files). For settings use ``config``; on macOS, ``preference`` gives you the separate
+``~/Library/Preferences`` location that Apple convention expects. Use ``data`` for everything else that must survive app
+updates.
+
+Within media dirs, pick the folder that matches the file's type from the user's perspective — not what your app does
+with it. A font your app installs for the user goes in ``fonts``, not ``data``.
.. mermaid::
flowchart TD
- A[What kind of data?] --> B{Can it be deleted
without data loss?}
- B -- Yes --> C{Is it used to
speed things up?}
- C -- Yes --> D[**cache** dir]
- C -- No --> E{Is it temporary
for this session?}
- E -- Yes --> F[**runtime** dir]
- E -- No --> G[**state** dir]
- B -- No --> H{Is it a
user preference?}
- H -- Yes --> I[**config** dir]
- H -- No --> J{Is it a
log file?}
- J -- Yes --> K[**log** dir]
- J -- No --> L[**data** dir]
+ A([What kind of data?]) --> B{Belongs to the app
or to the user?}
+
+ B -- App internal --> C{Can it be deleted
without data loss?}
+ C -- Yes --> D{Speeds things up?}
+ D -- Yes --> CACHE[cache dir]
+ D -- No --> E{Temporary for
this session only?}
+ E -- Yes --> RUNTIME[runtime dir]
+ E -- No --> STATE[state dir]
+ C -- No --> F{What kind?}
+ F -- Settings / options --> CONFIG[config dir]
+ F -- macOS preferences --> PREF[preference dir]
+ F -- Log file --> LOG[log dir]
+ F -- Everything else --> DATA[data dir]
+
+ B -- User-facing file --> G{File type?}
+ G -- Document / report --> DOC[documents dir]
+ G -- Downloaded content --> DL[downloads dir]
+ G -- Image --> PIC[pictures dir]
+ G -- Video --> VID[videos dir]
+ G -- Audio --> MUS[music dir]
+ G -- Font --> FONT[fonts dir]
+ G -- Template --> TMPL[templates dir]
+ G -- Project / code --> PROJ[projects dir]
+ G -- Desktop shortcut --> DESK[desktop dir]
+ G -- Share with others --> PUB[publicshare dir]
+
+ style A fill:#1e40af,stroke:#1e3a8a,color:#fff
+ style B fill:#d97706,stroke:#b45309,color:#fff
+ style C fill:#d97706,stroke:#b45309,color:#fff
+ style D fill:#d97706,stroke:#b45309,color:#fff
+ style E fill:#d97706,stroke:#b45309,color:#fff
+ style F fill:#d97706,stroke:#b45309,color:#fff
+ style G fill:#d97706,stroke:#b45309,color:#fff
+
+ style CACHE fill:#2563eb,stroke:#1d4ed8,color:#fff
+ style RUNTIME fill:#2563eb,stroke:#1d4ed8,color:#fff
+ style STATE fill:#2563eb,stroke:#1d4ed8,color:#fff
+ style CONFIG fill:#2563eb,stroke:#1d4ed8,color:#fff
+ style PREF fill:#7c3aed,stroke:#6d28d9,color:#fff
+ style LOG fill:#2563eb,stroke:#1d4ed8,color:#fff
+ style DATA fill:#2563eb,stroke:#1d4ed8,color:#fff
+
+ style DOC fill:#16a34a,stroke:#15803d,color:#fff
+ style DL fill:#16a34a,stroke:#15803d,color:#fff
+ style PIC fill:#16a34a,stroke:#15803d,color:#fff
+ style VID fill:#16a34a,stroke:#15803d,color:#fff
+ style MUS fill:#16a34a,stroke:#15803d,color:#fff
+ style FONT fill:#16a34a,stroke:#15803d,color:#fff
+ style TMPL fill:#16a34a,stroke:#15803d,color:#fff
+ style PROJ fill:#16a34a,stroke:#15803d,color:#fff
+ style DESK fill:#16a34a,stroke:#15803d,color:#fff
+ style PUB fill:#16a34a,stroke:#15803d,color:#fff
Data directories
================
@@ -141,6 +192,102 @@ Use ``user_log_dir`` and ``site_log_dir`` for application logs:
format="%(asctime)s - %(levelname)s - %(message)s",
)
+************************
+ User media directories
+************************
+
+Unlike app dirs (data, config, cache, etc.), media dirs are **not** scoped to the app name. They point to standard
+user-facing folders that exist independently of any particular application. Use them when your app needs to read from or
+save into a folder the user already expects — not when storing application state.
+
+The distinction matters:
+
+- ``user_data_dir("MyApp")`` → ``~/.local/share/MyApp`` — your app's private storage
+- ``user_documents_dir()`` → ``~/Documents`` — the user's document library
+
+On Linux, media dirs are defined by the `XDG user-dirs specification
+`_ and stored in ``~/.config/user-dirs.dirs``. The
+``xdg-user-dirs`` tool lets users relocate them. Set the corresponding environment variable (``XDG_DOCUMENTS_DIR``,
+``XDG_DOWNLOAD_DIR``, etc.) to override on a per-session basis. On macOS and Windows, ``platformdirs`` returns the
+platform-conventional location.
+
+Media and user-facing directories
+=================================
+
+Use these when your app saves or opens files the user should see in their own folders:
+
+``user_documents_dir`` (``XDG_DOCUMENTS_DIR``)
+ Exported reports, user-authored files. Save here when the file is *for the user*, not the app.
+
+``user_downloads_dir`` (``XDG_DOWNLOAD_DIR``)
+ Files fetched from the internet at the user's request.
+
+``user_pictures_dir`` / ``user_videos_dir`` / ``user_music_dir``
+ Platform media libraries. Use when importing or exporting to the user's existing collection.
+
+``user_desktop_dir`` (``XDG_DESKTOP_DIR``)
+ Shortcut files and launchers. Rarely needed directly in code.
+
+``user_projects_dir`` (``XDG_PROJECTS_DIR``)
+ Root directory for the user's coding projects. `Recently added to xdg-user-dirs
+ `_.
+
+``user_publicshare_dir`` (``XDG_PUBLICSHARE_DIR``)
+ Files shared with other local accounts. On Windows this is the machine-wide ``C:\Users\Public`` (``%PUBLIC%``), not
+ a per-user directory.
+
+.. code-block:: python
+
+ from platformdirs import user_documents_path
+
+ report = user_documents_path() / "report.pdf"
+
+Do not use these to store application state or config — if the file would confuse the user when they browse the folder,
+it belongs in ``user_data_dir`` instead.
+
+Templates
+=========
+
+``user_templates_dir`` (``XDG_TEMPLATES_DIR``) points to the folder used by file managers for new-file templates. macOS
+has no platform-defined templates directory; ``~/Templates`` is returned as a pragmatic fallback.
+
+Fonts
+=====
+
+``user_fonts_dir`` points to the per-user font installation directory:
+
+- **Linux**: ``$XDG_DATA_HOME/fonts`` (default ``~/.local/share/fonts``) — derived from ``$XDG_DATA_HOME``, not a
+ dedicated env var. See the `XDG Base Directory Specification
+ `_.
+- **macOS**: ``~/Library/Fonts``
+- **Windows**: ``%LOCALAPPDATA%\Microsoft\Windows\Fonts`` — the per-user font location added in Windows 10
+
+.. code-block:: python
+
+ import shutil
+ from platformdirs import user_fonts_path
+
+ font_dir = user_fonts_path()
+ font_dir.mkdir(parents=True, exist_ok=True)
+ shutil.copy("MyFont.ttf", font_dir / "MyFont.ttf")
+
+**********************
+ Preference directory
+**********************
+
+``user_preference_dir`` is meaningful mainly on macOS, where Apple's conventions distinguish two separate locations:
+
+- ``~/Library/Application Support/AppName`` — long-term application data, databases, plug-ins
+- ``~/Library/Preferences/AppName`` — user-adjustable preference files (historically ``.plist``)
+
+On Linux and Windows, ``user_preference_dir`` is an alias for ``user_config_dir`` — the XDG and Windows conventions make
+no such distinction. On Android, it also aliases ``user_config_dir``.
+
+Use ``user_preference_dir`` when you specifically need to follow Apple's `File System Programming Guide
+`_
+and store preference files in ``~/Library/Preferences``. For most cross-platform applications ``user_config_dir`` is
+sufficient.
+
**************************
User vs site directories
**************************
diff --git a/docs/index.rst b/docs/index.rst
index a894f4d6..e9540b33 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -44,15 +44,30 @@ and Android.
Keep multiple app versions side by side with the ``version`` parameter.
.. toctree::
- :maxdepth: 2
- :caption: Contents
+ :hidden:
+ :caption: Tutorial
tutorial
+
+.. toctree::
+ :hidden:
+ :caption: How-to guides
+
howto
- explanation
+
+.. toctree::
+ :hidden:
+ :caption: Reference
+
parameters
api
platforms
+
+.. toctree::
+ :hidden:
+ :caption: Explanation
+
+ explanation
changelog
********************
diff --git a/docs/platforms.rst b/docs/platforms.rst
index 9f347123..94a2c039 100644
--- a/docs/platforms.rst
+++ b/docs/platforms.rst
@@ -406,6 +406,10 @@ See also: :ref:`api:User desktop directory`
See also: :ref:`api:User projects directory`
+Defined by `$XDG_PROJECTS_DIR
+`_ (recently added
+to xdg-user-dirs).
+
.. tab-set::
.. tab-item:: Linux
@@ -429,6 +433,134 @@ See also: :ref:`api:User projects directory`
``/storage/emulated/0/Projects``
+``user_publicshare_dir``
+========================
+
+See also: :ref:`api:User public share directory`
+
+Defined by `$XDG_PUBLICSHARE_DIR `_.
+
+On Windows, this is the machine-wide ``C:\Users\Public`` (``%PUBLIC%``), shared across all local accounts — not a
+per-user directory. See `FOLDERID_Public `_.
+
+.. tab-set::
+
+ .. tab-item:: Linux
+ :sync: linux
+
+ ``~/Public`` (from ``$XDG_PUBLICSHARE_DIR`` if set, else ``$XDG_PUBLICSHARE_DIR`` entry in
+ ``user-dirs.dirs``, else ``~/Public``)
+
+ .. tab-item:: macOS
+ :sync: macos
+
+ ``~/Public``
+
+ .. tab-item:: Windows
+ :sync: windows
+
+ ``C:\Users\Public`` (``%PUBLIC%``)
+
+ .. tab-item:: Android
+ :sync: android
+
+ ``/storage/emulated/0/Public``
+
+``user_templates_dir``
+======================
+
+See also: :ref:`api:User templates directory`
+
+Defined by `$XDG_TEMPLATES_DIR `_. macOS has no
+platform-defined templates directory; ``~/Templates`` is returned as a pragmatic fallback. On Windows, see
+`FOLDERID_Templates `_.
+
+.. tab-set::
+
+ .. tab-item:: Linux
+ :sync: linux
+
+ ``~/Templates`` (from ``$XDG_TEMPLATES_DIR`` if set, else ``$XDG_TEMPLATES_DIR`` entry in
+ ``user-dirs.dirs``, else ``~/Templates``)
+
+ .. tab-item:: macOS
+ :sync: macos
+
+ ``~/Templates`` (pragmatic fallback; macOS has no native templates directory)
+
+ .. tab-item:: Windows
+ :sync: windows
+
+ ``%APPDATA%\Microsoft\Windows\Templates``
+
+ .. tab-item:: Android
+ :sync: android
+
+ ``/storage/emulated/0/Templates``
+
+``user_fonts_dir``
+==================
+
+See also: :ref:`api:User fonts directory`
+
+Derived from ``$XDG_DATA_HOME/fonts`` on Linux (no dedicated env var). See the `XDG Base Directory Specification
+`_. On Windows, uses the per-user font location added in Windows
+10.
+
+.. tab-set::
+
+ .. tab-item:: Linux
+ :sync: linux
+
+ ``$XDG_DATA_HOME/fonts`` (default ``~/.local/share/fonts``)
+
+ .. tab-item:: macOS
+ :sync: macos
+
+ ``~/Library/Fonts``
+
+ .. tab-item:: Windows
+ :sync: windows
+
+ ``%LOCALAPPDATA%\Microsoft\Windows\Fonts``
+
+ .. tab-item:: Android
+ :sync: android
+
+ ``/storage/emulated/0/fonts``
+
+``user_preference_dir``
+=======================
+
+See also: :ref:`api:User preference directory`
+
+On macOS, ``~/Library/Preferences`` is distinct from ``~/Library/Application Support`` (``user_config_dir``). See
+`Apple's File System Programming Guide
+`_.
+On all other platforms, this aliases ``user_config_dir``.
+
+.. tab-set::
+
+ .. tab-item:: Linux
+ :sync: linux
+
+ Same as ``user_config_dir`` (``$XDG_CONFIG_HOME`` or ``~/.config/AppName``)
+
+ .. tab-item:: macOS
+ :sync: macos
+
+ ``~/Library/Preferences/AppName`` (distinct from ``~/Library/Application Support``)
+
+ .. tab-item:: Windows
+ :sync: windows
+
+ Same as ``user_config_dir`` (``%APPDATA%\AppName``)
+
+ .. tab-item:: Android
+ :sync: android
+
+ Same as ``user_config_dir``
+
********************
Shared directories
********************
diff --git a/src/platformdirs/__init__.py b/src/platformdirs/__init__.py
index d0867d5f..e9d3cb67 100644
--- a/src/platformdirs/__init__.py
+++ b/src/platformdirs/__init__.py
@@ -349,6 +349,26 @@ def user_projects_dir() -> str:
return PlatformDirs().user_projects_dir
+def user_publicshare_dir() -> str:
+ """:returns: public share directory tied to the user"""
+ return PlatformDirs().user_publicshare_dir
+
+
+def user_templates_dir() -> str:
+ """:returns: templates directory tied to the user"""
+ return PlatformDirs().user_templates_dir
+
+
+def user_fonts_dir() -> str:
+ """:returns: fonts directory tied to the user"""
+ return PlatformDirs().user_fonts_dir
+
+
+def user_preference_dir() -> str:
+ """:returns: preference directory tied to the user"""
+ return PlatformDirs().user_preference_dir
+
+
def user_bin_dir() -> str:
""":returns: bin directory tied to the user"""
return PlatformDirs().user_bin_dir
@@ -730,6 +750,26 @@ def user_projects_path() -> Path:
return PlatformDirs().user_projects_path
+def user_publicshare_path() -> Path:
+ """:returns: public share path tied to the user"""
+ return PlatformDirs().user_publicshare_path
+
+
+def user_templates_path() -> Path:
+ """:returns: templates path tied to the user"""
+ return PlatformDirs().user_templates_path
+
+
+def user_fonts_path() -> Path:
+ """:returns: fonts path tied to the user"""
+ return PlatformDirs().user_fonts_path
+
+
+def user_preference_path() -> Path:
+ """:returns: preference path tied to the user"""
+ return PlatformDirs().user_preference_path
+
+
def user_bin_path() -> Path:
""":returns: bin path tied to the user"""
return PlatformDirs().user_bin_path
@@ -852,18 +892,26 @@ def site_runtime_path(
"user_documents_path",
"user_downloads_dir",
"user_downloads_path",
+ "user_fonts_dir",
+ "user_fonts_path",
"user_log_dir",
"user_log_path",
"user_music_dir",
"user_music_path",
"user_pictures_dir",
"user_pictures_path",
+ "user_preference_dir",
+ "user_preference_path",
"user_projects_dir",
"user_projects_path",
+ "user_publicshare_dir",
+ "user_publicshare_path",
"user_runtime_dir",
"user_runtime_path",
"user_state_dir",
"user_state_path",
+ "user_templates_dir",
+ "user_templates_path",
"user_videos_dir",
"user_videos_path",
]
diff --git a/src/platformdirs/__main__.py b/src/platformdirs/__main__.py
index 11d1e8eb..7a52b7d4 100644
--- a/src/platformdirs/__main__.py
+++ b/src/platformdirs/__main__.py
@@ -16,6 +16,10 @@
"user_videos_dir",
"user_music_dir",
"user_projects_dir",
+ "user_publicshare_dir",
+ "user_templates_dir",
+ "user_fonts_dir",
+ "user_preference_dir",
"user_bin_dir",
"site_bin_dir",
"user_applications_dir",
diff --git a/src/platformdirs/_xdg.py b/src/platformdirs/_xdg.py
index fae5750b..b2af284a 100644
--- a/src/platformdirs/_xdg.py
+++ b/src/platformdirs/_xdg.py
@@ -122,9 +122,30 @@ def user_desktop_dir(self) -> str:
def user_projects_dir(self) -> str:
""":returns: projects directory tied to the user, from ``$XDG_PROJECTS_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_PROJECTS_DIR", "").strip():
- return os.path.expanduser(path) # noqa: PTH111
+ return os.path.expanduser(path) # noqa: PTH111 # API returns str, not Path
return super().user_projects_dir
+ @property
+ def user_publicshare_dir(self) -> str:
+ """:returns: public share directory tied to the user, from ``$XDG_PUBLICSHARE_DIR`` if set, else platform default"""
+ if path := os.environ.get("XDG_PUBLICSHARE_DIR", "").strip():
+ return os.path.expanduser(path) # noqa: PTH111 # API returns str, not Path
+ return super().user_publicshare_dir
+
+ @property
+ def user_templates_dir(self) -> str:
+ """:returns: templates directory tied to the user, from ``$XDG_TEMPLATES_DIR`` if set, else platform default"""
+ if path := os.environ.get("XDG_TEMPLATES_DIR", "").strip():
+ return os.path.expanduser(path) # noqa: PTH111 # API returns str, not Path
+ return super().user_templates_dir
+
+ @property
+ def user_fonts_dir(self) -> str:
+ """:returns: fonts directory tied to the user, from ``$XDG_DATA_HOME/fonts`` if set, else platform default"""
+ if path := os.environ.get("XDG_DATA_HOME", "").strip():
+ return f"{os.path.expanduser(path)}/fonts" # noqa: PTH111 # API returns str, not Path
+ return super().user_fonts_dir
+
@property
def user_applications_dir(self) -> str:
""":returns: applications directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default"""
diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py
index 21c56b9c..885e8c6e 100644
--- a/src/platformdirs/android.py
+++ b/src/platformdirs/android.py
@@ -112,6 +112,26 @@ def user_projects_dir(self) -> str:
""":returns: projects directory tied to the user e.g. ``/storage/emulated/0/Projects``"""
return "/storage/emulated/0/Projects"
+ @property
+ def user_publicshare_dir(self) -> str:
+ """:returns: public share directory tied to the user e.g. ``/storage/emulated/0/Public``"""
+ return "/storage/emulated/0/Public"
+
+ @property
+ def user_templates_dir(self) -> str:
+ """:returns: templates directory tied to the user e.g. ``/storage/emulated/0/Templates``"""
+ return "/storage/emulated/0/Templates"
+
+ @property
+ def user_fonts_dir(self) -> str:
+ """:returns: fonts directory tied to the user e.g. ``/storage/emulated/0/fonts``"""
+ return "/storage/emulated/0/fonts"
+
+ @property
+ def user_preference_dir(self) -> str:
+ """:returns: preference directory tied to the user, same as ``user_config_dir``"""
+ return self.user_config_dir
+
@property
def user_bin_dir(self) -> str:
""":returns: bin directory tied to the user, e.g. ``/data/user///files/bin``"""
diff --git a/src/platformdirs/api.py b/src/platformdirs/api.py
index 65a06b9f..91ef9028 100644
--- a/src/platformdirs/api.py
+++ b/src/platformdirs/api.py
@@ -213,6 +213,26 @@ def user_desktop_dir(self) -> str:
def user_projects_dir(self) -> str:
""":returns: projects directory tied to the user"""
+ @property
+ @abstractmethod
+ def user_publicshare_dir(self) -> str:
+ """:returns: public share directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def user_templates_dir(self) -> str:
+ """:returns: templates directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def user_fonts_dir(self) -> str:
+ """:returns: fonts directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def user_preference_dir(self) -> str:
+ """:returns: preference directory tied to the user"""
+
@property
@abstractmethod
def user_bin_dir(self) -> str:
@@ -332,6 +352,26 @@ def user_projects_path(self) -> Path:
""":returns: projects path tied to the user"""
return Path(self.user_projects_dir)
+ @property
+ def user_publicshare_path(self) -> Path:
+ """:returns: public share path tied to the user"""
+ return Path(self.user_publicshare_dir)
+
+ @property
+ def user_templates_path(self) -> Path:
+ """:returns: templates path tied to the user"""
+ return Path(self.user_templates_dir)
+
+ @property
+ def user_fonts_path(self) -> Path:
+ """:returns: fonts path tied to the user"""
+ return Path(self.user_fonts_dir)
+
+ @property
+ def user_preference_path(self) -> Path:
+ """:returns: preference path tied to the user"""
+ return Path(self.user_preference_dir)
+
@property
def user_bin_path(self) -> Path:
""":returns: bin path tied to the user"""
diff --git a/src/platformdirs/macos.py b/src/platformdirs/macos.py
index 59c62aa9..9ebc2a0c 100644
--- a/src/platformdirs/macos.py
+++ b/src/platformdirs/macos.py
@@ -139,6 +139,26 @@ def user_projects_dir(self) -> str:
""":returns: projects directory tied to the user, e.g. ``~/Projects``"""
return os.path.expanduser("~/Projects") # noqa: PTH111
+ @property
+ def user_publicshare_dir(self) -> str:
+ """:returns: public share directory tied to the user, e.g. ``~/Public``"""
+ return os.path.expanduser("~/Public") # noqa: PTH111 # API returns str, not Path
+
+ @property
+ def user_templates_dir(self) -> str:
+ """:returns: templates directory tied to the user, e.g. ``~/Templates``"""
+ return os.path.expanduser("~/Templates") # noqa: PTH111 # API returns str, not Path
+
+ @property
+ def user_fonts_dir(self) -> str:
+ """:returns: fonts directory tied to the user, e.g. ``~/Library/Fonts``"""
+ return os.path.expanduser("~/Library/Fonts") # noqa: PTH111 # API returns str, not Path
+
+ @property
+ def user_preference_dir(self) -> str:
+ """:returns: preference directory tied to the user, e.g. ``~/Library/Preferences/AppName``"""
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences")) # noqa: PTH111 # API returns str, not Path
+
@property
def user_bin_dir(self) -> str:
""":returns: bin directory tied to the user, e.g. ``~/.local/bin``"""
diff --git a/src/platformdirs/unix.py b/src/platformdirs/unix.py
index f9bc91b2..500d3a4b 100644
--- a/src/platformdirs/unix.py
+++ b/src/platformdirs/unix.py
@@ -128,6 +128,26 @@ def user_projects_dir(self) -> str:
""":returns: projects directory tied to the user, e.g. ``~/Projects``"""
return _get_user_media_dir("XDG_PROJECTS_DIR", "~/Projects")
+ @property
+ def user_publicshare_dir(self) -> str:
+ """:returns: public share directory tied to the user, e.g. ``~/Public``"""
+ return _get_user_media_dir("XDG_PUBLICSHARE_DIR", "~/Public")
+
+ @property
+ def user_templates_dir(self) -> str:
+ """:returns: templates directory tied to the user, e.g. ``~/Templates``"""
+ return _get_user_media_dir("XDG_TEMPLATES_DIR", "~/Templates")
+
+ @property
+ def user_fonts_dir(self) -> str:
+ """:returns: fonts directory tied to the user, e.g. ``~/.local/share/fonts``"""
+ return f"{os.path.expanduser('~/.local/share')}/fonts" # noqa: PTH111 # API returns str, not Path
+
+ @property
+ def user_preference_dir(self) -> str:
+ """:returns: preference directory tied to the user, same as ``user_config_dir``"""
+ return self.user_config_dir
+
@property
def user_bin_dir(self) -> str:
""":returns: bin directory tied to the user, e.g. ``~/.local/bin``"""
diff --git a/src/platformdirs/windows.py b/src/platformdirs/windows.py
index 3d678ddb..476ffe73 100644
--- a/src/platformdirs/windows.py
+++ b/src/platformdirs/windows.py
@@ -4,6 +4,7 @@
import os
import sys
+from pathlib import Path
from typing import TYPE_CHECKING, Final
from .api import PlatformDirsABC
@@ -138,6 +139,26 @@ def user_projects_dir(self) -> str:
r""":returns: projects directory tied to the user, e.g. ``%USERPROFILE%\Projects``"""
return os.path.normpath(os.path.expanduser("~/Projects")) # noqa: PTH111
+ @property
+ def user_publicshare_dir(self) -> str:
+ r""":returns: public share directory e.g. ``C:\Users\Public``"""
+ return os.path.normpath(os.environ.get("PUBLIC", str(Path("~").expanduser().parent / "Public")))
+
+ @property
+ def user_templates_dir(self) -> str:
+ r""":returns: templates directory tied to the user e.g. ``%APPDATA%\Microsoft\Windows\Templates``"""
+ return os.path.normpath(str(Path(get_win_folder("CSIDL_APPDATA")) / "Microsoft" / "Windows" / "Templates"))
+
+ @property
+ def user_fonts_dir(self) -> str:
+ r""":returns: fonts directory tied to the user e.g. ``%LOCALAPPDATA%\Microsoft\Windows\Fonts``"""
+ return os.path.normpath(str(Path(get_win_folder("CSIDL_LOCAL_APPDATA")) / "Microsoft" / "Windows" / "Fonts"))
+
+ @property
+ def user_preference_dir(self) -> str:
+ r""":returns: preference directory tied to the user, same as ``user_config_dir``"""
+ return self.user_config_dir
+
@property
def user_bin_dir(self) -> str:
r""":returns: bin directory tied to the user, e.g. ``%LOCALAPPDATA%\Programs``"""
diff --git a/tests/conftest.py b/tests/conftest.py
index dc0833ad..a41bc89c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -19,6 +19,10 @@
"user_videos_dir",
"user_music_dir",
"user_projects_dir",
+ "user_publicshare_dir",
+ "user_templates_dir",
+ "user_fonts_dir",
+ "user_preference_dir",
"user_bin_dir",
"site_bin_dir",
"user_applications_dir",
diff --git a/tests/test_android.py b/tests/test_android.py
index 66b4f7f2..e3d42c73 100644
--- a/tests/test_android.py
+++ b/tests/test_android.py
@@ -64,6 +64,10 @@ def test_android(mocker: MockerFixture, params: dict[str, Any], func: str) -> No
"user_music_dir": "/storage/emulated/0/Music",
"user_desktop_dir": "/storage/emulated/0/Desktop",
"user_projects_dir": "/storage/emulated/0/Projects",
+ "user_publicshare_dir": "/storage/emulated/0/Public",
+ "user_templates_dir": "/storage/emulated/0/Templates",
+ "user_fonts_dir": "/storage/emulated/0/fonts",
+ "user_preference_dir": f"/data/data/com.example/shared_prefs{suffix}",
"user_bin_dir": "/data/data/com.example/files/bin",
"site_bin_dir": "/data/data/com.example/files/bin",
"user_applications_dir": f"/data/data/com.example/files{suffix}",
diff --git a/tests/test_macos.py b/tests/test_macos.py
index 3fd06b05..7519425c 100644
--- a/tests/test_macos.py
+++ b/tests/test_macos.py
@@ -84,6 +84,10 @@ def test_macos(mocker: MockerFixture, params: dict[str, Any], func: str) -> None
"user_music_dir": f"{home}/Music",
"user_desktop_dir": f"{home}/Desktop",
"user_projects_dir": f"{home}/Projects",
+ "user_publicshare_dir": f"{home}/Public",
+ "user_templates_dir": f"{home}/Templates",
+ "user_fonts_dir": f"{home}/Library/Fonts",
+ "user_preference_dir": f"{home}/Library/Preferences{suffix}",
"user_bin_dir": f"{home}/.local/bin",
"site_bin_dir": "/usr/local/bin",
"user_applications_dir": f"{home}/Applications",
@@ -274,6 +278,10 @@ def test_macos_xdg_empty_falls_back(
"user_music_dir": f"{home}/Music",
"user_desktop_dir": f"{home}/Desktop",
"user_projects_dir": f"{home}/Projects",
+ "user_publicshare_dir": f"{home}/Public",
+ "user_templates_dir": f"{home}/Templates",
+ "user_fonts_dir": f"{home}/Library/Fonts",
+ "user_preference_dir": f"{home}/Library/Preferences",
"user_bin_dir": f"{home}/.local/bin",
"site_bin_dir": "/usr/local/bin",
"user_applications_dir": f"{home}/Applications",
diff --git a/tests/test_unix.py b/tests/test_unix.py
index a85e9079..c94db299 100644
--- a/tests/test_unix.py
+++ b/tests/test_unix.py
@@ -34,6 +34,8 @@ def _reload_after_test() -> typing.Iterator[None]:
"user_music_dir",
"user_desktop_dir",
"user_projects_dir",
+ "user_publicshare_dir",
+ "user_templates_dir",
],
)
def test_user_media_dir(mocker: MockerFixture, prop: str) -> None:
@@ -53,6 +55,8 @@ def test_user_media_dir(mocker: MockerFixture, prop: str) -> None:
pytest.param("XDG_MUSIC_DIR", "user_music_dir", id="user_music_dir"),
pytest.param("XDG_DESKTOP_DIR", "user_desktop_dir", id="user_desktop_dir"),
pytest.param("XDG_PROJECTS_DIR", "user_projects_dir", id="user_projects_dir"),
+ pytest.param("XDG_PUBLICSHARE_DIR", "user_publicshare_dir", id="user_publicshare_dir"),
+ pytest.param("XDG_TEMPLATES_DIR", "user_templates_dir", id="user_templates_dir"),
],
)
def test_user_media_dir_env_var(mocker: MockerFixture, env_var: str, prop: str) -> None:
@@ -76,6 +80,8 @@ def test_user_media_dir_env_var(mocker: MockerFixture, env_var: str, prop: str)
pytest.param("XDG_MUSIC_DIR", "user_music_dir", "/home/example/Music", id="user_music_dir"),
pytest.param("XDG_DESKTOP_DIR", "user_desktop_dir", "/home/example/Desktop", id="user_desktop_dir"),
pytest.param("XDG_PROJECTS_DIR", "user_projects_dir", "/home/example/Projects", id="user_projects_dir"),
+ pytest.param("XDG_PUBLICSHARE_DIR", "user_publicshare_dir", "/home/example/Public", id="user_publicshare_dir"),
+ pytest.param("XDG_TEMPLATES_DIR", "user_templates_dir", "/home/example/Templates", id="user_templates_dir"),
],
)
def test_user_media_dir_default(mocker: MockerFixture, env_var: str, prop: str, default_abs_path: str) -> None:
@@ -94,6 +100,21 @@ def test_user_media_dir_default(mocker: MockerFixture, env_var: str, prop: str,
assert getattr(Unix(), prop) == default_abs_path
+def test_user_fonts_dir_default(mocker: MockerFixture) -> None:
+ mocker.patch.dict(os.environ, {"XDG_DATA_HOME": "", "HOME": "/home/example", "USERPROFILE": "/home/example"})
+ assert Unix().user_fonts_dir == "/home/example/.local/share/fonts"
+
+
+def test_user_fonts_dir_xdg_data_home(mocker: MockerFixture) -> None:
+ mocker.patch.dict(os.environ, {"XDG_DATA_HOME": "/custom/data"})
+ assert Unix().user_fonts_dir == "/custom/data/fonts"
+
+
+def test_user_preference_dir_is_config_dir() -> None:
+ dirs = Unix(appname="MyApp", version="1.0")
+ assert dirs.user_preference_dir == dirs.user_config_dir
+
+
class XDGVariable(typing.NamedTuple):
name: str
default_value: str
diff --git a/tests/test_windows.py b/tests/test_windows.py
index 88767a3f..c7f99f31 100644
--- a/tests/test_windows.py
+++ b/tests/test_windows.py
@@ -5,6 +5,7 @@
import os
import pathlib
import sys
+from pathlib import Path
from typing import TYPE_CHECKING, Any
from unittest.mock import MagicMock
@@ -92,6 +93,14 @@ def test_windows(params: dict[str, Any], func: str) -> None:
"user_music_dir": os.path.normpath(_WIN_FOLDERS["CSIDL_MYMUSIC"]),
"user_desktop_dir": os.path.normpath(_WIN_FOLDERS["CSIDL_DESKTOPDIRECTORY"]),
"user_projects_dir": os.path.normpath(pathlib.Path("~/Projects").expanduser()),
+ "user_publicshare_dir": os.path.normpath(
+ os.environ.get("PUBLIC", str(Path("~").expanduser().parent / "Public"))
+ ),
+ "user_templates_dir": os.path.normpath(
+ str(Path(_WIN_FOLDERS["CSIDL_APPDATA"]) / "Microsoft" / "Windows" / "Templates")
+ ),
+ "user_fonts_dir": os.path.normpath(str(Path(_LOCAL) / "Microsoft" / "Windows" / "Fonts")),
+ "user_preference_dir": local,
"user_bin_dir": os.path.join(_LOCAL, "Programs"), # noqa: PTH118
"site_bin_dir": os.path.join(_COMMON, "bin"), # noqa: PTH118
"user_applications_dir": os.path.normpath(_WIN_FOLDERS["CSIDL_PROGRAMS"]),