Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/platformdirs/_xdg.py: 7%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""XDG environment variable mixin for Unix and macOS."""
3from __future__ import annotations
5import os
6import posixpath
7import sys
8from contextlib import suppress
9from typing import Final
11from .api import PlatformDirsABC
14class XDGMixin(PlatformDirsABC):
15 """Mixin that checks XDG environment variables, falling back to platform-specific defaults via ``super()``."""
17 @property
18 def user_data_dir(self) -> str:
19 """Data directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default."""
20 if path := _xdg_dir("XDG_DATA_HOME"):
21 return self._append_app_name_and_version(path, private=True)
22 return super().user_data_dir
24 @property
25 def _site_data_dirs(self) -> list[str]:
26 if xdg_dirs := _xdg_dir_list("XDG_DATA_DIRS"):
27 return [self._join_app_name_and_version(p) for p in xdg_dirs]
28 return super()._site_data_dirs
30 @property
31 def site_data_dir(self) -> str:
32 """Data directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default."""
33 return self._select_site_dirs(self._site_data_dirs)
35 @property
36 def user_config_dir(self) -> str:
37 """Config directory tied to the user, from ``$XDG_CONFIG_HOME`` if set, else platform default."""
38 if path := _xdg_dir("XDG_CONFIG_HOME"):
39 return self._append_app_name_and_version(path, private=True)
40 return super().user_config_dir
42 @property
43 def _site_config_dirs(self) -> list[str]:
44 if xdg_dirs := _xdg_dir_list("XDG_CONFIG_DIRS"):
45 return [self._join_app_name_and_version(p) for p in xdg_dirs]
46 return super()._site_config_dirs
48 @property
49 def site_config_dir(self) -> str:
50 """Config directories shared by users, from ``$XDG_CONFIG_DIRS`` if set, else platform default."""
51 return self._select_site_dirs(self._site_config_dirs)
53 @property
54 def user_cache_dir(self) -> str:
55 """Cache directory tied to the user, from ``$XDG_CACHE_HOME`` if set, else platform default."""
56 if path := _xdg_dir("XDG_CACHE_HOME"):
57 return self._append_app_name_and_version(path, private=True)
58 return super().user_cache_dir
60 @property
61 def user_state_dir(self) -> str:
62 """State directory tied to the user, from ``$XDG_STATE_HOME`` if set, else platform default."""
63 if path := _xdg_dir("XDG_STATE_HOME"):
64 return self._append_app_name_and_version(path, private=True)
65 return super().user_state_dir
67 @property
68 def user_runtime_dir(self) -> str:
69 """Runtime directory tied to the user, from ``$XDG_RUNTIME_DIR`` if set, else platform default."""
70 if path := _xdg_dir("XDG_RUNTIME_DIR"):
71 return self._append_app_name_and_version(path, private=True)
72 return super().user_runtime_dir
74 @property
75 def site_runtime_dir(self) -> str:
76 """Runtime directory shared by users, from ``$XDG_RUNTIME_DIR`` if set, else platform default."""
77 if path := _xdg_dir("XDG_RUNTIME_DIR"):
78 return self._append_app_name_and_version(path, private=False)
79 return super().site_runtime_dir
81 @property
82 def user_documents_dir(self) -> str:
83 """Documents directory tied to the user, from ``$XDG_DOCUMENTS_DIR`` if set, else platform default."""
84 return self._xdg_media_dir("XDG_DOCUMENTS_DIR") or super().user_documents_dir
86 @property
87 def user_downloads_dir(self) -> str:
88 """Downloads directory tied to the user, from ``$XDG_DOWNLOAD_DIR`` if set, else platform default."""
89 return self._xdg_media_dir("XDG_DOWNLOAD_DIR") or super().user_downloads_dir
91 @property
92 def user_pictures_dir(self) -> str:
93 """Pictures directory tied to the user, from ``$XDG_PICTURES_DIR`` if set, else platform default."""
94 return self._xdg_media_dir("XDG_PICTURES_DIR") or super().user_pictures_dir
96 @property
97 def user_videos_dir(self) -> str:
98 """Videos directory tied to the user, from ``$XDG_VIDEOS_DIR`` if set, else platform default."""
99 return self._xdg_media_dir("XDG_VIDEOS_DIR") or super().user_videos_dir
101 @property
102 def user_music_dir(self) -> str:
103 """Music directory tied to the user, from ``$XDG_MUSIC_DIR`` if set, else platform default."""
104 return self._xdg_media_dir("XDG_MUSIC_DIR") or super().user_music_dir
106 @property
107 def user_desktop_dir(self) -> str:
108 """Desktop directory tied to the user, from ``$XDG_DESKTOP_DIR`` if set, else platform default."""
109 return self._xdg_media_dir("XDG_DESKTOP_DIR") or super().user_desktop_dir
111 @property
112 def user_projects_dir(self) -> str:
113 """Projects directory tied to the user, from ``$XDG_PROJECTS_DIR`` if set, else platform default."""
114 return self._xdg_media_dir("XDG_PROJECTS_DIR") or super().user_projects_dir
116 @property
117 def user_publicshare_dir(self) -> str:
118 """Public share directory tied to the user, from ``$XDG_PUBLICSHARE_DIR`` if set, else platform default."""
119 return self._xdg_media_dir("XDG_PUBLICSHARE_DIR") or super().user_publicshare_dir
121 @property
122 def user_templates_dir(self) -> str:
123 """Templates directory tied to the user, from ``$XDG_TEMPLATES_DIR`` if set, else platform default."""
124 return self._xdg_media_dir("XDG_TEMPLATES_DIR") or super().user_templates_dir
126 def _xdg_media_dir(self, env_var: str) -> str | None:
127 if path := _xdg_dir(env_var):
128 self._optionally_create_media_directory(path)
129 return path
131 @property
132 def user_fonts_dir(self) -> str:
133 """Fonts directory tied to the user, from ``$XDG_DATA_HOME/fonts`` if set, else platform default."""
134 if path := _xdg_dir("XDG_DATA_HOME"):
135 # posixpath keeps the XDG separator even when this mixin runs on a non-POSIX host.
136 return posixpath.join(path, "fonts")
137 return super().user_fonts_dir
139 @property
140 def user_applications_dir(self) -> str:
141 """Applications directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default."""
142 if path := _xdg_dir("XDG_DATA_HOME"):
143 return os.path.join(os.path.expanduser(path), "applications") # ruff:ignore[os-path-expanduser, os-path-join]
144 return super().user_applications_dir
146 @property
147 def _site_applications_dirs(self) -> list[str]:
148 if xdg_dirs := _xdg_dir_list("XDG_DATA_DIRS"):
149 return [os.path.join(p, "applications") for p in xdg_dirs] # ruff:ignore[os-path-join]
150 return super()._site_applications_dirs
152 @property
153 def site_applications_dir(self) -> str:
154 """Applications directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default."""
155 dirs = self._site_applications_dirs
156 return os.pathsep.join(dirs) if self.multipath else dirs[0]
159def _xdg_dir(env_var: str) -> str | None:
160 path: Final = os.environ.get(env_var, "").strip()
161 return path if posixpath.isabs(path) else None
164def _xdg_dir_list(env_var: str) -> list[str]:
165 return [
166 stripped for path in os.environ.get(env_var, "").split(os.pathsep) if posixpath.isabs(stripped := path.strip())
167 ]
170def _expand_user(path: str) -> str:
171 """Expand the leading ``~`` of ``path`` like :func:`os.path.expanduser`, treating an empty ``HOME`` as unset on POSIX.
173 :raises RuntimeError: if the home directory cannot be resolved.
175 """
176 if sys.platform != "win32" and "HOME" in os.environ and not os.environ["HOME"]:
177 # expanduser roots the path at / for an empty HOME; Rust's std::env::home_dir treats it as unset too
178 expanded = path
179 with suppress(ImportError, KeyError):
180 import pwd # ruff:ignore[import-outside-top-level] # missing on WASI
182 expanded = pwd.getpwuid(os.getuid()).pw_dir.rstrip("/") + path[1:] or "/"
183 else:
184 expanded = os.path.expanduser(path) # ruff:ignore[os-path-expanduser] # str(Path) rewrites separators on Windows
185 if expanded.startswith("~"):
186 msg = f"could not determine the home directory for {path!r}, set HOME or an absolute XDG variable"
187 raise RuntimeError(msg)
188 return expanded
191__all__ = [
192 "XDGMixin",
193 "_expand_user",
194 "_xdg_dir",
195]