Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ On macOS:
>>> user_runtime_dir(appname, appauthor)
'/Users/trentm/Library/Caches/TemporaryItems/SuperApp'

Note: On macOS, XDG environment variables (e.g. ``XDG_DATA_HOME``,
``XDG_CONFIG_HOME``, ``XDG_CACHE_HOME``) are also supported and take
precedence over the default macOS directories when set:

.. code-block:: pycon

>>> import os
>>> os.environ["XDG_CONFIG_HOME"] = "/Users/trentm/.config"
>>> user_config_dir(appname, appauthor)
'/Users/trentm/.config/SuperApp'

On Windows:

.. code-block:: pycon
Expand Down
124 changes: 124 additions & 0 deletions src/platformdirs/_xdg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""XDG environment variable mixin for Unix and macOS."""

from __future__ import annotations

import os

from .api import PlatformDirsABC


class XDGMixin(PlatformDirsABC):
"""Mixin that checks XDG environment variables, falling back to platform-specific defaults via ``super()``."""

@property
def user_data_dir(self) -> str:
""":return: data directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default"""
if path := os.environ.get("XDG_DATA_HOME", "").strip():
return self._append_app_name_and_version(path)
return super().user_data_dir

@property
def _site_data_dirs(self) -> list[str]:
if xdg_dirs := os.environ.get("XDG_DATA_DIRS", "").strip():
return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()]
return super()._site_data_dirs # type: ignore[misc]

@property
def site_data_dir(self) -> str:
""":return: data directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default"""
dirs = self._site_data_dirs
return os.pathsep.join(dirs) if self.multipath else dirs[0]

@property
def user_config_dir(self) -> str:
""":return: config directory tied to the user, from ``$XDG_CONFIG_HOME`` if set, else platform default"""
if path := os.environ.get("XDG_CONFIG_HOME", "").strip():
return self._append_app_name_and_version(path)
return super().user_config_dir

@property
def _site_config_dirs(self) -> list[str]:
if xdg_dirs := os.environ.get("XDG_CONFIG_DIRS", "").strip():
return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()]
return super()._site_config_dirs # type: ignore[misc]

@property
def site_config_dir(self) -> str:
""":return: config directories shared by users, from ``$XDG_CONFIG_DIRS`` if set, else platform default"""
dirs = self._site_config_dirs
return os.pathsep.join(dirs) if self.multipath else dirs[0]

@property
def user_cache_dir(self) -> str:
""":return: cache directory tied to the user, from ``$XDG_CACHE_HOME`` if set, else platform default"""
if path := os.environ.get("XDG_CACHE_HOME", "").strip():
return self._append_app_name_and_version(path)
return super().user_cache_dir

@property
def user_state_dir(self) -> str:
""":return: state directory tied to the user, from ``$XDG_STATE_HOME`` if set, else platform default"""
if path := os.environ.get("XDG_STATE_HOME", "").strip():
return self._append_app_name_and_version(path)
return super().user_state_dir

@property
def user_runtime_dir(self) -> str:
""":return: runtime directory tied to the user, from ``$XDG_RUNTIME_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_RUNTIME_DIR", "").strip():
return self._append_app_name_and_version(path)
return super().user_runtime_dir

@property
def site_runtime_dir(self) -> str:
""":return: runtime directory shared by users, from ``$XDG_RUNTIME_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_RUNTIME_DIR", "").strip():
return self._append_app_name_and_version(path)
return super().site_runtime_dir

@property
def user_documents_dir(self) -> str:
""":return: documents directory tied to the user, from ``$XDG_DOCUMENTS_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_DOCUMENTS_DIR", "").strip():
return os.path.expanduser(path) # noqa: PTH111
return super().user_documents_dir

@property
def user_downloads_dir(self) -> str:
""":return: downloads directory tied to the user, from ``$XDG_DOWNLOAD_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_DOWNLOAD_DIR", "").strip():
return os.path.expanduser(path) # noqa: PTH111
return super().user_downloads_dir

@property
def user_pictures_dir(self) -> str:
""":return: pictures directory tied to the user, from ``$XDG_PICTURES_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_PICTURES_DIR", "").strip():
return os.path.expanduser(path) # noqa: PTH111
return super().user_pictures_dir

@property
def user_videos_dir(self) -> str:
""":return: videos directory tied to the user, from ``$XDG_VIDEOS_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_VIDEOS_DIR", "").strip():
return os.path.expanduser(path) # noqa: PTH111
return super().user_videos_dir

@property
def user_music_dir(self) -> str:
""":return: music directory tied to the user, from ``$XDG_MUSIC_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_MUSIC_DIR", "").strip():
return os.path.expanduser(path) # noqa: PTH111
return super().user_music_dir

@property
def user_desktop_dir(self) -> str:
""":return: desktop directory tied to the user, from ``$XDG_DESKTOP_DIR`` if set, else platform default"""
if path := os.environ.get("XDG_DESKTOP_DIR", "").strip():
return os.path.expanduser(path) # noqa: PTH111
return super().user_desktop_dir


__all__ = [
"XDGMixin",
]
44 changes: 23 additions & 21 deletions src/platformdirs/macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,20 @@
import sys
from typing import TYPE_CHECKING

from ._xdg import XDGMixin
from .api import PlatformDirsABC

if TYPE_CHECKING:
from pathlib import Path


class MacOS(PlatformDirsABC):
class _MacOSDefaults(PlatformDirsABC):
"""
Platform directories for the macOS operating system.
Default platform directories for macOS.

Follows the guidance from
`Apple documentation <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_.
Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`,
`version <platformdirs.api.PlatformDirsABC.version>`,
`ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.

The XDG env var handling is in :class:`~platformdirs.xdg.XDGMixin`.
"""

@property
Expand All @@ -30,22 +28,12 @@ def user_data_dir(self) -> str:
return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support")) # noqa: PTH111

@property
def site_data_dir(self) -> str:
"""
:return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``.
If we're using a Python binary managed by `Homebrew <https://brew.sh>`_, the directory
will be under the Homebrew prefix, e.g. ``$homebrew_prefix/share/$appname/$version``.
If `multipath <platformdirs.api.PlatformDirsABC.multipath>` is enabled, and we're in Homebrew,
the response is a multi-path string separated by ":", e.g.
``$homebrew_prefix/share/$appname/$version:/Library/Application Support/$appname/$version``
"""
def _site_data_dirs(self) -> list[str]:
is_homebrew = "/opt/python" in sys.prefix
homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else ""
path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/share")] if is_homebrew else []
path_list.append(self._append_app_name_and_version("/Library/Application Support"))
if self.multipath:
return os.pathsep.join(path_list)
return path_list[0]
return path_list

@property
def site_data_path(self) -> Path:
Expand All @@ -58,9 +46,8 @@ def user_config_dir(self) -> str:
return self.user_data_dir

@property
def site_config_dir(self) -> str:
""":return: config directory shared by users, same as `site_data_dir`"""
return self.site_data_dir
def _site_config_dirs(self) -> list[str]:
return self._site_data_dirs

@property
def user_cache_dir(self) -> str:
Expand Down Expand Up @@ -141,6 +128,21 @@ def site_runtime_dir(self) -> str:
return self.user_runtime_dir


class MacOS(XDGMixin, _MacOSDefaults):
"""
Platform directories for the macOS operating system.

Follows the guidance from
`Apple documentation <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_.
Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`,
`version <platformdirs.api.PlatformDirsABC.version>`,
`ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.

XDG environment variables (e.g. ``$XDG_DATA_HOME``) are supported and take precedence over macOS defaults.

"""


__all__ = [
"MacOS",
]
Loading