Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_vendor/platformdirs/_xdg.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

126 statements  

1"""XDG environment variable mixin for Unix and macOS.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import posixpath 

7import sys 

8from contextlib import suppress 

9from typing import Final 

10 

11from .api import PlatformDirsABC 

12 

13 

14class XDGMixin(PlatformDirsABC): 

15 """Mixin that checks XDG environment variables, falling back to platform-specific defaults via ``super()``.""" 

16 

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 

23 

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 

29 

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) 

34 

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 

41 

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 

47 

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) 

52 

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 

59 

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 

66 

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 

73 

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 

80 

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 

85 

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 

90 

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 

95 

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 

100 

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 

105 

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 

110 

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 

115 

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 

120 

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 

125 

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 

130 

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 return f"{os.path.expanduser(path)}/fonts" # ruff:ignore[os-path-expanduser] # API returns str, not Path 

136 return super().user_fonts_dir 

137 

138 @property 

139 def user_applications_dir(self) -> str: 

140 """Applications directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default.""" 

141 if path := _xdg_dir("XDG_DATA_HOME"): 

142 return os.path.join(os.path.expanduser(path), "applications") # ruff:ignore[os-path-expanduser, os-path-join] 

143 return super().user_applications_dir 

144 

145 @property 

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

147 if xdg_dirs := _xdg_dir_list("XDG_DATA_DIRS"): 

148 return [os.path.join(p, "applications") for p in xdg_dirs] # ruff:ignore[os-path-join] 

149 return super()._site_applications_dirs 

150 

151 @property 

152 def site_applications_dir(self) -> str: 

153 """Applications directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default.""" 

154 dirs = self._site_applications_dirs 

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

156 

157 

158def _xdg_dir(env_var: str) -> str | None: 

159 path: Final = os.environ.get(env_var, "").strip() 

160 return path if posixpath.isabs(path) else None 

161 

162 

163def _xdg_dir_list(env_var: str) -> list[str]: 

164 return [ 

165 stripped for path in os.environ.get(env_var, "").split(os.pathsep) if posixpath.isabs(stripped := path.strip()) 

166 ] 

167 

168 

169def _expand_user(path: str) -> str: 

170 """Expand the leading ``~`` of ``path`` like :func:`os.path.expanduser`, treating an empty ``HOME`` as unset. 

171 

172 :raises RuntimeError: if neither ``HOME`` nor the password database gives a home directory. 

173 

174 """ 

175 if "HOME" in os.environ and not os.environ["HOME"]: 

176 # expanduser roots the path at / for an empty HOME; Rust's std::env::home_dir treats it as unset too 

177 expanded = path if (home := _passwd_home()) is None else (home.rstrip("/") + path[1:] or "/") 

178 else: 

179 expanded = os.path.expanduser(path) # ruff:ignore[os-path-expanduser] # str(Path) rewrites separators on Windows 

180 if expanded.startswith("~"): 

181 msg = f"could not determine the home directory for {path!r}, set HOME or an absolute XDG variable" 

182 raise RuntimeError(msg) 

183 return expanded 

184 

185 

186def _passwd_home() -> str | None: 

187 if sys.platform == "win32": # pragma: win32 cover # narrows pwd and os.getuid for ty 

188 return None 

189 with suppress(ImportError, KeyError): 

190 import pwd # ruff:ignore[import-outside-top-level] # missing on WASI 

191 

192 return pwd.getpwuid(os.getuid()).pw_dir 

193 return None 

194 

195 

196__all__ = [ 

197 "XDGMixin", 

198 "_expand_user", 

199 "_xdg_dir", 

200]