Add convenience methods to `PlatformDirsAPI` that allow iterating over both user and site dirs/paths. (#258)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bernát Gábor <gaborjbernat@gmail.com>
diff --git a/CHANGES.rst b/CHANGES.rst
index 1f3d70e..3fa20cc 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,10 @@
 platformdirs Changelog
 ======================
 
+platformdirs 4.1.0 (2024-01-XX)
+-------------------------------
+- Add convenience methods ``iter_{config,cache,data,runtime}_{dirs,paths}``.
+
 platformdirs 4.0.0 (2023-11-10)
 -------------------------------
 - UNIX: revert site_cache_dir to use ``/var/cache`` instead of ``/var/tmp``
diff --git a/src/platformdirs/api.py b/src/platformdirs/api.py
index 31a2bd8..031a38a 100644
--- a/src/platformdirs/api.py
+++ b/src/platformdirs/api.py
@@ -8,7 +8,7 @@
 from typing import TYPE_CHECKING
 
 if TYPE_CHECKING:
-    from typing import Literal
+    from typing import Iterator, Literal
 
 
 class PlatformDirsABC(ABC):
@@ -237,3 +237,43 @@
     def site_runtime_path(self) -> Path:
         """:return: runtime path shared by users"""
         return Path(self.site_runtime_dir)
+
+    def iter_config_dirs(self) -> Iterator[str]:
+        """:yield: all user and site configuration directories."""
+        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 self.user_data_dir
+        yield self.site_data_dir
+
+    def iter_cache_dirs(self) -> Iterator[str]:
+        """:yield: all user and site cache directories."""
+        yield self.user_cache_dir
+        yield self.site_cache_dir
+
+    def iter_runtime_dirs(self) -> Iterator[str]:
+        """:yield: all user and site runtime directories."""
+        yield self.user_runtime_dir
+        yield self.site_runtime_dir
+
+    def iter_config_paths(self) -> Iterator[Path]:
+        """:yield: all user and site configuration paths."""
+        for path in self.iter_config_dirs():
+            yield Path(path)
+
+    def iter_data_paths(self) -> Iterator[Path]:
+        """:yield: all user and site data paths."""
+        for path in self.iter_data_dirs():
+            yield Path(path)
+
+    def iter_cache_paths(self) -> Iterator[Path]:
+        """:yield: all user and site cache paths."""
+        for path in self.iter_cache_dirs():
+            yield Path(path)
+
+    def iter_runtime_paths(self) -> Iterator[Path]:
+        """:yield: all user and site runtime paths."""
+        for path in self.iter_runtime_dirs():
+            yield Path(path)
diff --git a/src/platformdirs/unix.py b/src/platformdirs/unix.py
index f23e412..ca4728e 100644
--- a/src/platformdirs/unix.py
+++ b/src/platformdirs/unix.py
@@ -6,6 +6,7 @@
 import sys
 from configparser import ConfigParser
 from pathlib import Path
+from typing import Iterator
 
 from .api import PlatformDirsABC
 
@@ -44,6 +45,13 @@
         return self._append_app_name_and_version(path)
 
     @property
+    def _site_data_dirs(self) -> list[str]:
+        path = os.environ.get("XDG_DATA_DIRS", "")
+        if not path.strip():
+            path = f"/usr/local/share{os.pathsep}/usr/share"
+        return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)]
+
+    @property
     def site_data_dir(self) -> str:
         """
         :return: data directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>` is
@@ -51,17 +59,10 @@
          OS path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version``
         """
         # XDG default for $XDG_DATA_DIRS; only first, if multipath is False
-        path = os.environ.get("XDG_DATA_DIRS", "")
-        if not path.strip():
-            path = f"/usr/local/share{os.pathsep}/usr/share"
-        return self._with_multi_path(path)
-
-    def _with_multi_path(self, path: str) -> str:
-        path_list = path.split(os.pathsep)
+        dirs = self._site_data_dirs
         if not self.multipath:
-            path_list = path_list[0:1]
-        path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list]  # noqa: PTH111
-        return os.pathsep.join(path_list)
+            return dirs[0]
+        return os.pathsep.join(dirs)
 
     @property
     def user_config_dir(self) -> str:
@@ -75,6 +76,13 @@
         return self._append_app_name_and_version(path)
 
     @property
+    def _site_config_dirs(self) -> list[str]:
+        path = os.environ.get("XDG_CONFIG_DIRS", "")
+        if not path.strip():
+            path = "/etc/xdg"
+        return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)]
+
+    @property
     def site_config_dir(self) -> str:
         """
         :return: config directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>`
@@ -82,10 +90,10 @@
          the OS path separator), e.g. ``/etc/xdg/$appname/$version``
         """
         # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False
-        path = os.environ.get("XDG_CONFIG_DIRS", "")
-        if not path.strip():
-            path = "/etc/xdg"
-        return self._with_multi_path(path)
+        dirs = self._site_config_dirs
+        if not self.multipath:
+            return dirs[0]
+        return os.pathsep.join(dirs)
 
     @property
     def user_cache_dir(self) -> str:
@@ -216,6 +224,16 @@
             directory = directory.split(os.pathsep)[0]
         return Path(directory)
 
+    def iter_config_dirs(self) -> Iterator[str]:
+        """:yield: all user and site configuration directories."""
+        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."""
+        yield self.user_data_dir
+        yield from self._site_data_dirs
+
 
 def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str:
     media_dir = _get_user_dirs_folder(env_var)