############################
Understanding platformdirs
############################
*************************
Why platformdirs exists
*************************
Every operating system has its own conventions for where applications should store data, configuration, cache, and logs.
A macOS app puts preferences under ``~/Library``, a Linux app follows the XDG Base Directory Specification, and a
Windows app uses ``AppData``. Hard-coding any of these paths makes an application non-portable.
``platformdirs`` solves this by detecting the current platform at runtime and returning the correct directory for each
purpose. Application authors write platform-agnostic code while end users get paths that follow their OS conventions.
******************************
Choosing the right directory
******************************
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{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
================
Use ``user_data_dir`` and ``site_data_dir`` for persistent application data that the user expects to keep:
- SQLite databases, document stores.
- Downloaded files, media assets.
- User-created content.
- Application state that must survive app updates.
.. code-block:: python
from platformdirs import user_data_path
db_path = user_data_path("MyApp") / "app.db"
downloads_dir = user_data_path("MyApp") / "downloads"
Config directories
==================
Use ``user_config_dir`` and ``site_config_dir`` for configuration files and user preferences:
- Settings files (JSON, TOML, INI, YAML).
- User preferences and options.
- Application themes, keybindings.
- Feature flags and toggles.
.. code-block:: python
from platformdirs import user_config_path
import json
config_file = user_config_path("MyApp") / "settings.json"
config_file.parent.mkdir(parents=True, exist_ok=True)
settings = {"theme": "dark", "auto_save": True}
config_file.write_text(json.dumps(settings))
Cache directories
=================
Use ``user_cache_dir`` and ``site_cache_dir`` for regenerable data that improves performance:
- API response caches.
- Thumbnail images, processed media.
- Compiled templates, bytecode.
- Downloaded package indexes.
Cached data can be safely deleted without losing functionality. Applications should gracefully handle missing cache
directories.
.. code-block:: python
from platformdirs import user_cache_path
cache_dir = user_cache_path("MyApp")
thumbnail_cache = cache_dir / "thumbnails"
api_cache = cache_dir / "api_responses"
State directories
=================
Use ``user_state_dir`` and ``site_state_dir`` for non-critical runtime state:
- Window positions, sizes.
- Recently opened files, MRU lists.
- Undo/redo history.
- Search history, autocomplete data.
State persists between sessions but is less important than data or config. Loss of state is inconvenient but not
catastrophic.
.. code-block:: python
from platformdirs import user_state_path
import json
state_file = user_state_path("MyApp") / "window_state.json"
state = {"width": 1024, "height": 768, "maximized": False}
state_file.parent.mkdir(parents=True, exist_ok=True)
state_file.write_text(json.dumps(state))
Log directories
===============
Use ``user_log_dir`` and ``site_log_dir`` for application logs:
- Debug logs, error logs.
- Audit trails, access logs.
- Performance metrics.
- Crash reports.
.. code-block:: python
import logging
from platformdirs import user_log_path
log_file = user_log_path("MyApp") / "app.log"
log_file.parent.mkdir(parents=True, exist_ok=True)
logging.basicConfig(
filename=log_file,
level=logging.INFO,
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
**************************
Each directory type has both ``user_*`` and ``site_*`` variants serving different purposes.
.. tab-set::
.. tab-item:: User directories
User directories (``user_data_dir``, ``user_config_dir``, etc.) are:
- **Per-user**: Each user on the system has their own separate directory.
- **Writable**: Normal users can read and write without special permissions.
- **Isolated**: Changes by one user don't affect others.
- **Default choice**: Use these unless you specifically need system-wide access.
.. code-block:: python
from platformdirs import user_config_path
# Each user gets their own config
config = user_config_path("MyApp") / "config.json"
.. tab-item:: Site directories
Site directories (``site_data_dir``, ``site_config_dir``, etc.) are:
- **System-wide**: Shared across all users on the machine.
- **Read-only for users**: Typically require administrator/root privileges to write.
- **System defaults**: Store default configurations, shared resources.
- **Package managers**: Used by system package managers for application data.
.. code-block:: python
from platformdirs import site_config_path, user_config_path
# Check site config first (system defaults), then user config (overrides)
site_cfg = site_config_path("MyApp") / "defaults.json"
user_cfg = user_config_path("MyApp") / "config.json"
if user_cfg.exists():
config = user_cfg
elif site_cfg.exists():
config = site_cfg
else:
config = None
**********************
Platform conventions
**********************
Each operating system has its own conventions for where application data belongs. Understanding these conventions helps
explain why ``platformdirs`` returns different paths on different platforms.
macOS
=====
On macOS, ``platformdirs`` uses the standard Apple ``~/Library`` directories by default. See `Apple's File System
Programming Guide
`_
for background.
On macOS, ``user_data_dir`` and ``user_config_dir`` both resolve to ``~/Library/Application Support/AppName``. If you
need to separate data from config, use subdirectories within that path.
XDG environment variables (``XDG_DATA_HOME``, ``XDG_CONFIG_HOME``, ``XDG_CACHE_HOME``, etc.) are also supported and take
precedence over the macOS defaults when set. This allows users who prefer the XDG layout to override the default
behavior.
When `Homebrew `_ is installed, ``site_data_dir`` and ``site_cache_dir`` include the Homebrew prefix as
an additional path when ``multipath=True``.
See :class:`platformdirs.macos.MacOS` for the full API reference.
Windows
=======
On Windows, ``platformdirs`` uses the Shell Folder APIs to resolve directories. See `Microsoft's Known Folder
documentation `_ for background.
Key behaviors:
- ``appauthor`` adds a parent directory: ``AppData\Local\\``. When ``appauthor`` is ``None`` (the default),
it falls back to ``appname``, producing a doubled path like ``AppData\Local\MyApp\MyApp``. This follows the Windows
convention of grouping applications by publisher. If your application does not need an author directory, pass
``appauthor=False`` to get ``AppData\Local\MyApp`` instead.
- ``roaming=True`` switches from ``AppData\Local`` to ``AppData\Roaming``, which syncs across machines in a Windows
domain. Use roaming for user preferences that should follow the user; use local (default) for machine-specific data
like caches.
- **OPINION**: ``user_cache_dir`` appends ``\Cache``, ``user_log_dir`` appends ``\Logs``
Unlike Linux/macOS where ``XDG_*`` variables are a platform standard, Windows has no built-in convention for overriding
folder locations at the application level. To fill this gap, ``platformdirs`` checks ``WIN_PD_OVERRIDE_*`` environment
variables before querying the Shell Folder APIs. This is useful when large data (ML models, package caches) should live
on a different drive without changing the system-wide ``APPDATA`` / ``LOCALAPPDATA`` variables that other applications
rely on.
The override variable name is ``WIN_PD_OVERRIDE_`` followed by the CSIDL suffix:
.. list-table::
:widths: 40 60
:header-rows: 1
- - Environment variable
- Overrides
- - ``WIN_PD_OVERRIDE_APPDATA``
- Roaming user data (``AppData\Roaming``)
- - ``WIN_PD_OVERRIDE_LOCAL_APPDATA``
- Local user data, config, cache, state (``AppData\Local``)
- - ``WIN_PD_OVERRIDE_COMMON_APPDATA``
- Site-wide data, config, cache, state (``ProgramData``)
- - ``WIN_PD_OVERRIDE_PERSONAL``
- Documents
- - ``WIN_PD_OVERRIDE_DOWNLOADS``
- Downloads
- - ``WIN_PD_OVERRIDE_MYPICTURES``
- Pictures
- - ``WIN_PD_OVERRIDE_MYVIDEO``
- Videos
- - ``WIN_PD_OVERRIDE_MYMUSIC``
- Music
- - ``WIN_PD_OVERRIDE_DESKTOPDIRECTORY``
- Desktop
- - ``WIN_PD_OVERRIDE_PROGRAMS``
- Applications (Start Menu Programs)
Empty or whitespace-only values are ignored and the normal resolution applies.
.. note::
**Windows Store Python (MSIX)**
Python installed from the Microsoft Store runs in a sandboxed (AppContainer) environment. Windows silently redirects
writes under ``AppData`` to a per-package private location, e.g.
``AppData\Local\Packages\PythonSoftwareFoundation.Python.3.X_\LocalCache\Local\...``.
``platformdirs`` returns the logical ``AppData`` path, which is correct for code running inside the same sandbox.
However, if you pass these paths to external processes (subprocesses, other applications), those processes may not
see files created at the logical path because they run outside the sandbox.
To obtain the real on-disk path for sharing with external processes, call :func:`os.path.realpath` on the path
**after** the file or directory has been created:
.. code-block:: python
import os
import platformdirs
data_dir = platformdirs.user_data_dir(
appname="MyApp", appauthor="Acme", ensure_exists=True
)
real_dir = os.path.realpath(data_dir)
This is a Windows design limitation, not a ``platformdirs`` bug. See `Microsoft's MSIX documentation
`_ for details on
filesystem virtualization.
See :class:`platformdirs.windows.Windows` for the full API reference.
Linux / Unix
============
On Linux and other Unix-like systems, ``platformdirs`` follows the `XDG Base Directory Specification
`_.
XDG environment variables override the defaults:
- ``XDG_DATA_HOME`` (default ``~/.local/share``)
- ``XDG_CONFIG_HOME`` (default ``~/.config``)
- ``XDG_CACHE_HOME`` (default ``~/.cache``)
- ``XDG_STATE_HOME`` (default ``~/.local/state``)
- ``XDG_RUNTIME_DIR`` (default ``/run/user/``)
- ``XDG_DATA_DIRS`` (default ``/usr/local/share:/usr/share``)
- ``XDG_CONFIG_DIRS`` (default ``/etc/xdg``)
When ``multipath=True``, ``site_data_dir`` and ``site_config_dir`` return all paths from the corresponding
``XDG_*_DIRS`` variable, joined by ``:``.
Writing to ``site_*`` directories typically requires root privileges. Normal users can only read from these locations.
**FreeBSD / OpenBSD / NetBSD**: ``user_runtime_dir`` falls back to ``/var/run/user/`` or ``/tmp/runtime-``
when ``/run/user/`` does not exist.
.. note::
**Running as root**
When ``use_site_for_root=True`` is passed and the process is running as root (uid 0), ``user_*_dir`` calls are
redirected to their ``site_*_dir`` equivalents. This is useful for system daemons and installers that should write
to system-wide directories rather than ``/root/.local/...``. XDG user environment variables (e.g. ``XDG_DATA_HOME``)
are bypassed when the redirect is active, since they are typically inherited from the calling user via ``sudo`` and
would defeat the purpose. The parameter is accepted on all platforms but only has an effect on Unix.
See :class:`platformdirs.unix.Unix` for the full API reference.
Android
=======
On Android, ``platformdirs`` uses the app's private storage directories. The app's package folder (e.g.
``/data/data/com.example.app``) is detected via ``python-for-android`` or ``pyjnius``. All directories are within your
app's private storage and data is automatically removed when the app is uninstalled.
Media directories (documents, downloads, pictures, videos, music) point to shared external storage under
``/storage/emulated/0/``. App-private directories don't require storage permissions; external storage access requires
appropriate Android permissions.
**Shell environments**: Android apps like `Termux `_ and Pydroid that function as Linux shells are
detected by the presence of the ``SHELL`` environment variable. In these environments, ``platformdirs`` uses the
Unix/XDG backend instead, including support for ``XDG_*`` environment variables.
See :class:`platformdirs.android.Android` for the full API reference.
*********************
Real-world examples
*********************
These examples show how popular Python projects use ``platformdirs`` in production. Each example is based on actual code
with links to the source.
Black (code formatter)
======================
`Black `_ uses ``user_cache_path`` to cache formatted files, speeding up repeat runs:
.. code-block:: python
import os
from pathlib import Path
from platformdirs import user_cache_path
def get_cache_dir() -> Path:
# Allow customization via environment variable
default_cache_dir = user_cache_path("black")
cache_dir = Path(os.environ.get("BLACK_CACHE_DIR", default_cache_dir))
return cache_dir / __version__
CACHE_DIR = get_cache_dir()
See `black/cache.py `_ for the full implementation.
virtualenv (environment manager)
================================
`virtualenv `_ uses ``user_data_dir`` to store application data with fallback to
temp directory if not writable:
.. code-block:: python
import os
from platformdirs import user_data_dir
def get_app_data_dir(env):
key = "VIRTUALENV_OVERRIDE_APP_DATA"
if key in env:
return env[key]
return user_data_dir(appname="virtualenv", appauthor="pypa")
folder = get_app_data_dir(os.environ)
folder = os.path.abspath(folder)
# Create directory and check writability
os.makedirs(folder, exist_ok=True)
if not os.access(folder, os.W_OK):
# Fallback to temp directory
folder = tempfile.gettempdir()
See `virtualenv/app_data/__init__.py
`_ for the full implementation.
Poetry (dependency manager)
===========================
`Poetry `_ uses all three directory types with environment variable overrides:
.. code-block:: python
import os
from pathlib import Path
from platformdirs import user_cache_path, user_config_path, user_data_path
APP_NAME = "pypoetry"
# Cache directory for downloads and build artifacts
DEFAULT_CACHE_DIR = user_cache_path(APP_NAME, appauthor=False)
# Config directory with environment override and roaming enabled
CONFIG_DIR = Path(
os.getenv("POETRY_CONFIG_DIR")
or user_config_path(APP_NAME, appauthor=False, roaming=True)
)
# Data directory with environment override
def data_dir() -> Path:
if poetry_home := os.getenv("POETRY_HOME"):
return Path(poetry_home).expanduser()
return user_data_path(APP_NAME, appauthor=False, roaming=True)
See `poetry/locations.py `_ for the full
implementation.
tox (testing tool)
==================
`tox `_ uses ``user_config_dir`` for user-level configuration:
.. code-block:: python
from pathlib import Path
from platformdirs import user_config_dir
DEFAULT_CONFIG_FILE = Path(user_config_dir("tox")) / "config.ini"
# Load config with environment variable override
config_file = os.getenv("TOX_USER_CONFIG_FILE", DEFAULT_CONFIG_FILE)
See `tox/config/cli/ini.py `_ for the full
implementation.