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
24 changes: 15 additions & 9 deletions src/platformdirs/macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,25 @@ class _MacOSDefaults(PlatformDirsABC): # noqa: PLR0904

"""

@property
def user_data_dir(self) -> str:
""":returns: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
def _base_user_app_support_dir(self) -> str:
return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support")) # noqa: PTH111

@property
def _site_data_dirs(self) -> list[str]:
def _base_site_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"))
return path_list

@property
def user_data_dir(self) -> str:
""":returns: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
return self._base_user_app_support_dir()

@property
def _site_data_dirs(self) -> list[str]:
return self._base_site_dirs()

@property
def site_data_path(self) -> Path:
""":returns: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
Expand All @@ -46,11 +52,11 @@ def site_data_path(self) -> Path:
@property
def user_config_dir(self) -> str:
""":returns: config directory tied to the user, same as `user_data_dir`"""
return self.user_data_dir
return self._base_user_app_support_dir()

@property
def _site_config_dirs(self) -> list[str]:
return self._site_data_dirs
return self._base_site_dirs()

@property
def user_cache_dir(self) -> str:
Expand All @@ -76,12 +82,12 @@ def site_cache_path(self) -> Path:
@property
def user_state_dir(self) -> str:
""":returns: state directory tied to the user, same as `user_data_dir`"""
return self.user_data_dir
return self._base_user_app_support_dir()

@property
def site_state_dir(self) -> str:
""":returns: state directory shared by users, same as `site_data_dir`"""
return self.site_data_dir
return self._base_site_dirs()[0]

@property
def user_log_dir(self) -> str:
Expand Down
58 changes: 58 additions & 0 deletions tests/test_macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,36 @@ def test_macos_xdg_empty_falls_back(
assert getattr(MacOS(), prop) == expected_map[prop]


@pytest.mark.parametrize(
("env_var_1", "prop_1", "env_var_2", "prop_2"),
[
pytest.param("XDG_DATA_HOME", "user_data_dir", "XDG_CONFIG_HOME", "user_config_dir", id="data/config"),
pytest.param("XDG_DATA_HOME", "user_data_dir", "XDG_STATE_HOME", "user_state_dir", id="data/state"),
pytest.param("XDG_CONFIG_HOME", "user_config_dir", "XDG_STATE_HOME", "user_state_dir", id="config/state"),
],
)
@pytest.mark.usefixtures("_clear_xdg_env")
def test_no_xdg_vars_leak(
monkeypatch: pytest.MonkeyPatch, env_var_1: str, prop_1: str, env_var_2: str, prop_2: str
) -> None:
value_1, value_2 = ("/foo/bar", "/xyz/jkl")

monkeypatch.setenv(env_var_1, value_1)
monkeypatch.setenv(env_var_2, "")
assert getattr(MacOS(), prop_1) == value_1
assert getattr(MacOS(), prop_1) != getattr(MacOS(), prop_2)

monkeypatch.setenv(env_var_1, "")
monkeypatch.setenv(env_var_2, value_2)
assert getattr(MacOS(), prop_2) == value_2
assert getattr(MacOS(), prop_1) != getattr(MacOS(), prop_2)

monkeypatch.setenv(env_var_1, value_1)
monkeypatch.setenv(env_var_2, value_2)
assert getattr(MacOS(), prop_1) == value_1
assert getattr(MacOS(), prop_2) == value_2


def test_iter_data_dirs_xdg(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("XDG_DATA_HOME", "/xdg/data")
monkeypatch.setenv("XDG_DATA_DIRS", "/xdg/share1:/xdg/share2")
Expand Down Expand Up @@ -318,3 +348,31 @@ def test_iter_data_dirs_no_homebrew(mocker: MockerFixture) -> None:
dirs = list(MacOS().iter_data_dirs())
home = str(Path("~").expanduser())
assert dirs == [f"{home}/Library/Application Support", "/Library/Application Support"]


@pytest.mark.usefixtures("_clear_xdg_env")
def test_no_xdg_site_dirs_leak(monkeypatch: pytest.MonkeyPatch) -> None:
data_value, config_value = ("/foo/bar/data", "/xyz/jkl/config")

monkeypatch.setenv("XDG_DATA_DIRS", data_value)
monkeypatch.setenv("XDG_CONFIG_DIRS", "")
data_dirs = list(MacOS().iter_data_dirs())
config_dirs = list(MacOS().iter_config_dirs())
assert data_value in data_dirs
assert data_value not in config_dirs

monkeypatch.setenv("XDG_DATA_DIRS", "")
monkeypatch.setenv("XDG_CONFIG_DIRS", config_value)
data_dirs = list(MacOS().iter_data_dirs())
config_dirs = list(MacOS().iter_config_dirs())
assert config_value in config_dirs
assert config_value not in data_dirs

monkeypatch.setenv("XDG_DATA_DIRS", data_value)
monkeypatch.setenv("XDG_CONFIG_DIRS", config_value)
data_dirs = list(MacOS().iter_data_dirs())
config_dirs = list(MacOS().iter_config_dirs())
assert data_value in data_dirs
assert config_value in config_dirs
assert config_value not in data_dirs
assert data_value not in config_dirs