appdirs.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2005-2010 ActiveState Software Inc.
  3. # Copyright (c) 2013 Eddy Petrișor
  4. # flake8: noqa
  5. """
  6. This file is directly from
  7. https://github.com/ActiveState/appdirs/blob/3fe6a83776843a46f20c2e5587afcffe05e03b39/appdirs.py
  8. The license of https://github.com/ActiveState/appdirs copied below:
  9. # This is the MIT license
  10. Copyright (c) 2010 ActiveState Software Inc.
  11. Permission is hereby granted, free of charge, to any person obtaining a
  12. copy of this software and associated documentation files (the
  13. "Software"), to deal in the Software without restriction, including
  14. without limitation the rights to use, copy, modify, merge, publish,
  15. distribute, sublicense, and/or sell copies of the Software, and to
  16. permit persons to whom the Software is furnished to do so, subject to
  17. the following conditions:
  18. The above copyright notice and this permission notice shall be included
  19. in all copies or substantial portions of the Software.
  20. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  21. OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  23. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  24. CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  25. TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  26. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. """
  28. """Utilities for determining application-specific dirs.
  29. See <https://github.com/ActiveState/appdirs> for details and usage.
  30. """
  31. # Dev Notes:
  32. # - MSDN on where to store app data files:
  33. # http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
  34. # - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
  35. # - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  36. __version__ = "1.4.4"
  37. __version_info__ = tuple(int(segment) for segment in __version__.split("."))
  38. import sys
  39. import os
  40. unicode = str
  41. if sys.platform.startswith('java'):
  42. import platform
  43. os_name = platform.java_ver()[3][0]
  44. if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc.
  45. system = 'win32'
  46. elif os_name.startswith('Mac'): # "Mac OS X", etc.
  47. system = 'darwin'
  48. else: # "Linux", "SunOS", "FreeBSD", etc.
  49. # Setting this to "linux2" is not ideal, but only Windows or Mac
  50. # are actually checked for and the rest of the module expects
  51. # *sys.platform* style strings.
  52. system = 'linux2'
  53. else:
  54. system = sys.platform
  55. def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
  56. r"""Return full path to the user-specific cache dir for this application.
  57. "appname" is the name of application.
  58. If None, just the system directory is returned.
  59. "appauthor" (only used on Windows) is the name of the
  60. appauthor or distributing body for this application. Typically
  61. it is the owning company name. This falls back to appname. You may
  62. pass False to disable it.
  63. "version" is an optional version path element to append to the
  64. path. You might want to use this if you want multiple versions
  65. of your app to be able to run independently. If used, this
  66. would typically be "<major>.<minor>".
  67. Only applied when appname is present.
  68. "opinion" (boolean) can be False to disable the appending of
  69. "Cache" to the base app data dir for Windows. See
  70. discussion below.
  71. Typical user cache directories are:
  72. Mac OS X: ~/Library/Caches/<AppName>
  73. Unix: ~/.cache/<AppName> (XDG default)
  74. Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
  75. Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
  76. On Windows the only suggestion in the MSDN docs is that local settings go in
  77. the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
  78. app data dir (the default returned by `user_data_dir` above). Apps typically
  79. put cache data somewhere *under* the given dir here. Some examples:
  80. ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
  81. ...\Acme\SuperApp\Cache\1.0
  82. OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
  83. This can be disabled with the `opinion=False` option.
  84. """
  85. if system == "win32":
  86. if appauthor is None:
  87. appauthor = appname
  88. path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
  89. if appname:
  90. if appauthor is not False:
  91. path = os.path.join(path, appauthor, appname)
  92. else:
  93. path = os.path.join(path, appname)
  94. if opinion:
  95. path = os.path.join(path, "Cache")
  96. elif system == 'darwin':
  97. path = os.path.expanduser('~/Library/Caches')
  98. if appname:
  99. path = os.path.join(path, appname)
  100. else:
  101. path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
  102. if appname:
  103. path = os.path.join(path, appname)
  104. if appname and version:
  105. path = os.path.join(path, version)
  106. return path
  107. #---- internal support stuff
  108. def _get_win_folder_from_registry(csidl_name):
  109. """This is a fallback technique at best. I'm not sure if using the
  110. registry for this guarantees us the correct answer for all CSIDL_*
  111. names.
  112. """
  113. import winreg as _winreg
  114. shell_folder_name = {
  115. "CSIDL_APPDATA": "AppData",
  116. "CSIDL_COMMON_APPDATA": "Common AppData",
  117. "CSIDL_LOCAL_APPDATA": "Local AppData",
  118. }[csidl_name]
  119. key = _winreg.OpenKey(
  120. _winreg.HKEY_CURRENT_USER,
  121. r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
  122. )
  123. dir, type = _winreg.QueryValueEx(key, shell_folder_name)
  124. return dir
  125. def _get_win_folder_with_pywin32(csidl_name):
  126. from win32com.shell import shellcon, shell
  127. dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
  128. # Try to make this a unicode path because SHGetFolderPath does
  129. # not return unicode strings when there is unicode data in the
  130. # path.
  131. try:
  132. dir = unicode(dir)
  133. # Downgrade to short path name if have highbit chars. See
  134. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  135. has_high_char = False
  136. for c in dir:
  137. if ord(c) > 255:
  138. has_high_char = True
  139. break
  140. if has_high_char:
  141. try:
  142. import win32api
  143. dir = win32api.GetShortPathName(dir)
  144. except ImportError:
  145. pass
  146. except UnicodeError:
  147. pass
  148. return dir
  149. def _get_win_folder_with_ctypes(csidl_name):
  150. import ctypes
  151. csidl_const = {
  152. "CSIDL_APPDATA": 26,
  153. "CSIDL_COMMON_APPDATA": 35,
  154. "CSIDL_LOCAL_APPDATA": 28,
  155. }[csidl_name]
  156. buf = ctypes.create_unicode_buffer(1024)
  157. ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
  158. # Downgrade to short path name if have highbit chars. See
  159. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  160. has_high_char = False
  161. for c in buf:
  162. if ord(c) > 255:
  163. has_high_char = True
  164. break
  165. if has_high_char:
  166. buf2 = ctypes.create_unicode_buffer(1024)
  167. if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
  168. buf = buf2
  169. return buf.value
  170. def _get_win_folder_with_jna(csidl_name):
  171. import array
  172. from com.sun import jna
  173. from com.sun.jna.platform import win32
  174. buf_size = win32.WinDef.MAX_PATH * 2
  175. buf = array.zeros('c', buf_size)
  176. shell = win32.Shell32.INSTANCE
  177. shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
  178. dir = jna.Native.toString(buf.tostring()).rstrip("\0")
  179. # Downgrade to short path name if have highbit chars. See
  180. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  181. has_high_char = False
  182. for c in dir:
  183. if ord(c) > 255:
  184. has_high_char = True
  185. break
  186. if has_high_char:
  187. buf = array.zeros('c', buf_size)
  188. kernel = win32.Kernel32.INSTANCE
  189. if kernel.GetShortPathName(dir, buf, buf_size):
  190. dir = jna.Native.toString(buf.tostring()).rstrip("\0")
  191. return dir
  192. if system == "win32":
  193. try:
  194. import win32com.shell
  195. _get_win_folder = _get_win_folder_with_pywin32
  196. except ImportError:
  197. try:
  198. from ctypes import windll
  199. _get_win_folder = _get_win_folder_with_ctypes
  200. except ImportError:
  201. try:
  202. import com.sun.jna
  203. _get_win_folder = _get_win_folder_with_jna
  204. except ImportError:
  205. _get_win_folder = _get_win_folder_from_registry