Skip to content

Commit

Permalink
Add support for Homebrew-installed software (#232)
Browse files Browse the repository at this point in the history
* Add support for Homebrew-installed software

* mock os.pathsep when running macOS tests on non-macOS systems

* respond to review comments

* respond to review comments
  • Loading branch information
singingwolfboy committed Oct 2, 2023
1 parent d63b824 commit 682cdcb
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/platformdirs/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ def __init__( # noqa: PLR0913
"""
self.multipath = multipath
"""
An optional parameter only applicable to Unix/Linux which indicates that the entire list of data dirs should be
returned. By default, the first item would only be returned.
An optional parameter which indicates that the entire list of data dirs should be returned.
By default, the first item would only be returned.
"""
self.opinion = opinion #: A flag to indicating to use opinionated values.
self.ensure_exists = ensure_exists
Expand Down
33 changes: 29 additions & 4 deletions src/platformdirs/macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations

import os.path
import sys

from .api import PlatformDirsABC

Expand All @@ -22,8 +23,20 @@ def user_data_dir(self) -> str:

@property
def site_data_dir(self) -> str:
""":return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``"""
return self._append_app_name_and_version("/Library/Application Support")
"""
: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. ``/opt/homebrew/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.
``/opt/homebrew/share/$appname/$version:/Library/Application Support/$appname/$version``
"""
is_homebrew = sys.prefix.startswith("/opt/homebrew")
path_list = [self._append_app_name_and_version("/opt/homebrew/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]

@property
def user_config_dir(self) -> str:
Expand All @@ -42,8 +55,20 @@ def user_cache_dir(self) -> str:

@property
def site_cache_dir(self) -> str:
""":return: cache directory shared by users, e.g. ``/Library/Caches/$appname/$version``"""
return self._append_app_name_and_version("/Library/Caches")
"""
:return: cache directory shared by users, e.g. ``/Library/Caches/$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. ``/opt/homebrew/var/cache/$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.
``/opt/homebrew/var/cache/$appname/$version:/Library/Caches/$appname/$version``
"""
is_homebrew = sys.prefix.startswith("/opt/homebrew")
path_list = [self._append_app_name_and_version("/opt/homebrew/var/cache")] if is_homebrew else []
path_list.append(self._append_app_name_and_version("/Library/Caches"))
if self.multipath:
return os.pathsep.join(path_list)
return path_list[0]

@property
def user_state_dir(self) -> str:
Expand Down
68 changes: 66 additions & 2 deletions tests/test_macos.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
from __future__ import annotations

import os
import sys
from pathlib import Path
from typing import Any
from typing import TYPE_CHECKING, Any

import pytest

from platformdirs.macos import MacOS

if TYPE_CHECKING:
from pytest_mock import MockerFixture


@pytest.fixture(autouse=True)
def _fix_os_pathsep(mocker: MockerFixture) -> None:
"""
If we're not actually running on macOS, set `os.pathsep` to what it should be on macOS.
"""
if sys.platform != "darwin": # pragma: darwin no cover
mocker.patch("os.pathsep", ":")
mocker.patch("os.path.pathsep", ":")


@pytest.mark.parametrize(
"params",
Expand All @@ -17,7 +31,15 @@
pytest.param({"appname": "foo", "version": "v1.0"}, id="app_name_version"),
],
)
def test_macos(params: dict[str, Any], func: str) -> None:
def test_macos(mocker: MockerFixture, params: dict[str, Any], func: str) -> None:
# Make sure we are not in Homebrew
py_version = sys.version_info
builtin_py_prefix = (
"/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework"
f"/Versions/{py_version.major}.{py_version.minor}"
)
mocker.patch("sys.prefix", builtin_py_prefix)

result = getattr(MacOS(**params), func)

home = str(Path("~").expanduser())
Expand Down Expand Up @@ -45,3 +67,45 @@ def test_macos(params: dict[str, Any], func: str) -> None:
expected = expected_map[func]

assert result == expected


@pytest.mark.parametrize(
"params",
[
pytest.param({}, id="no_args"),
pytest.param({"appname": "foo"}, id="app_name"),
pytest.param({"appname": "foo", "version": "v1.0"}, id="app_name_version"),
],
)
@pytest.mark.parametrize(
"site_func",
[
"site_data_dir",
"site_config_dir",
"site_cache_dir",
"site_runtime_dir",
],
)
@pytest.mark.parametrize("multipath", [pytest.param(True, id="multipath"), pytest.param(False, id="singlepath")])
def test_macos_homebrew(mocker: MockerFixture, params: dict[str, Any], multipath: bool, site_func: str) -> None:
mocker.patch("sys.prefix", "/opt/homebrew/opt/python")

result = getattr(MacOS(multipath=multipath, **params), site_func)

home = str(Path("~").expanduser())
suffix_elements = tuple(params[i] for i in ("appname", "version") if i in params)
suffix = os.sep.join(("", *suffix_elements)) if suffix_elements else "" # noqa: PTH118

expected_map = {
"site_data_dir": f"/opt/homebrew/share{suffix}",
"site_config_dir": f"/opt/homebrew/share{suffix}",
"site_cache_dir": f"/opt/homebrew/var/cache{suffix}",
"site_runtime_dir": f"{home}/Library/Caches/TemporaryItems{suffix}",
}
if multipath:
expected_map["site_data_dir"] += f":/Library/Application Support{suffix}"
expected_map["site_config_dir"] += f":/Library/Application Support{suffix}"
expected_map["site_cache_dir"] += f":/Library/Caches{suffix}"
expected = expected_map[site_func]

assert result == expected

0 comments on commit 682cdcb

Please sign in to comment.