Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/platformdirs/unix.py: 55%

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

165 statements  

1"""Unix.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import sys 

7from configparser import ConfigParser 

8from functools import cached_property 

9from pathlib import Path 

10from tempfile import gettempdir 

11from typing import TYPE_CHECKING, NoReturn 

12 

13from ._xdg import XDGMixin 

14from .api import PlatformDirsABC 

15 

16if TYPE_CHECKING: 

17 from collections.abc import Iterator 

18 

19if sys.platform == "win32": 

20 

21 def getuid() -> NoReturn: 

22 msg = "should only be used on Unix" 

23 raise RuntimeError(msg) 

24 

25else: 

26 from os import getuid 

27 

28 

29class _UnixDefaults(PlatformDirsABC): # noqa: PLR0904 

30 """Default directories for Unix/Linux without XDG environment variable overrides. 

31 

32 The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`. 

33 

34 """ 

35 

36 @cached_property 

37 def _use_site(self) -> bool: 

38 return self.use_site_for_root and getuid() == 0 

39 

40 @property 

41 def user_data_dir(self) -> str: 

42 """:returns: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or ``$XDG_DATA_HOME/$appname/$version``""" 

43 return self._append_app_name_and_version(os.path.expanduser("~/.local/share")) # noqa: PTH111 

44 

45 @property 

46 def _site_data_dirs(self) -> list[str]: 

47 return [self._append_app_name_and_version("/usr/local/share"), self._append_app_name_and_version("/usr/share")] 

48 

49 @property 

50 def user_config_dir(self) -> str: 

51 """:returns: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or ``$XDG_CONFIG_HOME/$appname/$version``""" 

52 return self._append_app_name_and_version(os.path.expanduser("~/.config")) # noqa: PTH111 

53 

54 @property 

55 def _site_config_dirs(self) -> list[str]: 

56 return [self._append_app_name_and_version("/etc/xdg")] 

57 

58 @property 

59 def user_cache_dir(self) -> str: 

60 """:returns: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or ``$XDG_CACHE_HOME/$appname/$version``""" 

61 return self._append_app_name_and_version(os.path.expanduser("~/.cache")) # noqa: PTH111 

62 

63 @property 

64 def site_cache_dir(self) -> str: 

65 """:returns: cache directory shared by users, e.g. ``/var/cache/$appname/$version``""" 

66 return self._append_app_name_and_version("/var/cache") 

67 

68 @property 

69 def user_state_dir(self) -> str: 

70 """:returns: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or ``$XDG_STATE_HOME/$appname/$version``""" 

71 return self._append_app_name_and_version(os.path.expanduser("~/.local/state")) # noqa: PTH111 

72 

73 @property 

74 def site_state_dir(self) -> str: 

75 """:returns: state directory shared by users, e.g. ``/var/lib/$appname/$version``""" 

76 return self._append_app_name_and_version("/var/lib") 

77 

78 @property 

79 def user_log_dir(self) -> str: 

80 """:returns: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it""" 

81 path = self.user_state_dir 

82 if self.opinion: 

83 path = os.path.join(path, "log") # noqa: PTH118 

84 self._optionally_create_directory(path) 

85 return path 

86 

87 @property 

88 def site_log_dir(self) -> str: 

89 """:returns: log directory shared by users, e.g. ``/var/log/$appname/$version`` 

90 

91 Unlike `user_log_dir`, ``opinion`` has no effect since ``/var/log`` is inherently a log directory. 

92 

93 """ 

94 return self._append_app_name_and_version("/var/log") 

95 

96 @property 

97 def user_documents_dir(self) -> str: 

98 """:returns: documents directory tied to the user, e.g. ``~/Documents``""" 

99 return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents") 

100 

101 @property 

102 def user_downloads_dir(self) -> str: 

103 """:returns: downloads directory tied to the user, e.g. ``~/Downloads``""" 

104 return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads") 

105 

106 @property 

107 def user_pictures_dir(self) -> str: 

108 """:returns: pictures directory tied to the user, e.g. ``~/Pictures``""" 

109 return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures") 

110 

111 @property 

112 def user_videos_dir(self) -> str: 

113 """:returns: videos directory tied to the user, e.g. ``~/Videos``""" 

114 return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos") 

115 

116 @property 

117 def user_music_dir(self) -> str: 

118 """:returns: music directory tied to the user, e.g. ``~/Music``""" 

119 return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music") 

120 

121 @property 

122 def user_desktop_dir(self) -> str: 

123 """:returns: desktop directory tied to the user, e.g. ``~/Desktop``""" 

124 return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop") 

125 

126 @property 

127 def user_bin_dir(self) -> str: 

128 """:returns: bin directory tied to the user, e.g. ``~/.local/bin``""" 

129 return os.path.expanduser("~/.local/bin") # noqa: PTH111 

130 

131 @property 

132 def site_bin_dir(self) -> str: 

133 """:returns: bin directory shared by users, e.g. ``/usr/local/bin``""" 

134 return "/usr/local/bin" 

135 

136 @property 

137 def user_applications_dir(self) -> str: 

138 """:returns: applications directory tied to the user, e.g. ``~/.local/share/applications``""" 

139 return os.path.join(os.path.expanduser("~/.local/share"), "applications") # noqa: PTH111, PTH118 

140 

141 @property 

142 def _site_applications_dirs(self) -> list[str]: 

143 return [os.path.join(p, "applications") for p in ["/usr/local/share", "/usr/share"]] # noqa: PTH118 

144 

145 @property 

146 def site_applications_dir(self) -> str: 

147 """:returns: applications directory shared by users, e.g. ``/usr/share/applications``""" 

148 dirs = self._site_applications_dirs 

149 return os.pathsep.join(dirs) if self.multipath else dirs[0] 

150 

151 @property 

152 def user_runtime_dir(self) -> str: 

153 """:returns: runtime directory tied to the user, e.g. ``$XDG_RUNTIME_DIR/$appname/$version``. 

154 

155 If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/tmp/run/user/$(id -u)`` on OpenBSD, ``/var/run/user/$(id -u)`` on FreeBSD/NetBSD, ``/run/user/$(id -u)`` otherwise). If the default is not writable, falls back to a temporary directory. 

156 

157 """ 

158 if sys.platform.startswith("openbsd"): 

159 path = f"/tmp/run/user/{getuid()}" # noqa: S108 

160 elif sys.platform.startswith(("freebsd", "netbsd")): 

161 path = f"/var/run/user/{getuid()}" 

162 else: 

163 path = f"/run/user/{getuid()}" 

164 if not os.access(path, os.W_OK): 

165 path = f"{gettempdir()}/runtime-{getuid()}" 

166 return self._append_app_name_and_version(path) 

167 

168 @property 

169 def site_runtime_dir(self) -> str: 

170 """:returns: runtime directory shared by users, e.g. ``/run/$appname/$version`` or ``$XDG_RUNTIME_DIR/$appname/$version``. 

171 

172 Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will fall back to paths associated to the root user instead of a regular logged-in user if it's not set. 

173 

174 If you wish to ensure that a logged-in root user path is returned e.g. ``/run/user/0``, use `user_runtime_dir` instead. 

175 

176 For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set. 

177 

178 """ 

179 if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): 

180 path = "/var/run" 

181 else: 

182 path = "/run" 

183 return self._append_app_name_and_version(path) 

184 

185 @property 

186 def site_data_path(self) -> Path: 

187 """:returns: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" 

188 return self._first_item_as_path_if_multipath(self.site_data_dir) 

189 

190 @property 

191 def site_config_path(self) -> Path: 

192 """:returns: config path shared by users, returns the first item, even if ``multipath`` is set to ``True``""" 

193 return self._first_item_as_path_if_multipath(self.site_config_dir) 

194 

195 @property 

196 def site_cache_path(self) -> Path: 

197 """:returns: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" 

198 return self._first_item_as_path_if_multipath(self.site_cache_dir) 

199 

200 def iter_config_dirs(self) -> Iterator[str]: 

201 """:yield: all user and site configuration directories.""" 

202 if not self._use_site: 

203 yield self.user_config_dir 

204 yield from self._site_config_dirs 

205 

206 def iter_data_dirs(self) -> Iterator[str]: 

207 """:yield: all user and site data directories.""" 

208 if not self._use_site: 

209 yield self.user_data_dir 

210 yield from self._site_data_dirs 

211 

212 

213class Unix(XDGMixin, _UnixDefaults): 

214 """On Unix/Linux, we follow the `XDG Basedir Spec <https://specifications.freedesktop.org/basedir/latest/>`_. 

215 

216 The spec allows overriding directories with environment variables. The examples shown are the default values, 

217 alongside the name of the environment variable that overrides them. Makes use of the `appname 

218 <platformdirs.api.PlatformDirsABC.appname>`, `version <platformdirs.api.PlatformDirsABC.version>`, `multipath 

219 <platformdirs.api.PlatformDirsABC.multipath>`, `opinion <platformdirs.api.PlatformDirsABC.opinion>`, `ensure_exists 

220 <platformdirs.api.PlatformDirsABC.ensure_exists>`. 

221 

222 """ 

223 

224 @property 

225 def user_data_dir(self) -> str: 

226 """:returns: data directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

227 return self.site_data_dir if self._use_site else super().user_data_dir 

228 

229 @property 

230 def user_config_dir(self) -> str: 

231 """:returns: config directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

232 return self.site_config_dir if self._use_site else super().user_config_dir 

233 

234 @property 

235 def user_cache_dir(self) -> str: 

236 """:returns: cache directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

237 return self.site_cache_dir if self._use_site else super().user_cache_dir 

238 

239 @property 

240 def user_state_dir(self) -> str: 

241 """:returns: state directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

242 return self.site_state_dir if self._use_site else super().user_state_dir 

243 

244 @property 

245 def user_log_dir(self) -> str: 

246 """:returns: log directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

247 return self.site_log_dir if self._use_site else super().user_log_dir 

248 

249 @property 

250 def user_applications_dir(self) -> str: 

251 """:returns: applications directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

252 return self.site_applications_dir if self._use_site else super().user_applications_dir 

253 

254 @property 

255 def user_runtime_dir(self) -> str: 

256 """:returns: runtime directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

257 return self.site_runtime_dir if self._use_site else super().user_runtime_dir 

258 

259 @property 

260 def user_bin_dir(self) -> str: 

261 """:returns: bin directory tied to the user, or site equivalent when root with ``use_site_for_root``""" 

262 return self.site_bin_dir if self._use_site else super().user_bin_dir 

263 

264 

265def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str: 

266 if media_dir := _get_user_dirs_folder(env_var): 

267 return media_dir 

268 return os.path.expanduser(fallback_tilde_path) # noqa: PTH111 

269 

270 

271def _get_user_dirs_folder(key: str) -> str | None: 

272 """Return directory from user-dirs.dirs config file. 

273 

274 See https://freedesktop.org/wiki/Software/xdg-user-dirs/. 

275 

276 """ 

277 config_home = os.environ.get("XDG_CONFIG_HOME", "").strip() or os.path.expanduser("~/.config") # noqa: PTH111 

278 user_dirs_config_path = Path(config_home) / "user-dirs.dirs" 

279 if user_dirs_config_path.exists(): 

280 parser = ConfigParser() 

281 

282 with user_dirs_config_path.open() as stream: 

283 parser.read_string(f"[top]\n{stream.read()}") 

284 

285 if key not in parser["top"]: 

286 return None 

287 

288 path = parser["top"][key].strip('"') 

289 return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111 

290 

291 return None 

292 

293 

294__all__ = [ 

295 "Unix", 

296]