Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-platformdirs for
openSUSE:Factory checked in at 2026-09-07 11:28:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-platformdirs (Old)
and /work/SRC/openSUSE:Factory/.python-platformdirs.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-platformdirs"
Mon Sep 7 11:28:09 2026 rev:24 rq:1375228 version:4.11.7
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-platformdirs/python-platformdirs.changes
2026-08-24 12:02:05.082633853 +0200
+++
/work/SRC/openSUSE:Factory/.python-platformdirs.new.1265/python-platformdirs.changes
2026-09-07 11:29:20.134023349 +0200
@@ -1,0 +2,15 @@
+Tue Sep 1 20:50:57 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 4.11.7:
+ * fix: accept use_site_for_root in the bin functions
+- update to 4.11.5:
+ * docs: fix the iterator order claim in api.rst
+ * fix: return one site applications path for multipath
+ * fix: accept app arguments in the applications functions
+ * refactor: keyword-only booleans on the applications functions
+ * fix: accept app arguments in user_preference_dir
+- update to 4.11.4:
+ * fix: stop iter_*_dirs yielding the same directory twice
+ * docs: fix merge order in the config how-to
+
+-------------------------------------------------------------------
Old:
----
platformdirs-4.11.3.tar.gz
New:
----
platformdirs-4.11.7.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-platformdirs.spec ++++++
--- /var/tmp/diff_new_pack.6UiaHF/_old 2026-09-07 11:29:20.838048039 +0200
+++ /var/tmp/diff_new_pack.6UiaHF/_new 2026-09-07 11:29:20.839048074 +0200
@@ -27,7 +27,7 @@
%{?pythons_for_pypi}
%{?sle15_python_module_pythons}
Name: python-platformdirs%{psuffix}
-Version: 4.11.3
+Version: 4.11.7
Release: 0
Summary: Module for determining appropriate platform-specific dirs
License: MIT
++++++ platformdirs-4.11.3.tar.gz -> platformdirs-4.11.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/PKG-INFO
new/platformdirs-4.11.7/PKG-INFO
--- old/platformdirs-4.11.3/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.5
Name: platformdirs
-Version: 4.11.3
+Version: 4.11.7
Summary: A small Python package for determining appropriate platform-specific
dirs, e.g. a `user data dir`.
Project-URL: Changelog,
https://platformdirs.readthedocs.io/en/latest/changelog.html
Project-URL: Documentation, https://platformdirs.readthedocs.io
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/pyproject.toml
new/platformdirs-4.11.7/pyproject.toml
--- old/platformdirs-4.11.3/pyproject.toml 2020-02-02 01:00:00.000000000
+0100
+++ new/platformdirs-4.11.7/pyproject.toml 2020-02-02 01:00:00.000000000
+0100
@@ -133,14 +133,15 @@
"S104", # Possible binding to all interfaces
]
lint.per-file-ignores."tests/**/*.py" = [
- "D", # don't care about documentation in tests
- "FBT", # don't care about booleans as positional arguments in tests
- "INP001", # no implicit namespace
- "PLC2701", # Private name import
- "PLR0917", # Too many positional arguments
- "PLR2004", # Magic value used in comparison, consider replacing with a
constant variable
- "S101", # asserts allowed in tests
- "S603", # `subprocess` call: check for execution of untrusted input
+ "D", # don't care about documentation in tests
+ "FBT", # don't care about booleans as positional
arguments in tests
+ "INP001", # no implicit namespace
+ "PLC2701", # Private name import
+ "PLR0917", # Too many positional arguments
+ "PLR2004", # Magic value used in comparison, consider
replacing with a constant variable
+ "pytest-fixture-autouse", # shared setup fixtures are meant to apply to
every test
+ "S101", # asserts allowed in tests
+ "S603", # `subprocess` call: check for execution of
untrusted input
]
lint.isort = { known-first-party = [
"platformdirs",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/src/platformdirs/__init__.py
new/platformdirs-4.11.7/src/platformdirs/__init__.py
--- old/platformdirs-4.11.3/src/platformdirs/__init__.py 2020-02-02
01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/src/platformdirs/__init__.py 2020-02-02
01:00:00.000000000 +0100
@@ -364,14 +364,42 @@
return PlatformDirs().user_fonts_dir
-def user_preference_dir() -> str:
- """:returns: preference directory tied to the user"""
- return PlatformDirs().user_preference_dir
+def user_preference_dir( # ruff:ignore[too-many-arguments]
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ *,
+ roaming: bool = False,
+ ensure_exists: bool = False,
+ use_site_for_root: bool = False,
+) -> str:
+ """:param appname: See `appname
<platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor
<platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
+ :param ensure_exists: See `ensure_exists
<platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root
<platformdirs.api.PlatformDirsABC.use_site_for_root>`.
+ :returns: preference directory tied to the user
+
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ roaming=roaming,
+ ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
+ ).user_preference_dir
-def user_bin_dir() -> str:
- """:returns: bin directory tied to the user"""
- return PlatformDirs().user_bin_dir
+
+def user_bin_dir(*, use_site_for_root: bool = False) -> str:
+ """:param use_site_for_root: See `use_site_for_root
<platformdirs.api.PlatformDirsABC.use_site_for_root>`.
+
+ :returns: bin directory tied to the user
+
+ """
+ return PlatformDirs(use_site_for_root=use_site_for_root).user_bin_dir
def site_bin_dir() -> str:
@@ -379,22 +407,53 @@
return PlatformDirs().site_bin_dir
-def user_applications_dir() -> str:
- """:returns: applications directory tied to the user"""
- return PlatformDirs().user_applications_dir
+def user_applications_dir(
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ *,
+ ensure_exists: bool = False,
+ use_site_for_root: bool = False,
+) -> str:
+ """:param appname: See `appname
<platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor
<platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param ensure_exists: See `ensure_exists
<platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root
<platformdirs.api.PlatformDirsABC.use_site_for_root>`.
+
+ :returns: applications directory tied to the user
+
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
+ ).user_applications_dir
def site_applications_dir(
multipath: bool = False, #
ruff:ignore[boolean-type-hint-positional-argument,
boolean-default-value-positional-argument]
ensure_exists: bool = False, #
ruff:ignore[boolean-type-hint-positional-argument,
boolean-default-value-positional-argument]
+ *,
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
) -> str:
""":param multipath: See `multipath
<platformdirs.api.PlatformDirsABC.multipath>`.
:param ensure_exists: See `ensure_exists
<platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor
<platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:returns: applications directory shared by users
"""
return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
multipath=multipath,
ensure_exists=ensure_exists,
).site_applications_dir
@@ -765,14 +824,42 @@
return PlatformDirs().user_fonts_path
-def user_preference_path() -> Path:
- """:returns: preference path tied to the user"""
- return PlatformDirs().user_preference_path
+def user_preference_path( # ruff:ignore[too-many-arguments]
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ *,
+ roaming: bool = False,
+ ensure_exists: bool = False,
+ use_site_for_root: bool = False,
+) -> Path:
+ """:param appname: See `appname
<platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor
<platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
+ :param ensure_exists: See `ensure_exists
<platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root
<platformdirs.api.PlatformDirsABC.use_site_for_root>`.
+ :returns: preference path tied to the user
+
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ roaming=roaming,
+ ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
+ ).user_preference_path
-def user_bin_path() -> Path:
- """:returns: bin path tied to the user"""
- return PlatformDirs().user_bin_path
+
+def user_bin_path(*, use_site_for_root: bool = False) -> Path:
+ """:param use_site_for_root: See `use_site_for_root
<platformdirs.api.PlatformDirsABC.use_site_for_root>`.
+
+ :returns: bin path tied to the user
+
+ """
+ return PlatformDirs(use_site_for_root=use_site_for_root).user_bin_path
def site_bin_path() -> Path:
@@ -780,22 +867,53 @@
return PlatformDirs().site_bin_path
-def user_applications_path() -> Path:
- """:returns: applications path tied to the user"""
- return PlatformDirs().user_applications_path
+def user_applications_path(
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ *,
+ ensure_exists: bool = False,
+ use_site_for_root: bool = False,
+) -> Path:
+ """:param appname: See `appname
<platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor
<platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param ensure_exists: See `ensure_exists
<platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root
<platformdirs.api.PlatformDirsABC.use_site_for_root>`.
+
+ :returns: applications path tied to the user
+
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
+ ).user_applications_path
def site_applications_path(
multipath: bool = False, #
ruff:ignore[boolean-type-hint-positional-argument,
boolean-default-value-positional-argument]
ensure_exists: bool = False, #
ruff:ignore[boolean-type-hint-positional-argument,
boolean-default-value-positional-argument]
+ *,
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
) -> Path:
""":param multipath: See `multipath
<platformdirs.api.PlatformDirsABC.multipath>`.
:param ensure_exists: See `ensure_exists
<platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor
<platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:returns: applications path shared by users
"""
return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
multipath=multipath,
ensure_exists=ensure_exists,
).site_applications_path
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/src/platformdirs/api.py
new/platformdirs-4.11.7/src/platformdirs/api.py
--- old/platformdirs-4.11.3/src/platformdirs/api.py 2020-02-02
01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/src/platformdirs/api.py 2020-02-02
01:00:00.000000000 +0100
@@ -8,7 +8,7 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
- from collections.abc import Iterator
+ from collections.abc import Iterable, Iterator
from typing import Literal
@@ -389,8 +389,8 @@
@property
def site_applications_path(self) -> Path:
- """Applications path shared by users."""
- return Path(self.site_applications_dir)
+ """Applications path shared by users. Only return the first item, even
if ``multipath`` is set to ``True``."""
+ return
self._first_item_as_path_if_multipath(self.site_applications_dir)
@property
def user_runtime_path(self) -> Path:
@@ -404,31 +404,49 @@
def iter_config_dirs(self) -> Iterator[str]:
""":yield: all user and site configuration directories."""
+ yield from _unique(self._iter_config_dirs())
+
+ def _iter_config_dirs(self) -> Iterator[str]:
yield self.user_config_dir
yield self.site_config_dir
def iter_data_dirs(self) -> Iterator[str]:
""":yield: all user and site data directories."""
+ yield from _unique(self._iter_data_dirs())
+
+ def _iter_data_dirs(self) -> Iterator[str]:
yield self.user_data_dir
yield self.site_data_dir
def iter_cache_dirs(self) -> Iterator[str]:
""":yield: all user and site cache directories."""
+ yield from _unique(self._iter_cache_dirs())
+
+ def _iter_cache_dirs(self) -> Iterator[str]:
yield self.user_cache_dir
yield self.site_cache_dir
def iter_state_dirs(self) -> Iterator[str]:
""":yield: all user and site state directories."""
+ yield from _unique(self._iter_state_dirs())
+
+ def _iter_state_dirs(self) -> Iterator[str]:
yield self.user_state_dir
yield self.site_state_dir
def iter_log_dirs(self) -> Iterator[str]:
""":yield: all user and site log directories."""
+ yield from _unique(self._iter_log_dirs())
+
+ def _iter_log_dirs(self) -> Iterator[str]:
yield self.user_log_dir
yield self.site_log_dir
def iter_runtime_dirs(self) -> Iterator[str]:
""":yield: all user and site runtime directories."""
+ yield from _unique(self._iter_runtime_dirs())
+
+ def _iter_runtime_dirs(self) -> Iterator[str]:
yield self.user_runtime_dir
yield self.site_runtime_dir
@@ -461,3 +479,14 @@
""":yield: all user and site runtime paths."""
for path in self.iter_runtime_dirs():
yield Path(path)
+
+
+def _unique(dirs: Iterable[str]) -> Iterator[str]:
+ """:yield: ``dirs`` in order, skipping any directory already yielded."""
+ # Lazy on purpose: under ensure_exists reading a site_*_dir creates it, so
draining ``dirs`` up front would
+ # create directories for a caller that stops after the first entry.
+ seen: set[str] = set()
+ for path in dirs:
+ if path not in seen:
+ seen.add(path)
+ yield path
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/src/platformdirs/macos.py
new/platformdirs-4.11.7/src/platformdirs/macos.py
--- old/platformdirs-4.11.3/src/platformdirs/macos.py 2020-02-02
01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/src/platformdirs/macos.py 2020-02-02
01:00:00.000000000 +0100
@@ -197,18 +197,15 @@
"""Runtime directory shared by users, same as `user_runtime_dir`."""
return self.user_runtime_dir
- def iter_config_dirs(self) -> Iterator[str]:
- """:yield: all user and site configuration directories."""
+ def _iter_config_dirs(self) -> Iterator[str]:
yield self.user_config_dir
yield from self._site_config_dirs
- def iter_data_dirs(self) -> Iterator[str]:
- """:yield: all user and site data directories."""
+ def _iter_data_dirs(self) -> Iterator[str]:
yield self.user_data_dir
yield from self._site_data_dirs
- def iter_cache_dirs(self) -> Iterator[str]:
- """:yield: all user and site cache directories."""
+ def _iter_cache_dirs(self) -> Iterator[str]:
yield self.user_cache_dir
yield from self._site_cache_dirs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/src/platformdirs/unix.py
new/platformdirs-4.11.7/src/platformdirs/unix.py
--- old/platformdirs-4.11.3/src/platformdirs/unix.py 2020-02-02
01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/src/platformdirs/unix.py 2020-02-02
01:00:00.000000000 +0100
@@ -226,38 +226,34 @@
"""Cache path shared by users. Only return the first item, even if
``multipath`` is set to ``True``."""
return self._first_item_as_path_if_multipath(self.site_cache_dir)
- def iter_config_dirs(self) -> Iterator[str]:
- """:yield: all user and site configuration directories."""
+ def _iter_config_dirs(self) -> Iterator[str]:
+ # Under multipath the user dir is an os.pathsep-joined string that no
single site entry matches, so the
+ # dedupe in iter_config_dirs cannot drop it. Skip it here instead.
if not self._use_site:
yield self.user_config_dir
yield from self._site_config_dirs
- def iter_data_dirs(self) -> Iterator[str]:
- """:yield: all user and site data directories."""
+ def _iter_data_dirs(self) -> Iterator[str]:
if not self._use_site:
yield self.user_data_dir
yield from self._site_data_dirs
- def iter_cache_dirs(self) -> Iterator[str]:
- """:yield: all user and site cache directories."""
+ def _iter_cache_dirs(self) -> Iterator[str]:
if not self._use_site:
yield self.user_cache_dir
yield self.site_cache_dir
- def iter_state_dirs(self) -> Iterator[str]:
- """:yield: all user and site state directories."""
+ def _iter_state_dirs(self) -> Iterator[str]:
if not self._use_site:
yield self.user_state_dir
yield self.site_state_dir
- def iter_log_dirs(self) -> Iterator[str]:
- """:yield: all user and site log directories."""
+ def _iter_log_dirs(self) -> Iterator[str]:
if not self._use_site:
yield self.user_log_dir
yield self.site_log_dir
- def iter_runtime_dirs(self) -> Iterator[str]:
- """:yield: all user and site runtime directories."""
+ def _iter_runtime_dirs(self) -> Iterator[str]:
if not self._use_site:
yield self.user_runtime_dir
yield self.site_runtime_dir
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/src/platformdirs/version.py
new/platformdirs-4.11.7/src/platformdirs/version.py
--- old/platformdirs-4.11.3/src/platformdirs/version.py 2020-02-02
01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/src/platformdirs/version.py 2020-02-02
01:00:00.000000000 +0100
@@ -18,7 +18,7 @@
commit_id: str | None
__commit_id__: str | None
-__version__ = version = '4.11.3'
-__version_tuple__ = version_tuple = (4, 11, 3)
+__version__ = version = '4.11.7'
+__version_tuple__ = version_tuple = (4, 11, 7)
__commit_id__ = commit_id = None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/tests/test_android.py
new/platformdirs-4.11.7/tests/test_android.py
--- old/platformdirs-4.11.3/tests/test_android.py 2020-02-02
01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/tests/test_android.py 2020-02-02
01:00:00.000000000 +0100
@@ -1,19 +1,25 @@
from __future__ import annotations
import sys
-from typing import TYPE_CHECKING, Any
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Final
from unittest.mock import MagicMock
import pytest
+import platformdirs
from platformdirs.android import Android
if TYPE_CHECKING:
- from pathlib import Path
-
from pytest_mock import MockerFixture
[email protected]
+def _example_android_folder(mocker: MockerFixture) -> None:
+ mocker.patch("platformdirs.android._android_folder",
return_value="/data/data/com.example", autospec=True)
+ mocker.patch("platformdirs.android.os.path.join", lambda *args:
"/".join(args))
+
+
@pytest.mark.parametrize(
"params",
[
@@ -31,9 +37,8 @@
"app_name_author_version_false_opinion",
],
)
-def test_android(mocker: MockerFixture, params: dict[str, Any], func: str) ->
None:
- mocker.patch("platformdirs.android._android_folder",
return_value="/data/data/com.example", autospec=True)
- mocker.patch("platformdirs.android.os.path.join", lambda *args:
"/".join(args))
[email protected]("_example_android_folder")
+def test_android(params: dict[str, Any], func: str) -> None:
result = getattr(Android(**params), func)
suffix_elements = []
@@ -178,8 +183,6 @@
prop: str,
subdir: str,
) -> None:
- from pathlib import Path # ruff:ignore[import-outside-top-level]
-
mocker.patch("platformdirs.android._android_folder",
return_value=str(tmp_path), autospec=True)
cache_dir = tmp_path / "cache"
cache_dir.mkdir()
@@ -190,3 +193,38 @@
expected = str(cache_dir / "myapp" / subdir)
assert result == expected
assert Path(result).is_dir()
+
+
[email protected](
+ ("func", "expected"),
+ [
+ pytest.param("iter_config_dirs",
"/data/data/com.example/shared_prefs/foo", id="config"),
+ pytest.param("iter_data_dirs", "/data/data/com.example/files/foo",
id="data"),
+ pytest.param("iter_cache_dirs", "/data/data/com.example/cache/foo",
id="cache"),
+ pytest.param("iter_state_dirs", "/data/data/com.example/files/foo",
id="state"),
+ pytest.param("iter_log_dirs", "/data/data/com.example/cache/foo/log",
id="log"),
+ pytest.param("iter_runtime_dirs",
"/data/data/com.example/cache/foo/tmp", id="runtime"),
+ ],
+)
[email protected]("_example_android_folder")
+def test_android_iter_dirs_no_duplicates(func: str, expected: str) -> None:
+ # Every site_*_dir on Android is defined as its user_*_dir.
+ assert list(getattr(Android(appname="foo"), func)()) == [expected]
+
+
+_SCOPED_APPLICATIONS_DIR: Final[str] = "/data/data/com.example/files/foo/1.0"
+
+
[email protected]("func", ["user_applications_dir",
"site_applications_dir"])
[email protected]("_example_android_folder")
+def test_android_applications_dir_function_takes_app_arguments(mocker:
MockerFixture, func: str) -> None:
+ mocker.patch("platformdirs.PlatformDirs", Android)
+ # Android scopes both applications directories to the app, so the function
has to forward the name and version.
+ assert getattr(platformdirs, func)(appname="foo", version="1.0") ==
_SCOPED_APPLICATIONS_DIR
+
+
[email protected]("func", ["user_applications_path",
"site_applications_path"])
[email protected]("_example_android_folder")
+def test_android_applications_path_function_takes_app_arguments(mocker:
MockerFixture, func: str) -> None:
+ mocker.patch("platformdirs.PlatformDirs", Android)
+ assert getattr(platformdirs, func)(appname="foo", version="1.0") ==
Path(_SCOPED_APPLICATIONS_DIR)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/tests/test_api.py
new/platformdirs-4.11.7/tests/test_api.py
--- old/platformdirs-4.11.3/tests/test_api.py 2020-02-02 01:00:00.000000000
+0100
+++ new/platformdirs-4.11.7/tests/test_api.py 2020-02-02 01:00:00.000000000
+0100
@@ -59,6 +59,34 @@
assert function_dir_signature.parameters ==
function_path_signature.parameters
[email protected]("func", ["user_applications_dir",
"user_applications_path"])
+def test_user_applications_function_boolean_options_are_keyword_only(func:
str) -> None:
+ # These options have not shipped yet, so they can be keyword-only without
breaking any caller.
+ parameters = inspect.Signature.from_callable(getattr(platformdirs,
func)).parameters
+ positional = [name for name, param in parameters.items() if param.kind is
param.POSITIONAL_OR_KEYWORD]
+ assert positional == ["appname", "appauthor", "version"]
+
+
[email protected]("func", ["site_applications_dir",
"site_applications_path"])
+def test_site_applications_function_keeps_multipath_positional(func: str) ->
None:
+ # multipath has been the first positional argument since 4.9.0, so the app
arguments are keyword-only.
+ parameters = inspect.Signature.from_callable(getattr(platformdirs,
func)).parameters
+ positional = [name for name, param in parameters.items() if param.kind is
param.POSITIONAL_OR_KEYWORD]
+ assert positional == ["multipath", "ensure_exists"]
+
+
+def test_function_matches_its_property_for_app_arguments(func: str) -> None:
+ function = getattr(platformdirs, func)
+ scoped = getattr(platformdirs.PlatformDirs("MyApp", "MyCompany",
version="1.0"), func)
+ if {"appname", "version"} <=
inspect.Signature.from_callable(function).parameters.keys():
+ assert function(appname="MyApp", appauthor="MyCompany", version="1.0")
== scoped
+ else:
+ # A function without the app arguments can only ever return the
unscoped base directory, so a property that
+ # is app-scoped on any platform is out of its reach. Only one
direction holds: a function may have to take
+ # arguments this platform ignores because another platform scopes the
same property.
+ assert scoped == getattr(platformdirs.PlatformDirs(), func)
+
+
@pytest.mark.parametrize("root", ["A", "/system", None])
@pytest.mark.parametrize("data", ["D", "/data", None])
@pytest.mark.parametrize("path", ["/data/data/a/files", "/C"])
@@ -141,3 +169,10 @@
class PlatformDirsSubclass(platformdirs.PlatformDirs): ...
class AppDirsSubclass(platformdirs.AppDirs): ...
+
+
[email protected]("kind", ["config", "data", "cache", "state", "log",
"runtime"])
+def test_iter_dirs_yields_user_before_site(kind: str) -> None:
+ # docs/howto.rst merges config in reverse of this order so the user
directory wins.
+ dirs = platformdirs.PlatformDirs("MyApp", "MyCompany", version="1.0")
+ assert next(getattr(dirs, f"iter_{kind}_dirs")()) == getattr(dirs,
f"user_{kind}_dir")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/tests/test_macos.py
new/platformdirs-4.11.7/tests/test_macos.py
--- old/platformdirs-4.11.3/tests/test_macos.py 2020-02-02 01:00:00.000000000
+0100
+++ new/platformdirs-4.11.7/tests/test_macos.py 2020-02-02 01:00:00.000000000
+0100
@@ -44,6 +44,16 @@
@pytest.fixture
+def home() -> str:
+ return str(Path("~").expanduser())
+
+
[email protected]
+def _homebrew_py_prefix(mocker: MockerFixture) -> None:
+ mocker.patch("sys.prefix",
"/opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.13")
+
+
[email protected]
def _builtin_py_prefix(mocker: MockerFixture) -> None:
"""Keep ``sys.prefix`` off the ``/opt/python`` Homebrew heuristic so
directories use the system defaults."""
py_version = sys.version_info
@@ -63,10 +73,9 @@
],
)
@pytest.mark.usefixtures("_clear_xdg_env", "_builtin_py_prefix")
-def test_macos(params: dict[str, Any], func: str) -> None:
+def test_macos(home: str, params: dict[str, Any], func: str) -> None:
result = getattr(MacOS(**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 "" #
ruff:ignore[os-path-join]
@@ -126,7 +135,9 @@
)
@pytest.mark.parametrize("multipath", [pytest.param(True, id="multipath"),
pytest.param(False, id="singlepath")])
@pytest.mark.usefixtures("_clear_xdg_env")
-def test_macos_homebrew(mocker: MockerFixture, params: dict[str, Any],
multipath: bool, site_func: str) -> None:
+def test_macos_homebrew(
+ mocker: MockerFixture, home: str, params: dict[str, Any], multipath: bool,
site_func: str
+) -> None:
test_data = [
{
"sys_prefix":
"/opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.13",
@@ -146,7 +157,6 @@
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
"" # ruff:ignore[os-path-join]
@@ -258,9 +268,8 @@
],
)
@pytest.mark.usefixtures("_clear_xdg_env", "_builtin_py_prefix")
-def test_macos_xdg_empty_falls_back(monkeypatch: pytest.MonkeyPatch, env_var:
str, prop: str) -> None:
+def test_macos_xdg_empty_falls_back(monkeypatch: pytest.MonkeyPatch, home:
str, env_var: str, prop: str) -> None:
monkeypatch.setenv(env_var, "")
- home = str(Path("~").expanduser())
expected_map = {
"user_data_dir": f"{home}/Library/Application Support",
"user_config_dir": f"{home}/Library/Application Support",
@@ -329,22 +338,24 @@
assert dirs == ["/xdg/config", "/xdg/etc1", "/xdg/etc2"]
[email protected]("_clear_xdg_env")
-def test_iter_data_dirs_homebrew(mocker: MockerFixture) -> None:
- mocker.patch("sys.prefix",
"/opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.13")
[email protected]("_clear_xdg_env", "_homebrew_py_prefix")
+def test_iter_data_dirs_homebrew(home: str) -> None:
dirs = list(MacOS().iter_data_dirs())
- home = str(Path("~").expanduser())
assert dirs == [f"{home}/Library/Application Support",
"/opt/homebrew/share", "/Library/Application Support"]
[email protected]("_clear_xdg_env")
-def test_iter_config_dirs_homebrew(mocker: MockerFixture) -> None:
- mocker.patch("sys.prefix",
"/opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.13")
[email protected]("_clear_xdg_env", "_homebrew_py_prefix")
+def test_iter_config_dirs_homebrew(home: str) -> None:
dirs = list(MacOS().iter_config_dirs())
- home = str(Path("~").expanduser())
assert dirs == [f"{home}/Library/Application Support",
"/opt/homebrew/share", "/Library/Application Support"]
[email protected]("_clear_xdg_env")
+def test_site_applications_path_multipath_returns_first_path(monkeypatch:
pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("XDG_DATA_DIRS",
f"/custom/first{os.pathsep}/custom/second")
+ assert MacOS(multipath=True).site_applications_path ==
Path("/custom/first/applications")
+
+
@pytest.mark.usefixtures("_clear_xdg_env", "_builtin_py_prefix")
@pytest.mark.parametrize(
"value",
@@ -368,27 +379,22 @@
assert getattr(MacOS(appname="foo"), prop) == expected
[email protected]("_clear_xdg_env")
[email protected]("_clear_xdg_env", "_homebrew_py_prefix")
@pytest.mark.parametrize("multipath", [True, False])
-def test_iter_cache_dirs_homebrew(mocker: MockerFixture, multipath: bool) ->
None:
- mocker.patch("sys.prefix",
"/opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.13")
+def test_iter_cache_dirs_homebrew(home: str, multipath: bool) -> None:
dirs = list(MacOS(multipath=multipath).iter_cache_dirs())
- home = str(Path("~").expanduser())
assert dirs == [f"{home}/Library/Caches", "/opt/homebrew/var/cache",
"/Library/Caches"]
[email protected]("_clear_xdg_env")
-def test_iter_cache_paths_homebrew_multipath(mocker: MockerFixture) -> None:
- mocker.patch("sys.prefix",
"/opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.13")
[email protected]("_clear_xdg_env", "_homebrew_py_prefix")
+def test_iter_cache_paths_homebrew_multipath(home: str) -> None:
paths = list(MacOS(multipath=True).iter_cache_paths())
- home = str(Path("~").expanduser())
assert paths == [Path(f"{home}/Library/Caches"),
Path("/opt/homebrew/var/cache"), Path("/Library/Caches")]
@pytest.mark.usefixtures("_clear_xdg_env", "_builtin_py_prefix")
-def test_iter_data_dirs_no_homebrew() -> None:
+def test_iter_data_dirs_no_homebrew(home: str) -> None:
dirs = list(MacOS().iter_data_dirs())
- home = str(Path("~").expanduser())
assert dirs == [f"{home}/Library/Application Support",
"/Library/Application Support"]
@@ -421,9 +427,8 @@
@pytest.mark.usefixtures("_clear_xdg_env", "_builtin_py_prefix")
-def test_macos_site_runtime_path() -> None:
+def test_macos_site_runtime_path(home: str) -> None:
result = MacOS(appname="foo").site_runtime_path
- home = str(Path("~").expanduser())
assert result == Path(f"{home}/Library/Caches/TemporaryItems/foo")
@@ -436,3 +441,10 @@
# Calling again with an already-existing directory must not raise.
second = dirs.user_data_dir
assert first == second
+
+
[email protected]("_clear_xdg_env", "_builtin_py_prefix")
+def test_macos_iter_runtime_dirs_no_duplicate(home: str) -> None:
+ # site_runtime_dir is defined as user_runtime_dir.
+ expected = os.path.join(f"{home}/Library/Caches/TemporaryItems", "foo") #
ruff:ignore[os-path-join]
+ assert list(MacOS(appname="foo").iter_runtime_dirs()) == [expected]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/tests/test_unix.py
new/platformdirs-4.11.7/tests/test_unix.py
--- old/platformdirs-4.11.3/tests/test_unix.py 2020-02-02 01:00:00.000000000
+0100
+++ new/platformdirs-4.11.7/tests/test_unix.py 2020-02-02 01:00:00.000000000
+0100
@@ -1,19 +1,21 @@
from __future__ import annotations
import importlib
+import inspect
import os
import sys
import typing
+from pathlib import Path
from tempfile import gettempdir
import pytest
+import platformdirs
from platformdirs import unix
from platformdirs.unix import Unix
if typing.TYPE_CHECKING:
from collections.abc import Callable, Iterator
- from pathlib import Path
from pytest_mock import MockerFixture
@@ -24,6 +26,26 @@
importlib.reload(unix)
[email protected]
+def _as_root(mocker: MockerFixture) -> None:
+ mocker.patch("platformdirs.unix.getuid", return_value=0)
+
+
[email protected]
+def _as_non_root(mocker: MockerFixture) -> None:
+ mocker.patch("platformdirs.unix.getuid", return_value=1000)
+
+
[email protected]
+def _writable_runtime_dir(mocker: MockerFixture) -> None:
+ mocker.patch("os.access", return_value=True)
+
+
[email protected]
+def _no_xdg_runtime_dir(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
+
+
@pytest.mark.parametrize(
"prop",
[
@@ -340,6 +362,13 @@
assert dirs == ["/xdg/config", "/xdg/etc1", "/xdg/etc2"]
+def test_iter_data_dirs_creates_only_the_consumed_dir(monkeypatch:
pytest.MonkeyPatch, tmp_path: Path) -> None:
+ monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "user"))
+ monkeypatch.setenv("XDG_DATA_DIRS", str(tmp_path / "site"))
+ next(Unix(ensure_exists=True).iter_data_dirs())
+ assert not (tmp_path / "site").exists()
+
+
@pytest.mark.parametrize(
"value",
[
@@ -368,6 +397,11 @@
assert Unix(appname="foo", multipath=True).site_data_dir ==
os.pathsep.join(dirs)
+def test_site_applications_path_multipath_returns_first_path(monkeypatch:
pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("XDG_DATA_DIRS",
f"/custom/first{os.pathsep}/custom/second")
+ assert Unix(multipath=True).site_applications_path ==
Path("/custom/first/applications")
+
+
def test_user_media_dir_from_user_dirs_file(
mocker: MockerFixture, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
@@ -432,43 +466,48 @@
),
),
("user_bin_dir", "/usr/local/bin"),
+ ("user_applications_dir", f"/usr/local/share{os.sep}applications"),
]
[email protected]("_as_root", "_no_xdg_runtime_dir")
@pytest.mark.parametrize(("prop", "expected"), _SITE_REDIRECT_CASES)
-def test_use_site_for_root_as_root(
- mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, prop: str,
expected: str
-) -> None:
- mocker.patch("platformdirs.unix.getuid", return_value=0)
- monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
+def test_use_site_for_root_as_root(prop: str, expected: str) -> None:
result = getattr(Unix(appname="foo", use_site_for_root=True), prop)
assert result == expected
[email protected]("_as_non_root", "_no_xdg_runtime_dir",
"_writable_runtime_dir")
@pytest.mark.parametrize(("prop", "expected"), _SITE_REDIRECT_CASES)
-def test_use_site_for_root_as_non_root(
- mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, prop: str,
expected: str
-) -> None:
- mocker.patch("platformdirs.unix.getuid", return_value=1000)
- monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
- mocker.patch("os.access", return_value=True)
+def test_use_site_for_root_as_non_root(prop: str, expected: str) -> None:
dirs = Unix(appname="foo", use_site_for_root=True)
result = getattr(dirs, prop)
assert result != expected
[email protected]("_as_root", "_no_xdg_runtime_dir")
[email protected]("suffix", ["dir", "path"])
@pytest.mark.parametrize(("prop", "expected"), _SITE_REDIRECT_CASES)
-def test_use_site_for_root_disabled_as_root(
- mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, prop: str,
expected: str
+def test_use_site_for_root_reaches_the_module_function(
+ mocker: MockerFixture, prop: str, expected: str, suffix: str
) -> None:
- mocker.patch("platformdirs.unix.getuid", return_value=0)
- monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
- mocker.patch("os.access", return_value=True)
+ # The module-level functions have to reach every property the site
redirect touches.
+ mocker.patch("platformdirs.PlatformDirs", Unix)
+ function = getattr(platformdirs, prop.removesuffix("dir") + suffix)
+ accepted = inspect.Signature.from_callable(function).parameters
+ options = {"use_site_for_root": True, "appname": "foo"}
+ assert Path(function(**{k: v for k, v in options.items() if k in
accepted})) == Path(expected)
+
+
[email protected]("_as_root", "_no_xdg_runtime_dir",
"_writable_runtime_dir")
[email protected](("prop", "expected"), _SITE_REDIRECT_CASES)
+def test_use_site_for_root_disabled_as_root(prop: str, expected: str) -> None:
dirs = Unix(appname="foo", use_site_for_root=False)
result = getattr(dirs, prop)
assert result != expected
[email protected]("_as_root", "_no_xdg_runtime_dir")
@pytest.mark.parametrize(
("xdg_var", "prop", "expected_site"),
[
@@ -480,15 +519,14 @@
],
)
def test_use_site_for_root_bypasses_xdg_user_vars(
- mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, xdg_var: str,
prop: str, expected_site: str
+ monkeypatch: pytest.MonkeyPatch, xdg_var: str, prop: str, expected_site:
str
) -> None:
- mocker.patch("platformdirs.unix.getuid", return_value=0)
monkeypatch.setenv(xdg_var, "/custom/xdg/path")
- monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
result = getattr(Unix(appname="foo", use_site_for_root=True), prop)
assert result == expected_site
[email protected]("_as_root")
@pytest.mark.parametrize(
("xdg_var", "func"),
[
@@ -497,12 +535,10 @@
],
)
def test_use_site_iter_dirs_no_duplicates(
- mocker: MockerFixture,
monkeypatch: pytest.MonkeyPatch,
xdg_var: str,
func: Callable[[Unix], Iterator[str]],
) -> None:
- mocker.patch("platformdirs.unix.getuid", return_value=0)
monkeypatch.setenv(xdg_var, "/custom/xdg/path")
result = func(Unix(appname="foo", use_site_for_root=True))
assert list(result) == [os.path.join("/custom/xdg/path", "foo")] #
ruff:ignore[os-path-join]
@@ -522,30 +558,41 @@
]
[email protected]("_as_root", "_no_xdg_runtime_dir")
@pytest.mark.parametrize(("func", "expected"), _SINGLE_SITE_ITER_CASES)
-def test_use_site_iter_dirs_no_duplicates_single_site_dir(
- mocker: MockerFixture,
- monkeypatch: pytest.MonkeyPatch,
- func: Callable[[Unix], Iterator[str]],
- expected: str,
-) -> None:
- mocker.patch("platformdirs.unix.getuid", return_value=0)
- monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
+def test_use_site_iter_dirs_no_duplicates_single_site_dir(func:
Callable[[Unix], Iterator[str]], expected: str) -> None:
result = func(Unix(appname="foo", use_site_for_root=True))
assert list(result) == [expected]
[email protected]("_as_non_root", "_no_xdg_runtime_dir",
"_writable_runtime_dir")
@pytest.mark.parametrize(("func", "expected"), _SINGLE_SITE_ITER_CASES)
-def test_iter_dirs_as_non_root_keeps_user_dir(
- mocker: MockerFixture,
- monkeypatch: pytest.MonkeyPatch,
- func: Callable[[Unix], Iterator[str]],
- expected: str,
-) -> None:
- mocker.patch("platformdirs.unix.getuid", return_value=1000)
- monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
- mocker.patch("os.access", return_value=True)
+def test_iter_dirs_as_non_root_keeps_user_dir(func: Callable[[Unix],
Iterator[str]], expected: str) -> None:
result = list(func(Unix(appname="foo", use_site_for_root=True)))
assert len(result) == 2
assert result[0] != expected
assert result[1] == expected
+
+
[email protected]("_as_root")
[email protected](
+ ("xdg_var", "func"),
+ [
+ ("XDG_CONFIG_DIRS", Unix.iter_config_dirs),
+ ("XDG_DATA_DIRS", Unix.iter_data_dirs),
+ ],
+)
+def test_iter_dirs_as_root_with_multipath_skips_joined_user_dir(
+ monkeypatch: pytest.MonkeyPatch,
+ xdg_var: str,
+ func: Callable[[Unix], Iterator[str]],
+) -> None:
+ monkeypatch.setenv(xdg_var, f"/xdg/a{os.pathsep}/xdg/b")
+ # Under multipath the user dir is the joined string, which no single site
entry matches.
+ assert list(func(Unix(multipath=True, use_site_for_root=True))) ==
["/xdg/a", "/xdg/b"]
+
+
+def test_iter_runtime_dirs_no_duplicate_with_xdg_runtime_dir(monkeypatch:
pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("XDG_RUNTIME_DIR", "/run/user/1000")
+ # $XDG_RUNTIME_DIR backs both the user and the site runtime directory.
+ assert list(Unix(appname="foo").iter_runtime_dirs()) ==
[os.path.join("/run/user/1000", "foo")] # ruff:ignore[os-path-join]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/platformdirs-4.11.3/tests/test_windows.py
new/platformdirs-4.11.7/tests/test_windows.py
--- old/platformdirs-4.11.3/tests/test_windows.py 2020-02-02
01:00:00.000000000 +0100
+++ new/platformdirs-4.11.7/tests/test_windows.py 2020-02-02
01:00:00.000000000 +0100
@@ -436,3 +436,9 @@
monkeypatch.setattr("platformdirs.windows._resolve_win_folder", lambda
csidl: _WIN_FOLDERS[csidl])
monkeypatch.setenv("WIN_PD_OVERRIDE_LOCAL_APPDATA", " X:\\custom ")
assert get_win_folder("CSIDL_LOCAL_APPDATA") == r"X:\custom"
+
+
+def test_windows_iter_runtime_dirs_no_duplicate() -> None:
+ # site_runtime_dir is defined as user_runtime_dir.
+ expected = os.path.join(_LOCAL, "Temp", "bar", "foo") #
ruff:ignore[os-path-join]
+ assert list(Windows(appname="foo", appauthor="bar").iter_runtime_dirs())
== [expected]