diff --git a/docs/api.rst b/docs/api.rst index 8e2cb17..7f7a765 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -101,6 +101,12 @@ Shared cache directory .. autofunction:: platformdirs.site_cache_dir .. autofunction:: platformdirs.site_cache_path +Shared state directory +---------------------- + +.. autofunction:: platformdirs.site_state_dir +.. autofunction:: platformdirs.site_state_path + Shared log directory -------------------- diff --git a/docs/platforms.rst b/docs/platforms.rst index 88433b0..77b19d5 100644 --- a/docs/platforms.rst +++ b/docs/platforms.rst @@ -84,6 +84,21 @@ Default paths * - Android - ``/data/data//cache/SuperApp/log`` +``site_state_dir`` +~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :widths: 20 80 + + * - Linux + - ``/var/lib/SuperApp`` + * - macOS + - ``/Library/Application Support/SuperApp`` + * - Windows + - ``C:\ProgramData\Acme\SuperApp`` + * - Android + - ``/data/data//files/SuperApp`` + ``site_log_dir`` ~~~~~~~~~~~~~~~~ diff --git a/src/platformdirs/__init__.py b/src/platformdirs/__init__.py index df3fe45..39a4481 100644 --- a/src/platformdirs/__init__.py +++ b/src/platformdirs/__init__.py @@ -221,6 +221,27 @@ def user_state_dir( ).user_state_dir +def site_state_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param ensure_exists: See `ensure_exists `. + :returns: state directory shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + ensure_exists=ensure_exists, + ).site_state_dir + + def user_log_dir( appname: str | None = None, appauthor: str | Literal[False] | None = None, @@ -515,6 +536,27 @@ def user_state_path( ).user_state_path +def site_state_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param ensure_exists: See `ensure_exists `. + :returns: state path shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + ensure_exists=ensure_exists, + ).site_state_path + + def user_log_path( appname: str | None = None, appauthor: str | Literal[False] | None = None, @@ -657,6 +699,8 @@ def site_runtime_path( "site_log_path", "site_runtime_dir", "site_runtime_path", + "site_state_dir", + "site_state_path", "user_cache_dir", "user_cache_path", "user_config_dir", diff --git a/src/platformdirs/__main__.py b/src/platformdirs/__main__.py index 0f4aa1f..8196568 100644 --- a/src/platformdirs/__main__.py +++ b/src/platformdirs/__main__.py @@ -19,6 +19,7 @@ "site_data_dir", "site_config_dir", "site_cache_dir", + "site_state_dir", "site_log_dir", "site_runtime_dir", ) diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py index 532de0b..8bf5e59 100644 --- a/src/platformdirs/android.py +++ b/src/platformdirs/android.py @@ -62,6 +62,11 @@ def user_state_dir(self) -> str: """:return: state directory tied to the user, same as `user_data_dir`""" return self.user_data_dir + @property + def site_state_dir(self) -> str: + """:return: state directory shared by users, same as `user_state_dir`""" + return self.user_state_dir + @property def user_log_dir(self) -> str: """ diff --git a/src/platformdirs/api.py b/src/platformdirs/api.py index 22d0880..aa2e84f 100644 --- a/src/platformdirs/api.py +++ b/src/platformdirs/api.py @@ -149,6 +149,11 @@ def site_cache_dir(self) -> str: def user_state_dir(self) -> str: """:return: state directory tied to the user""" + @property + @abstractmethod + def site_state_dir(self) -> str: + """:return: state directory shared by users""" + @property @abstractmethod def user_log_dir(self) -> str: @@ -234,6 +239,11 @@ def user_state_path(self) -> Path: """:return: state path tied to the user""" return Path(self.user_state_dir) + @property + def site_state_path(self) -> Path: + """:return: state path shared by users""" + return Path(self.site_state_dir) + @property def user_log_path(self) -> Path: """:return: log path tied to the user""" @@ -299,6 +309,11 @@ 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 self.user_state_dir + yield self.site_state_dir + def iter_log_dirs(self) -> Iterator[str]: """:yield: all user and site log directories.""" yield self.user_log_dir @@ -324,6 +339,11 @@ def iter_cache_paths(self) -> Iterator[Path]: for path in self.iter_cache_dirs(): yield Path(path) + def iter_state_paths(self) -> Iterator[Path]: + """:yield: all user and site state paths.""" + for path in self.iter_state_dirs(): + yield Path(path) + def iter_log_paths(self) -> Iterator[Path]: """:yield: all user and site log paths.""" for path in self.iter_log_dirs(): diff --git a/src/platformdirs/macos.py b/src/platformdirs/macos.py index 44cfcbe..7a97641 100644 --- a/src/platformdirs/macos.py +++ b/src/platformdirs/macos.py @@ -82,6 +82,11 @@ def user_state_dir(self) -> str: """:return: state directory tied to the user, same as `user_data_dir`""" return self.user_data_dir + @property + def site_state_dir(self) -> str: + """:return: state directory shared by users, same as `site_data_dir`""" + return self.site_data_dir + @property def user_log_dir(self) -> str: """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``""" diff --git a/src/platformdirs/unix.py b/src/platformdirs/unix.py index 806f230..efda3d2 100644 --- a/src/platformdirs/unix.py +++ b/src/platformdirs/unix.py @@ -25,7 +25,7 @@ def getuid() -> NoReturn: from os import getuid -class _UnixDefaults(PlatformDirsABC): +class _UnixDefaults(PlatformDirsABC): # noqa: PLR0904 """ Default directories for Unix/Linux without XDG environment variable overrides. @@ -77,6 +77,11 @@ def user_state_dir(self) -> str: """ return self._append_app_name_and_version(os.path.expanduser("~/.local/state")) # noqa: PTH111 + @property + def site_state_dir(self) -> str: + """:return: state directory shared by users, e.g. ``/var/lib/$appname/$version``""" + return self._append_app_name_and_version("/var/lib") + @property def user_log_dir(self) -> str: """:return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it""" diff --git a/src/platformdirs/windows.py b/src/platformdirs/windows.py index f6c67c7..c3d17d5 100644 --- a/src/platformdirs/windows.py +++ b/src/platformdirs/windows.py @@ -89,6 +89,11 @@ def user_state_dir(self) -> str: """:return: state directory tied to the user, same as `user_data_dir`""" return self.user_data_dir + @property + def site_state_dir(self) -> str: + """:return: state directory shared by users, same as `site_data_dir`""" + return self.site_data_dir + @property def user_log_dir(self) -> str: """:return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it""" diff --git a/tests/conftest.py b/tests/conftest.py index df9f349..23feb2c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,6 +22,7 @@ "site_data_dir", "site_config_dir", "site_cache_dir", + "site_state_dir", "site_log_dir", "site_runtime_dir", ) diff --git a/tests/test_android.py b/tests/test_android.py index 8995bca..b14eb04 100644 --- a/tests/test_android.py +++ b/tests/test_android.py @@ -52,6 +52,7 @@ def test_android(mocker: MockerFixture, params: dict[str, Any], func: str) -> No "user_cache_dir": f"/data/data/com.example/cache{suffix}", "site_cache_dir": f"/data/data/com.example/cache{suffix}", "user_state_dir": f"/data/data/com.example/files{suffix}", + "site_state_dir": f"/data/data/com.example/files{suffix}", "user_log_dir": f"/data/data/com.example/cache{suffix}{'' if params.get('opinion', True) is False else '/log'}", "site_log_dir": f"/data/data/com.example/cache{suffix}{'' if params.get('opinion', True) is False else '/log'}", "user_documents_dir": "/storage/emulated/0/Documents", diff --git a/tests/test_macos.py b/tests/test_macos.py index 57a5431..8c2620f 100644 --- a/tests/test_macos.py +++ b/tests/test_macos.py @@ -74,6 +74,7 @@ def test_macos(mocker: MockerFixture, params: dict[str, Any], func: str) -> None "user_cache_dir": f"{home}/Library/Caches{suffix}", "site_cache_dir": f"/Library/Caches{suffix}", "user_state_dir": f"{home}/Library/Application Support{suffix}", + "site_state_dir": f"/Library/Application Support{suffix}", "user_log_dir": f"{home}/Library/Logs{suffix}", "site_log_dir": f"/Library/Logs{suffix}", "user_documents_dir": f"{home}/Documents", diff --git a/tests/test_unix.py b/tests/test_unix.py index f1cbada..09a349b 100644 --- a/tests/test_unix.py +++ b/tests/test_unix.py @@ -106,6 +106,7 @@ def _func_to_path(func: str) -> XDGVariable | None: "user_log_dir": XDGVariable("XDG_STATE_HOME", "~/.local/state"), "user_runtime_dir": XDGVariable("XDG_RUNTIME_DIR", f"{gettempdir()}/runtime-1234"), "site_log_dir": None, + "site_state_dir": None, "site_runtime_dir": XDGVariable("XDG_RUNTIME_DIR", "/run"), } return mapping.get(func) @@ -160,6 +161,11 @@ def test_site_log_dir_fixed_path(opinion: bool) -> None: assert result == os.path.join("/var/log", "foo") # noqa: PTH118 +def test_site_state_dir_fixed_path() -> None: + result = Unix(appname="foo").site_state_dir + assert result == os.path.join("/var/lib", "foo") # noqa: PTH118 + + @pytest.mark.usefixtures("_getuid") @pytest.mark.parametrize("platform", ["freebsd", "openbsd", "netbsd"]) def test_platform_on_bsd(monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture, platform: str) -> None: diff --git a/tests/test_windows.py b/tests/test_windows.py index 9fdf06e..a9fc14a 100644 --- a/tests/test_windows.py +++ b/tests/test_windows.py @@ -78,6 +78,7 @@ def test_windows(params: dict[str, Any], func: str) -> None: "user_cache_dir": cache_local, "site_cache_dir": cache_common, "user_state_dir": local, + "site_state_dir": common, "user_log_dir": log, "site_log_dir": log_common, "user_documents_dir": os.path.normpath(_WIN_FOLDERS["CSIDL_PERSONAL"]),