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

122 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 # 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 

138 

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 

145 

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 

151 

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] 

157 

158 

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 

162 

163 

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 ] 

168 

169 

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. 

172 

173 :raises RuntimeError: if the home directory cannot be resolved. 

174 

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 

181 

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 

189 

190 

191__all__ = [ 

192 "XDGMixin", 

193 "_expand_user", 

194 "_xdg_dir", 

195]