diff --git a/CHANGES.rst b/CHANGES.rst index 1e2cae5f..761d251b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,6 +12,9 @@ platformdirs 2.1.0 :class:`PlatformDirsABC ` and it's implementations: :class:`Android `, :class:`MacOS `, :class:`Unix ` and :class:`Windows ` +- Add ``*_path`` API, returning :class:`pathlib.Path ` objects instead of :class:`str` + (``user_data_path``, ``user_config_path``, ``user_cache_path``, ``user_state_path``, ``user_log_path``, + ``site_data_path``, ``site_config_path``) - by `@papr `_ platformdirs 2.0.2 ------------------ diff --git a/docs/api.rst b/docs/api.rst index 536544c8..92e9c1e5 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -10,26 +10,31 @@ User data directory ------------------- .. autofunction:: platformdirs.user_data_dir +.. autofunction:: platformdirs.user_data_path User config directory --------------------- .. autofunction:: platformdirs.user_config_dir +.. autofunction:: platformdirs.user_config_path Cache directory ------------------- .. autofunction:: platformdirs.user_cache_dir +.. autofunction:: platformdirs.user_cache_path State directory ------------------- .. autofunction:: platformdirs.user_state_dir +.. autofunction:: platformdirs.user_state_path Logs directory ------------------- .. autofunction:: platformdirs.user_log_dir +.. autofunction:: platformdirs.user_log_path Shared directories ~~~~~~~~~~~~~~~~~~ @@ -40,11 +45,13 @@ Shared data directory --------------------- .. autofunction:: platformdirs.site_data_dir +.. autofunction:: platformdirs.site_data_path Shared config directory ----------------------- .. autofunction:: platformdirs.site_config_dir +.. autofunction:: platformdirs.site_config_path Platforms ~~~~~~~~~ diff --git a/src/platformdirs/__init__.py b/src/platformdirs/__init__.py index b2a265ed..f2f6abc9 100644 --- a/src/platformdirs/__init__.py +++ b/src/platformdirs/__init__.py @@ -5,6 +5,7 @@ import importlib import os import sys +from pathlib import Path from typing import TYPE_CHECKING, Optional, Type, Union if TYPE_CHECKING: @@ -143,6 +144,118 @@ def user_log_dir( return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_dir +def user_data_path( + appname: Optional[str] = None, + appauthor: Union[str, None, "Literal[False]"] = None, + version: Optional[str] = None, + roaming: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: data path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_path + + +def site_data_path( + appname: Optional[str] = None, + appauthor: Union[str, None, "Literal[False]"] = None, + version: Optional[str] = None, + multipath: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `multipath `. + :returns: data path shared by users + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_path + + +def user_config_path( + appname: Optional[str] = None, + appauthor: Union[str, None, "Literal[False]"] = None, + version: Optional[str] = None, + roaming: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: config path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_path + + +def site_config_path( + appname: Optional[str] = None, + appauthor: Union[str, None, "Literal[False]"] = None, + version: Optional[str] = None, + multipath: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :returns: config path shared by the users + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_path + + +def user_cache_path( + appname: Optional[str] = None, + appauthor: Union[str, None, "Literal[False]"] = None, + version: Optional[str] = None, + opinion: bool = True, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :returns: cache path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_path + + +def user_state_path( + appname: Optional[str] = None, + appauthor: Union[str, None, "Literal[False]"] = None, + version: Optional[str] = None, + roaming: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: state path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_path + + +def user_log_path( + appname: Optional[str] = None, + appauthor: Union[str, None, "Literal[False]"] = None, + version: Optional[str] = None, + opinion: bool = True, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :returns: log path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_path + + __all__ = [ "__version__", "__version_info__", @@ -156,4 +269,11 @@ def user_log_dir( "user_log_dir", "site_data_dir", "site_config_dir", + "user_data_path", + "user_config_path", + "user_cache_path", + "user_state_path", + "user_log_path", + "site_data_path", + "site_config_path", ] diff --git a/src/platformdirs/api.py b/src/platformdirs/api.py index 159a7fc2..7b291734 100644 --- a/src/platformdirs/api.py +++ b/src/platformdirs/api.py @@ -1,6 +1,7 @@ import os import sys from abc import ABC, abstractmethod +from pathlib import Path from typing import Optional, Union if sys.version_info >= (3, 8): # pragma: no branch @@ -97,3 +98,38 @@ def user_state_dir(self) -> str: @abstractmethod def user_log_dir(self) -> str: """:return: log directory tied to the user""" + + @property + def user_data_path(self) -> Path: + """:return: data path tied to the user""" + return Path(self.user_data_dir) + + @property + def site_data_path(self) -> Path: + """:return: data path shared by users""" + return Path(self.site_data_dir) + + @property + def user_config_path(self) -> Path: + """:return: config path tied to the user""" + return Path(self.user_config_dir) + + @property + def site_config_path(self) -> Path: + """:return: config path shared by the users""" + return Path(self.site_config_dir) + + @property + def user_cache_path(self) -> Path: + """:return: cache path tied to the user""" + return Path(self.user_cache_dir) + + @property + def user_state_path(self) -> Path: + """:return: state path tied to the user""" + return Path(self.user_state_dir) + + @property + def user_log_path(self) -> Path: + """:return: log path tied to the user""" + return Path(self.user_log_dir) diff --git a/src/platformdirs/unix.py b/src/platformdirs/unix.py index 2dde7493..02beca29 100644 --- a/src/platformdirs/unix.py +++ b/src/platformdirs/unix.py @@ -1,4 +1,5 @@ import os +from pathlib import Path from .api import PlatformDirsABC @@ -66,7 +67,6 @@ def site_config_dir(self) -> str: :return: config directories shared by users (if `multipath ` is enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS path separator), e.g. ``/etc/xdg/$appname/$version`` - :return: """ # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False if "XDG_CONFIG_DIRS" in os.environ: @@ -109,6 +109,22 @@ def user_log_dir(self) -> str: path = os.path.join(path, "log") return path + @property + def site_data_path(self) -> Path: + """:return: data path shared by users. Only return first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_data_dir) + + @property + def site_config_path(self) -> Path: + """:return: config path shared by the users. Only return first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_config_dir) + + def _first_item_as_path_if_multipath(self, directory: str) -> Path: + if self.multipath: + # If multipath is True, the first path is returned. + directory = directory.split(os.pathsep)[0] + return Path(directory) + __all__ = [ "Unix", diff --git a/tests/conftest.py b/tests/conftest.py index 8392fe87..d3e9eae6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,6 +19,13 @@ def func(request: SubRequest) -> str: return cast(str, request.param) +@pytest.fixture(params=PROPS) +def func_path(request: SubRequest) -> str: + prop = cast(str, request.param) + prop = prop.replace("_dir", "_path") + return prop + + @pytest.fixture() def props() -> Tuple[str, ...]: return PROPS diff --git a/tests/test_api.py b/tests/test_api.py index 665a9576..3eab8543 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,3 +1,5 @@ +import inspect +from pathlib import Path from typing import Optional import pytest @@ -24,6 +26,28 @@ def test_property_result_is_str(func: str) -> None: assert isinstance(result, str) +def test_method_result_is_path(func_path: str) -> None: + method = getattr(platformdirs, func_path) + result = method("MyApp", "MyCompany") + assert isinstance(result, Path) + + +def test_property_result_is_path(func_path: str) -> None: + dirs = platformdirs.PlatformDirs("MyApp", "MyCompany", version="1.0") + result = getattr(dirs, func_path) + assert isinstance(result, Path) + + +def test_function_interface_is_in_sync(func: str) -> None: + function_dir = getattr(platformdirs, func) + function_path = getattr(platformdirs, func.replace("_dir", "_path")) + assert inspect.isfunction(function_dir) + assert inspect.isfunction(function_path) + function_dir_signature = inspect.Signature.from_callable(function_dir) + function_path_signature = inspect.Signature.from_callable(function_path) + assert function_dir_signature.parameters == function_path_signature.parameters + + @pytest.mark.parametrize("root", ["A", "/system", None]) @pytest.mark.parametrize("data", ["D", "/data", None]) def test_android_active(monkeypatch: MonkeyPatch, root: Optional[str], data: Optional[str]) -> None: diff --git a/whitelist.txt b/whitelist.txt index df3a104d..7428da1c 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -14,6 +14,7 @@ highbit HKEY impl intersphinx +isfunction jnius kernel32 lru @@ -23,6 +24,7 @@ normpath ord param params +pathlib pathsep platformdirs pyjnius