You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

appdirs.py 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. """
  2. This code was taken from https://github.com/ActiveState/appdirs and modified
  3. to suit our purposes.
  4. """
  5. # The following comment should be removed at some point in the future.
  6. # mypy: disallow-untyped-defs=False
  7. from __future__ import absolute_import
  8. import os
  9. import sys
  10. from pip._vendor.six import PY2, text_type
  11. from pip._internal.utils.compat import WINDOWS, expanduser
  12. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  13. if MYPY_CHECK_RUNNING:
  14. from typing import List
  15. def user_cache_dir(appname):
  16. # type: (str) -> str
  17. r"""
  18. Return full path to the user-specific cache dir for this application.
  19. "appname" is the name of application.
  20. Typical user cache directories are:
  21. macOS: ~/Library/Caches/<AppName>
  22. Unix: ~/.cache/<AppName> (XDG default)
  23. Windows: C:\Users\<username>\AppData\Local\<AppName>\Cache
  24. On Windows the only suggestion in the MSDN docs is that local settings go
  25. in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the
  26. non-roaming app data dir (the default returned by `user_data_dir`). Apps
  27. typically put cache data somewhere *under* the given dir here. Some
  28. examples:
  29. ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
  30. ...\Acme\SuperApp\Cache\1.0
  31. OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
  32. """
  33. if WINDOWS:
  34. # Get the base path
  35. path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
  36. # When using Python 2, return paths as bytes on Windows like we do on
  37. # other operating systems. See helper function docs for more details.
  38. if PY2 and isinstance(path, text_type):
  39. path = _win_path_to_bytes(path)
  40. # Add our app name and Cache directory to it
  41. path = os.path.join(path, appname, "Cache")
  42. elif sys.platform == "darwin":
  43. # Get the base path
  44. path = expanduser("~/Library/Caches")
  45. # Add our app name to it
  46. path = os.path.join(path, appname)
  47. else:
  48. # Get the base path
  49. path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache"))
  50. # Add our app name to it
  51. path = os.path.join(path, appname)
  52. return path
  53. def user_data_dir(appname, roaming=False):
  54. # type: (str, bool) -> str
  55. r"""
  56. Return full path to the user-specific data dir for this application.
  57. "appname" is the name of application.
  58. If None, just the system directory is returned.
  59. "roaming" (boolean, default False) can be set True to use the Windows
  60. roaming appdata directory. That means that for users on a Windows
  61. network setup for roaming profiles, this user data will be
  62. sync'd on login. See
  63. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  64. for a discussion of issues.
  65. Typical user data directories are:
  66. macOS: ~/Library/Application Support/<AppName>
  67. if it exists, else ~/.config/<AppName>
  68. Unix: ~/.local/share/<AppName> # or in
  69. $XDG_DATA_HOME, if defined
  70. Win XP (not roaming): C:\Documents and Settings\<username>\ ...
  71. ...Application Data\<AppName>
  72. Win XP (roaming): C:\Documents and Settings\<username>\Local ...
  73. ...Settings\Application Data\<AppName>
  74. Win 7 (not roaming): C:\\Users\<username>\AppData\Local\<AppName>
  75. Win 7 (roaming): C:\\Users\<username>\AppData\Roaming\<AppName>
  76. For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
  77. That means, by default "~/.local/share/<AppName>".
  78. """
  79. if WINDOWS:
  80. const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
  81. path = os.path.join(os.path.normpath(_get_win_folder(const)), appname)
  82. elif sys.platform == "darwin":
  83. path = os.path.join(
  84. expanduser('~/Library/Application Support/'),
  85. appname,
  86. ) if os.path.isdir(os.path.join(
  87. expanduser('~/Library/Application Support/'),
  88. appname,
  89. )
  90. ) else os.path.join(
  91. expanduser('~/.config/'),
  92. appname,
  93. )
  94. else:
  95. path = os.path.join(
  96. os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")),
  97. appname,
  98. )
  99. return path
  100. def user_config_dir(appname, roaming=True):
  101. # type: (str, bool) -> str
  102. """Return full path to the user-specific config dir for this application.
  103. "appname" is the name of application.
  104. If None, just the system directory is returned.
  105. "roaming" (boolean, default True) can be set False to not use the
  106. Windows roaming appdata directory. That means that for users on a
  107. Windows network setup for roaming profiles, this user data will be
  108. sync'd on login. See
  109. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  110. for a discussion of issues.
  111. Typical user data directories are:
  112. macOS: same as user_data_dir
  113. Unix: ~/.config/<AppName>
  114. Win *: same as user_data_dir
  115. For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
  116. That means, by default "~/.config/<AppName>".
  117. """
  118. if WINDOWS:
  119. path = user_data_dir(appname, roaming=roaming)
  120. elif sys.platform == "darwin":
  121. path = user_data_dir(appname)
  122. else:
  123. path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config"))
  124. path = os.path.join(path, appname)
  125. return path
  126. # for the discussion regarding site_config_dirs locations
  127. # see <https://github.com/pypa/pip/issues/1733>
  128. def site_config_dirs(appname):
  129. # type: (str) -> List[str]
  130. r"""Return a list of potential user-shared config dirs for this application.
  131. "appname" is the name of application.
  132. Typical user config directories are:
  133. macOS: /Library/Application Support/<AppName>/
  134. Unix: /etc or $XDG_CONFIG_DIRS[i]/<AppName>/ for each value in
  135. $XDG_CONFIG_DIRS
  136. Win XP: C:\Documents and Settings\All Users\Application ...
  137. ...Data\<AppName>\
  138. Vista: (Fail! "C:\ProgramData" is a hidden *system* directory
  139. on Vista.)
  140. Win 7: Hidden, but writeable on Win 7:
  141. C:\ProgramData\<AppName>\
  142. """
  143. if WINDOWS:
  144. path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
  145. pathlist = [os.path.join(path, appname)]
  146. elif sys.platform == 'darwin':
  147. pathlist = [os.path.join('/Library/Application Support', appname)]
  148. else:
  149. # try looking in $XDG_CONFIG_DIRS
  150. xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
  151. if xdg_config_dirs:
  152. pathlist = [
  153. os.path.join(expanduser(x), appname)
  154. for x in xdg_config_dirs.split(os.pathsep)
  155. ]
  156. else:
  157. pathlist = []
  158. # always look in /etc directly as well
  159. pathlist.append('/etc')
  160. return pathlist
  161. # -- Windows support functions --
  162. def _get_win_folder_from_registry(csidl_name):
  163. # type: (str) -> str
  164. """
  165. This is a fallback technique at best. I'm not sure if using the
  166. registry for this guarantees us the correct answer for all CSIDL_*
  167. names.
  168. """
  169. import _winreg
  170. shell_folder_name = {
  171. "CSIDL_APPDATA": "AppData",
  172. "CSIDL_COMMON_APPDATA": "Common AppData",
  173. "CSIDL_LOCAL_APPDATA": "Local AppData",
  174. }[csidl_name]
  175. key = _winreg.OpenKey(
  176. _winreg.HKEY_CURRENT_USER,
  177. r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
  178. )
  179. directory, _type = _winreg.QueryValueEx(key, shell_folder_name)
  180. return directory
  181. def _get_win_folder_with_ctypes(csidl_name):
  182. # type: (str) -> str
  183. # On Python 2, ctypes.create_unicode_buffer().value returns "unicode",
  184. # which isn't the same as str in the annotation above.
  185. csidl_const = {
  186. "CSIDL_APPDATA": 26,
  187. "CSIDL_COMMON_APPDATA": 35,
  188. "CSIDL_LOCAL_APPDATA": 28,
  189. }[csidl_name]
  190. buf = ctypes.create_unicode_buffer(1024)
  191. windll = ctypes.windll # type: ignore
  192. windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
  193. # Downgrade to short path name if have highbit chars. See
  194. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  195. has_high_char = False
  196. for c in buf:
  197. if ord(c) > 255:
  198. has_high_char = True
  199. break
  200. if has_high_char:
  201. buf2 = ctypes.create_unicode_buffer(1024)
  202. if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
  203. buf = buf2
  204. # The type: ignore is explained under the type annotation for this function
  205. return buf.value # type: ignore
  206. if WINDOWS:
  207. try:
  208. import ctypes
  209. _get_win_folder = _get_win_folder_with_ctypes
  210. except ImportError:
  211. _get_win_folder = _get_win_folder_from_registry
  212. def _win_path_to_bytes(path):
  213. """Encode Windows paths to bytes. Only used on Python 2.
  214. Motivation is to be consistent with other operating systems where paths
  215. are also returned as bytes. This avoids problems mixing bytes and Unicode
  216. elsewhere in the codebase. For more details and discussion see
  217. <https://github.com/pypa/pip/issues/3463>.
  218. If encoding using ASCII and MBCS fails, return the original Unicode path.
  219. """
  220. for encoding in ('ASCII', 'MBCS'):
  221. try:
  222. return path.encode(encoding)
  223. except (UnicodeEncodeError, LookupError):
  224. pass
  225. return path