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

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

144 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 """ 

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

32 

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

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 """ 

43 :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or 

44 ``$XDG_DATA_HOME/$appname/$version`` 

45 """ 

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

47 

48 @property 

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

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

51 

52 @property 

53 def user_config_dir(self) -> str: 

54 """ 

55 :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or 

56 ``$XDG_CONFIG_HOME/$appname/$version`` 

57 """ 

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

59 

60 @property 

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

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

63 

64 @property 

65 def user_cache_dir(self) -> str: 

66 """ 

67 :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or 

68 ``$XDG_CACHE_HOME/$appname/$version`` 

69 """ 

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

71 

72 @property 

73 def site_cache_dir(self) -> str: 

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

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

76 

77 @property 

78 def user_state_dir(self) -> str: 

79 """ 

80 :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or 

81 ``$XDG_STATE_HOME/$appname/$version`` 

82 """ 

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

84 

85 @property 

86 def site_state_dir(self) -> str: 

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

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

89 

90 @property 

91 def user_log_dir(self) -> str: 

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

93 path = self.user_state_dir 

94 if self.opinion: 

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

96 self._optionally_create_directory(path) 

97 return path 

98 

99 @property 

100 def site_log_dir(self) -> str: 

101 """ 

102 :return: log directory shared by users, e.g. ``/var/log/$appname/$version`` 

103 

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

105 """ 

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

107 

108 @property 

109 def user_documents_dir(self) -> str: 

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

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

112 

113 @property 

114 def user_downloads_dir(self) -> str: 

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

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

117 

118 @property 

119 def user_pictures_dir(self) -> str: 

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

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

122 

123 @property 

124 def user_videos_dir(self) -> str: 

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

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

127 

128 @property 

129 def user_music_dir(self) -> str: 

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

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

132 

133 @property 

134 def user_desktop_dir(self) -> str: 

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

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

137 

138 @property 

139 def user_bin_dir(self) -> str: 

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

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

142 

143 @property 

144 def user_applications_dir(self) -> str: 

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

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

147 

148 @property 

149 def user_runtime_dir(self) -> str: 

150 """ 

151 :return: runtime directory tied to the user, e.g. ``$XDG_RUNTIME_DIR/$appname/$version``. 

152 

153 If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/var/run/user/$(id -u)`` on 

154 FreeBSD/OpenBSD/NetBSD, ``/run/user/$(id -u)`` otherwise). If the default is not writable, 

155 falls back to a temporary directory. 

156 """ 

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

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

159 else: 

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

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

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

163 return self._append_app_name_and_version(path) 

164 

165 @property 

166 def site_runtime_dir(self) -> str: 

167 """ 

168 :return: runtime directory shared by users, e.g. ``/run/$appname/$version`` or \ 

169 ``$XDG_RUNTIME_DIR/$appname/$version``. 

170 

171 Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will 

172 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` 

175 instead. 

176 

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

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 """:return: 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 """:return: 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 """:return: 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 yield self.user_config_dir 

203 yield from self._site_config_dirs 

204 

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

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

207 yield self.user_data_dir 

208 yield from self._site_data_dirs 

209 

210 

211class Unix(XDGMixin, _UnixDefaults): 

212 """ 

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

214 

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

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

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

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

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

220 """ 

221 

222 @property 

223 def user_data_dir(self) -> str: 

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

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

226 

227 @property 

228 def user_config_dir(self) -> str: 

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

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

231 

232 @property 

233 def user_cache_dir(self) -> str: 

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

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

236 

237 @property 

238 def user_state_dir(self) -> str: 

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

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

241 

242 @property 

243 def user_log_dir(self) -> str: 

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

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

246 

247 @property 

248 def user_runtime_dir(self) -> str: 

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

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

251 

252 

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

254 if media_dir := _get_user_dirs_folder(env_var): 

255 return media_dir 

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

257 

258 

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

260 """ 

261 Return directory from user-dirs.dirs config file. 

262 

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

264 

265 """ 

266 user_dirs_config_path = Path(os.path.expanduser("~/.config")) / "user-dirs.dirs" # noqa: PTH111 

267 if user_dirs_config_path.exists(): 

268 parser = ConfigParser() 

269 

270 with user_dirs_config_path.open() as stream: 

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

272 

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

274 return None 

275 

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

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

278 

279 return None 

280 

281 

282__all__ = [ 

283 "Unix", 

284]