Skip to content

Commit

Permalink
Add support for Homebrew-installed software
Browse files Browse the repository at this point in the history
  • Loading branch information
singingwolfboy committed Sep 28, 2023
1 parent d63b824 commit 684252f
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 8 deletions.
5 changes: 3 additions & 2 deletions src/platformdirs/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ 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. This is only applicable for Linux/Unix systems,
as well as macOS systems where `Homebrew <https://brew.sh>` is installed.
"""
self.opinion = opinion #: A flag to indicating to use opinionated values.
self.ensure_exists = ensure_exists
Expand Down
35 changes: 31 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,21 @@ 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``
"""
path_list = [self._append_app_name_and_version("/Library/Application Support")]
is_homebrew = sys.prefix.startswith("/opt/homebrew")
if is_homebrew:
path_list.insert(0, self._append_app_name_and_version("/opt/homebrew/share"))
if self.multipath:
return os.pathsep.join(path_list)
return path_list[0]

@property
def user_config_dir(self) -> str:
Expand All @@ -42,8 +56,21 @@ 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:/opt/homebrew/share/$appname/$version``
"""
path_list = [self._append_app_name_and_version("/Library/Caches")]
is_homebrew = sys.prefix.startswith("/opt/homebrew")
if is_homebrew:
path_list.insert(0, self._append_app_name_and_version("/opt/homebrew/var/cache"))
if self.multipath:
return os.pathsep.join(path_list)
return path_list[0]

@property
def user_state_dir(self) -> str:
Expand Down
63 changes: 61 additions & 2 deletions tests/test_macos.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
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.mark.parametrize(
"params",
Expand All @@ -17,7 +21,17 @@
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
mocker.patch(
"sys.prefix",
(
"/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions"
f"/{py_version.major}.{py_version.minor}"
),
)

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

home = str(Path("~").expanduser())
Expand Down Expand Up @@ -45,3 +59,48 @@ 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("multipath", [pytest.param(True, id="multipath"), pytest.param(False, id="singlepath")])
def test_macos_homebrew(mocker: MockerFixture, params: dict[str, Any], multipath: bool, func: str) -> None:
mocker.patch("sys.prefix", "/opt/homebrew/opt/python")

result = getattr(MacOS(multipath=multipath, **params), 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 = {
"user_data_dir": f"{home}/Library/Application Support{suffix}",
"site_data_dir": f"/opt/homebrew/share{suffix}",
"user_config_dir": f"{home}/Library/Application Support{suffix}",
"site_config_dir": f"/opt/homebrew/share{suffix}",
"user_cache_dir": f"{home}/Library/Caches{suffix}",
"site_cache_dir": f"/opt/homebrew/var/cache{suffix}",
"user_state_dir": f"{home}/Library/Application Support{suffix}",
"user_log_dir": f"{home}/Library/Logs{suffix}",
"user_documents_dir": f"{home}/Documents",
"user_downloads_dir": f"{home}/Downloads",
"user_pictures_dir": f"{home}/Pictures",
"user_videos_dir": f"{home}/Movies",
"user_music_dir": f"{home}/Music",
"user_desktop_dir": f"{home}/Desktop",
"user_runtime_dir": f"{home}/Library/Caches/TemporaryItems{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[func]

assert result == expected

0 comments on commit 684252f

Please sign in to comment.