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

132 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:08 +0000

1"""Unix.""" 

2from __future__ import annotations 

3 

4import os 

5import sys 

6from configparser import ConfigParser 

7from pathlib import Path 

8 

9from .api import PlatformDirsABC 

10 

11if sys.platform == "win32": 

12 

13 def getuid() -> int: 

14 msg = "should only be used on Unix" 

15 raise RuntimeError(msg) 

16 

17else: 

18 from os import getuid 

19 

20 

21class Unix(PlatformDirsABC): 

22 """ 

23 On Unix/Linux, we follow the 

24 `XDG Basedir Spec <https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_. The spec allows 

25 overriding directories with environment variables. The examples show are the default values, alongside the name of 

26 the environment variable that overrides them. Makes use of the 

27 `appname <platformdirs.api.PlatformDirsABC.appname>`, 

28 `version <platformdirs.api.PlatformDirsABC.version>`, 

29 `multipath <platformdirs.api.PlatformDirsABC.multipath>`, 

30 `opinion <platformdirs.api.PlatformDirsABC.opinion>`, 

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

32 """ 

33 

34 @property 

35 def user_data_dir(self) -> str: 

36 """ 

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

38 ``$XDG_DATA_HOME/$appname/$version`` 

39 """ 

40 path = os.environ.get("XDG_DATA_HOME", "") 

41 if not path.strip(): 

42 path = os.path.expanduser("~/.local/share") # noqa: PTH111 

43 return self._append_app_name_and_version(path) 

44 

45 @property 

46 def site_data_dir(self) -> str: 

47 """ 

48 :return: data directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>` is 

49 enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS 

50 path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version`` 

51 """ 

52 # XDG default for $XDG_DATA_DIRS; only first, if multipath is False 

53 path = os.environ.get("XDG_DATA_DIRS", "") 

54 if not path.strip(): 

55 path = f"/usr/local/share{os.pathsep}/usr/share" 

56 return self._with_multi_path(path) 

57 

58 def _with_multi_path(self, path: str) -> str: 

59 path_list = path.split(os.pathsep) 

60 if not self.multipath: 

61 path_list = path_list[0:1] 

62 path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list] # noqa: PTH111 

63 return os.pathsep.join(path_list) 

64 

65 @property 

66 def user_config_dir(self) -> str: 

67 """ 

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

69 ``$XDG_CONFIG_HOME/$appname/$version`` 

70 """ 

71 path = os.environ.get("XDG_CONFIG_HOME", "") 

72 if not path.strip(): 

73 path = os.path.expanduser("~/.config") # noqa: PTH111 

74 return self._append_app_name_and_version(path) 

75 

76 @property 

77 def site_config_dir(self) -> str: 

78 """ 

79 :return: config directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>` 

80 is enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS 

81 path separator), e.g. ``/etc/xdg/$appname/$version`` 

82 """ 

83 # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False 

84 path = os.environ.get("XDG_CONFIG_DIRS", "") 

85 if not path.strip(): 

86 path = "/etc/xdg" 

87 return self._with_multi_path(path) 

88 

89 @property 

90 def user_cache_dir(self) -> str: 

91 """ 

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

93 ``~/$XDG_CACHE_HOME/$appname/$version`` 

94 """ 

95 path = os.environ.get("XDG_CACHE_HOME", "") 

96 if not path.strip(): 

97 path = os.path.expanduser("~/.cache") # noqa: PTH111 

98 return self._append_app_name_and_version(path) 

99 

100 @property 

101 def site_cache_dir(self) -> str: 

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

103 return self._append_app_name_and_version("/var/tmp") # noqa: S108 

104 

105 @property 

106 def user_state_dir(self) -> str: 

107 """ 

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

109 ``$XDG_STATE_HOME/$appname/$version`` 

110 """ 

111 path = os.environ.get("XDG_STATE_HOME", "") 

112 if not path.strip(): 

113 path = os.path.expanduser("~/.local/state") # noqa: PTH111 

114 return self._append_app_name_and_version(path) 

115 

116 @property 

117 def user_log_dir(self) -> str: 

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

119 path = self.user_state_dir 

120 if self.opinion: 

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

122 self._optionally_create_directory(path) 

123 return path 

124 

125 @property 

126 def user_documents_dir(self) -> str: 

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

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

129 

130 @property 

131 def user_downloads_dir(self) -> str: 

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

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

134 

135 @property 

136 def user_pictures_dir(self) -> str: 

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

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

139 

140 @property 

141 def user_videos_dir(self) -> str: 

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

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

144 

145 @property 

146 def user_music_dir(self) -> str: 

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

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

149 

150 @property 

151 def user_desktop_dir(self) -> str: 

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

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

154 

155 @property 

156 def user_runtime_dir(self) -> str: 

157 """ 

158 :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or 

159 ``$XDG_RUNTIME_DIR/$appname/$version``. 

160 

161 For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if 

162 exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR`` 

163 is not set. 

164 """ 

165 path = os.environ.get("XDG_RUNTIME_DIR", "") 

166 if not path.strip(): 

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

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

169 if not Path(path).exists(): 

170 path = f"/tmp/runtime-{getuid()}" # noqa: S108 

171 else: 

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

173 return self._append_app_name_and_version(path) 

174 

175 @property 

176 def site_runtime_dir(self) -> str: 

177 """ 

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

179 ``$XDG_RUNTIME_DIR/$appname/$version``. 

180 

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

182 fallback to paths associated to the root user instead of a regular logged-in user if it's not set. 

183 

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

185 instead. 

186 

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

188 """ 

189 path = os.environ.get("XDG_RUNTIME_DIR", "") 

190 if not path.strip(): 

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

192 path = "/var/run" 

193 else: 

194 path = "/run" 

195 return self._append_app_name_and_version(path) 

196 

197 @property 

198 def site_data_path(self) -> Path: 

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

200 return self._first_item_as_path_if_multipath(self.site_data_dir) 

201 

202 @property 

203 def site_config_path(self) -> Path: 

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

205 return self._first_item_as_path_if_multipath(self.site_config_dir) 

206 

207 @property 

208 def site_cache_path(self) -> Path: 

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

210 return self._first_item_as_path_if_multipath(self.site_cache_dir) 

211 

212 def _first_item_as_path_if_multipath(self, directory: str) -> Path: 

213 if self.multipath: 

214 # If multipath is True, the first path is returned. 

215 directory = directory.split(os.pathsep)[0] 

216 return Path(directory) 

217 

218 

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

220 media_dir = _get_user_dirs_folder(env_var) 

221 if media_dir is None: 

222 media_dir = os.environ.get(env_var, "").strip() 

223 if not media_dir: 

224 media_dir = os.path.expanduser(fallback_tilde_path) # noqa: PTH111 

225 

226 return media_dir 

227 

228 

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

230 """Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/.""" 

231 user_dirs_config_path = Path(Unix().user_config_dir) / "user-dirs.dirs" 

232 if user_dirs_config_path.exists(): 

233 parser = ConfigParser() 

234 

235 with user_dirs_config_path.open() as stream: 

236 # Add fake section header, so ConfigParser doesn't complain 

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

238 

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

240 return None 

241 

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

243 # Handle relative home paths 

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

245 

246 return None 

247 

248 

249__all__ = [ 

250 "Unix", 

251]