appdirs.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Copyright (c) 2005-2010 ActiveState Software Inc.
  4. # Copyright (c) 2013 Eddy Petrișor
  5. """Utilities for determining application-specific dirs.
  6. See <http://github.com/ActiveState/appdirs> for details and usage.
  7. """
  8. # Dev Notes:
  9. # - MSDN on where to store app data files:
  10. # http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
  11. # - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
  12. # - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  13. __version_info__ = (1, 4, 3)
  14. __version__ = '.'.join(map(str, __version_info__))
  15. import sys
  16. import os
  17. PY3 = sys.version_info[0] == 3
  18. if PY3:
  19. unicode = str
  20. if sys.platform.startswith('java'):
  21. import platform
  22. os_name = platform.java_ver()[3][0]
  23. if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc.
  24. system = 'win32'
  25. elif os_name.startswith('Mac'): # "Mac OS X", etc.
  26. system = 'darwin'
  27. else: # "Linux", "SunOS", "FreeBSD", etc.
  28. # Setting this to "linux2" is not ideal, but only Windows or Mac
  29. # are actually checked for and the rest of the module expects
  30. # *sys.platform* style strings.
  31. system = 'linux2'
  32. elif sys.platform == 'cli' and os.name == 'nt':
  33. # Detect Windows in IronPython to match pip._internal.utils.compat.WINDOWS
  34. # Discussion: <https://github.com/pypa/pip/pull/7501>
  35. system = 'win32'
  36. else:
  37. system = sys.platform
  38. def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
  39. r"""Return full path to the user-specific data dir for this application.
  40. "appname" is the name of application.
  41. If None, just the system directory is returned.
  42. "appauthor" (only used on Windows) is the name of the
  43. appauthor or distributing body for this application. Typically
  44. it is the owning company name. This falls back to appname. You may
  45. pass False to disable it.
  46. "version" is an optional version path element to append to the
  47. path. You might want to use this if you want multiple versions
  48. of your app to be able to run independently. If used, this
  49. would typically be "<major>.<minor>".
  50. Only applied when appname is present.
  51. "roaming" (boolean, default False) can be set True to use the Windows
  52. roaming appdata directory. That means that for users on a Windows
  53. network setup for roaming profiles, this user data will be
  54. sync'd on login. See
  55. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  56. for a discussion of issues.
  57. Typical user data directories are:
  58. Mac OS X: ~/Library/Application Support/<AppName> # or ~/.config/<AppName>, if the other does not exist
  59. Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
  60. Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
  61. Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
  62. Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
  63. Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
  64. For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
  65. That means, by default "~/.local/share/<AppName>".
  66. """
  67. if system == "win32":
  68. if appauthor is None:
  69. appauthor = appname
  70. const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
  71. path = os.path.normpath(_get_win_folder(const))
  72. if appname:
  73. if appauthor is not False:
  74. path = os.path.join(path, appauthor, appname)
  75. else:
  76. path = os.path.join(path, appname)
  77. elif system == 'darwin':
  78. path = os.path.expanduser('~/Library/Application Support/')
  79. if appname:
  80. path = os.path.join(path, appname)
  81. else:
  82. path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share"))
  83. if appname:
  84. path = os.path.join(path, appname)
  85. if appname and version:
  86. path = os.path.join(path, version)
  87. return path
  88. def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
  89. r"""Return full path to the user-shared data dir for this application.
  90. "appname" is the name of application.
  91. If None, just the system directory is returned.
  92. "appauthor" (only used on Windows) is the name of the
  93. appauthor or distributing body for this application. Typically
  94. it is the owning company name. This falls back to appname. You may
  95. pass False to disable it.
  96. "version" is an optional version path element to append to the
  97. path. You might want to use this if you want multiple versions
  98. of your app to be able to run independently. If used, this
  99. would typically be "<major>.<minor>".
  100. Only applied when appname is present.
  101. "multipath" is an optional parameter only applicable to *nix
  102. which indicates that the entire list of data dirs should be
  103. returned. By default, the first item from XDG_DATA_DIRS is
  104. returned, or '/usr/local/share/<AppName>',
  105. if XDG_DATA_DIRS is not set
  106. Typical site data directories are:
  107. Mac OS X: /Library/Application Support/<AppName>
  108. Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
  109. Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
  110. Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
  111. Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
  112. For Unix, this is using the $XDG_DATA_DIRS[0] default.
  113. WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
  114. """
  115. if system == "win32":
  116. if appauthor is None:
  117. appauthor = appname
  118. path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
  119. if appname:
  120. if appauthor is not False:
  121. path = os.path.join(path, appauthor, appname)
  122. else:
  123. path = os.path.join(path, appname)
  124. elif system == 'darwin':
  125. path = os.path.expanduser('/Library/Application Support')
  126. if appname:
  127. path = os.path.join(path, appname)
  128. else:
  129. # XDG default for $XDG_DATA_DIRS
  130. # only first, if multipath is False
  131. path = os.getenv('XDG_DATA_DIRS',
  132. os.pathsep.join(['/usr/local/share', '/usr/share']))
  133. pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
  134. if appname:
  135. if version:
  136. appname = os.path.join(appname, version)
  137. pathlist = [os.path.join(x, appname) for x in pathlist]
  138. if multipath:
  139. path = os.pathsep.join(pathlist)
  140. else:
  141. path = pathlist[0]
  142. return path
  143. if appname and version:
  144. path = os.path.join(path, version)
  145. return path
  146. def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
  147. r"""Return full path to the user-specific config dir for this application.
  148. "appname" is the name of application.
  149. If None, just the system directory is returned.
  150. "appauthor" (only used on Windows) is the name of the
  151. appauthor or distributing body for this application. Typically
  152. it is the owning company name. This falls back to appname. You may
  153. pass False to disable it.
  154. "version" is an optional version path element to append to the
  155. path. You might want to use this if you want multiple versions
  156. of your app to be able to run independently. If used, this
  157. would typically be "<major>.<minor>".
  158. Only applied when appname is present.
  159. "roaming" (boolean, default False) can be set True to use the Windows
  160. roaming appdata directory. That means that for users on a Windows
  161. network setup for roaming profiles, this user data will be
  162. sync'd on login. See
  163. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  164. for a discussion of issues.
  165. Typical user config directories are:
  166. Mac OS X: same as user_data_dir
  167. Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
  168. Win *: same as user_data_dir
  169. For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
  170. That means, by default "~/.config/<AppName>".
  171. """
  172. if system in ["win32", "darwin"]:
  173. path = user_data_dir(appname, appauthor, None, roaming)
  174. else:
  175. path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
  176. if appname:
  177. path = os.path.join(path, appname)
  178. if appname and version:
  179. path = os.path.join(path, version)
  180. return path
  181. # for the discussion regarding site_config_dir locations
  182. # see <https://github.com/pypa/pip/issues/1733>
  183. def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
  184. r"""Return full path to the user-shared data dir for this application.
  185. "appname" is the name of application.
  186. If None, just the system directory is returned.
  187. "appauthor" (only used on Windows) is the name of the
  188. appauthor or distributing body for this application. Typically
  189. it is the owning company name. This falls back to appname. You may
  190. pass False to disable it.
  191. "version" is an optional version path element to append to the
  192. path. You might want to use this if you want multiple versions
  193. of your app to be able to run independently. If used, this
  194. would typically be "<major>.<minor>".
  195. Only applied when appname is present.
  196. "multipath" is an optional parameter only applicable to *nix
  197. which indicates that the entire list of config dirs should be
  198. returned. By default, the first item from XDG_CONFIG_DIRS is
  199. returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
  200. Typical site config directories are:
  201. Mac OS X: same as site_data_dir
  202. Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
  203. $XDG_CONFIG_DIRS
  204. Win *: same as site_data_dir
  205. Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
  206. For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
  207. WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
  208. """
  209. if system in ["win32", "darwin"]:
  210. path = site_data_dir(appname, appauthor)
  211. if appname and version:
  212. path = os.path.join(path, version)
  213. else:
  214. # XDG default for $XDG_CONFIG_DIRS (missing or empty)
  215. # see <https://github.com/pypa/pip/pull/7501#discussion_r360624829>
  216. # only first, if multipath is False
  217. path = os.getenv('XDG_CONFIG_DIRS') or '/etc/xdg'
  218. pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) if x]
  219. if appname:
  220. if version:
  221. appname = os.path.join(appname, version)
  222. pathlist = [os.path.join(x, appname) for x in pathlist]
  223. if multipath:
  224. path = os.pathsep.join(pathlist)
  225. else:
  226. path = pathlist[0]
  227. return path
  228. def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
  229. r"""Return full path to the user-specific cache dir for this application.
  230. "appname" is the name of application.
  231. If None, just the system directory is returned.
  232. "appauthor" (only used on Windows) is the name of the
  233. appauthor or distributing body for this application. Typically
  234. it is the owning company name. This falls back to appname. You may
  235. pass False to disable it.
  236. "version" is an optional version path element to append to the
  237. path. You might want to use this if you want multiple versions
  238. of your app to be able to run independently. If used, this
  239. would typically be "<major>.<minor>".
  240. Only applied when appname is present.
  241. "opinion" (boolean) can be False to disable the appending of
  242. "Cache" to the base app data dir for Windows. See
  243. discussion below.
  244. Typical user cache directories are:
  245. Mac OS X: ~/Library/Caches/<AppName>
  246. Unix: ~/.cache/<AppName> (XDG default)
  247. Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
  248. Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
  249. On Windows the only suggestion in the MSDN docs is that local settings go in
  250. the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
  251. app data dir (the default returned by `user_data_dir` above). Apps typically
  252. put cache data somewhere *under* the given dir here. Some examples:
  253. ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
  254. ...\Acme\SuperApp\Cache\1.0
  255. OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
  256. This can be disabled with the `opinion=False` option.
  257. """
  258. if system == "win32":
  259. if appauthor is None:
  260. appauthor = appname
  261. path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
  262. # When using Python 2, return paths as bytes on Windows like we do on
  263. # other operating systems. See helper function docs for more details.
  264. if not PY3 and isinstance(path, unicode):
  265. path = _win_path_to_bytes(path)
  266. if appname:
  267. if appauthor is not False:
  268. path = os.path.join(path, appauthor, appname)
  269. else:
  270. path = os.path.join(path, appname)
  271. if opinion:
  272. path = os.path.join(path, "Cache")
  273. elif system == 'darwin':
  274. path = os.path.expanduser('~/Library/Caches')
  275. if appname:
  276. path = os.path.join(path, appname)
  277. else:
  278. path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
  279. if appname:
  280. path = os.path.join(path, appname)
  281. if appname and version:
  282. path = os.path.join(path, version)
  283. return path
  284. def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
  285. r"""Return full path to the user-specific state dir for this application.
  286. "appname" is the name of application.
  287. If None, just the system directory is returned.
  288. "appauthor" (only used on Windows) is the name of the
  289. appauthor or distributing body for this application. Typically
  290. it is the owning company name. This falls back to appname. You may
  291. pass False to disable it.
  292. "version" is an optional version path element to append to the
  293. path. You might want to use this if you want multiple versions
  294. of your app to be able to run independently. If used, this
  295. would typically be "<major>.<minor>".
  296. Only applied when appname is present.
  297. "roaming" (boolean, default False) can be set True to use the Windows
  298. roaming appdata directory. That means that for users on a Windows
  299. network setup for roaming profiles, this user data will be
  300. sync'd on login. See
  301. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  302. for a discussion of issues.
  303. Typical user state directories are:
  304. Mac OS X: same as user_data_dir
  305. Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined
  306. Win *: same as user_data_dir
  307. For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
  308. to extend the XDG spec and support $XDG_STATE_HOME.
  309. That means, by default "~/.local/state/<AppName>".
  310. """
  311. if system in ["win32", "darwin"]:
  312. path = user_data_dir(appname, appauthor, None, roaming)
  313. else:
  314. path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state"))
  315. if appname:
  316. path = os.path.join(path, appname)
  317. if appname and version:
  318. path = os.path.join(path, version)
  319. return path
  320. def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
  321. r"""Return full path to the user-specific log dir for this application.
  322. "appname" is the name of application.
  323. If None, just the system directory is returned.
  324. "appauthor" (only used on Windows) is the name of the
  325. appauthor or distributing body for this application. Typically
  326. it is the owning company name. This falls back to appname. You may
  327. pass False to disable it.
  328. "version" is an optional version path element to append to the
  329. path. You might want to use this if you want multiple versions
  330. of your app to be able to run independently. If used, this
  331. would typically be "<major>.<minor>".
  332. Only applied when appname is present.
  333. "opinion" (boolean) can be False to disable the appending of
  334. "Logs" to the base app data dir for Windows, and "log" to the
  335. base cache dir for Unix. See discussion below.
  336. Typical user log directories are:
  337. Mac OS X: ~/Library/Logs/<AppName>
  338. Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
  339. Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
  340. Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
  341. On Windows the only suggestion in the MSDN docs is that local settings
  342. go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
  343. examples of what some windows apps use for a logs dir.)
  344. OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
  345. value for Windows and appends "log" to the user cache dir for Unix.
  346. This can be disabled with the `opinion=False` option.
  347. """
  348. if system == "darwin":
  349. path = os.path.join(
  350. os.path.expanduser('~/Library/Logs'),
  351. appname)
  352. elif system == "win32":
  353. path = user_data_dir(appname, appauthor, version)
  354. version = False
  355. if opinion:
  356. path = os.path.join(path, "Logs")
  357. else:
  358. path = user_cache_dir(appname, appauthor, version)
  359. version = False
  360. if opinion:
  361. path = os.path.join(path, "log")
  362. if appname and version:
  363. path = os.path.join(path, version)
  364. return path
  365. class AppDirs(object):
  366. """Convenience wrapper for getting application dirs."""
  367. def __init__(self, appname=None, appauthor=None, version=None,
  368. roaming=False, multipath=False):
  369. self.appname = appname
  370. self.appauthor = appauthor
  371. self.version = version
  372. self.roaming = roaming
  373. self.multipath = multipath
  374. @property
  375. def user_data_dir(self):
  376. return user_data_dir(self.appname, self.appauthor,
  377. version=self.version, roaming=self.roaming)
  378. @property
  379. def site_data_dir(self):
  380. return site_data_dir(self.appname, self.appauthor,
  381. version=self.version, multipath=self.multipath)
  382. @property
  383. def user_config_dir(self):
  384. return user_config_dir(self.appname, self.appauthor,
  385. version=self.version, roaming=self.roaming)
  386. @property
  387. def site_config_dir(self):
  388. return site_config_dir(self.appname, self.appauthor,
  389. version=self.version, multipath=self.multipath)
  390. @property
  391. def user_cache_dir(self):
  392. return user_cache_dir(self.appname, self.appauthor,
  393. version=self.version)
  394. @property
  395. def user_state_dir(self):
  396. return user_state_dir(self.appname, self.appauthor,
  397. version=self.version)
  398. @property
  399. def user_log_dir(self):
  400. return user_log_dir(self.appname, self.appauthor,
  401. version=self.version)
  402. #---- internal support stuff
  403. def _get_win_folder_from_registry(csidl_name):
  404. """This is a fallback technique at best. I'm not sure if using the
  405. registry for this guarantees us the correct answer for all CSIDL_*
  406. names.
  407. """
  408. if PY3:
  409. import winreg as _winreg
  410. else:
  411. import _winreg
  412. shell_folder_name = {
  413. "CSIDL_APPDATA": "AppData",
  414. "CSIDL_COMMON_APPDATA": "Common AppData",
  415. "CSIDL_LOCAL_APPDATA": "Local AppData",
  416. }[csidl_name]
  417. key = _winreg.OpenKey(
  418. _winreg.HKEY_CURRENT_USER,
  419. r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
  420. )
  421. dir, type = _winreg.QueryValueEx(key, shell_folder_name)
  422. return dir
  423. def _get_win_folder_with_pywin32(csidl_name):
  424. from win32com.shell import shellcon, shell
  425. dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
  426. # Try to make this a unicode path because SHGetFolderPath does
  427. # not return unicode strings when there is unicode data in the
  428. # path.
  429. try:
  430. dir = unicode(dir)
  431. # Downgrade to short path name if have highbit chars. See
  432. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  433. has_high_char = False
  434. for c in dir:
  435. if ord(c) > 255:
  436. has_high_char = True
  437. break
  438. if has_high_char:
  439. try:
  440. import win32api
  441. dir = win32api.GetShortPathName(dir)
  442. except ImportError:
  443. pass
  444. except UnicodeError:
  445. pass
  446. return dir
  447. def _get_win_folder_with_ctypes(csidl_name):
  448. import ctypes
  449. csidl_const = {
  450. "CSIDL_APPDATA": 26,
  451. "CSIDL_COMMON_APPDATA": 35,
  452. "CSIDL_LOCAL_APPDATA": 28,
  453. }[csidl_name]
  454. buf = ctypes.create_unicode_buffer(1024)
  455. ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
  456. # Downgrade to short path name if have highbit chars. See
  457. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  458. has_high_char = False
  459. for c in buf:
  460. if ord(c) > 255:
  461. has_high_char = True
  462. break
  463. if has_high_char:
  464. buf2 = ctypes.create_unicode_buffer(1024)
  465. if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
  466. buf = buf2
  467. return buf.value
  468. def _get_win_folder_with_jna(csidl_name):
  469. import array
  470. from com.sun import jna
  471. from com.sun.jna.platform import win32
  472. buf_size = win32.WinDef.MAX_PATH * 2
  473. buf = array.zeros('c', buf_size)
  474. shell = win32.Shell32.INSTANCE
  475. shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
  476. dir = jna.Native.toString(buf.tostring()).rstrip("\0")
  477. # Downgrade to short path name if have highbit chars. See
  478. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  479. has_high_char = False
  480. for c in dir:
  481. if ord(c) > 255:
  482. has_high_char = True
  483. break
  484. if has_high_char:
  485. buf = array.zeros('c', buf_size)
  486. kernel = win32.Kernel32.INSTANCE
  487. if kernel.GetShortPathName(dir, buf, buf_size):
  488. dir = jna.Native.toString(buf.tostring()).rstrip("\0")
  489. return dir
  490. if system == "win32":
  491. try:
  492. from ctypes import windll
  493. _get_win_folder = _get_win_folder_with_ctypes
  494. except ImportError:
  495. try:
  496. import com.sun.jna
  497. _get_win_folder = _get_win_folder_with_jna
  498. except ImportError:
  499. _get_win_folder = _get_win_folder_from_registry
  500. def _win_path_to_bytes(path):
  501. """Encode Windows paths to bytes. Only used on Python 2.
  502. Motivation is to be consistent with other operating systems where paths
  503. are also returned as bytes. This avoids problems mixing bytes and Unicode
  504. elsewhere in the codebase. For more details and discussion see
  505. <https://github.com/pypa/pip/issues/3463>.
  506. If encoding using ASCII and MBCS fails, return the original Unicode path.
  507. """
  508. for encoding in ('ASCII', 'MBCS'):
  509. try:
  510. return path.encode(encoding)
  511. except (UnicodeEncodeError, LookupError):
  512. pass
  513. return path
  514. #---- self test code
  515. if __name__ == "__main__":
  516. appname = "MyApp"
  517. appauthor = "MyCompany"
  518. props = ("user_data_dir",
  519. "user_config_dir",
  520. "user_cache_dir",
  521. "user_state_dir",
  522. "user_log_dir",
  523. "site_data_dir",
  524. "site_config_dir")
  525. print("-- app dirs %s --" % __version__)
  526. print("-- app dirs (with optional 'version')")
  527. dirs = AppDirs(appname, appauthor, version="1.0")
  528. for prop in props:
  529. print("%s: %s" % (prop, getattr(dirs, prop)))
  530. print("\n-- app dirs (without optional 'version')")
  531. dirs = AppDirs(appname, appauthor)
  532. for prop in props:
  533. print("%s: %s" % (prop, getattr(dirs, prop)))
  534. print("\n-- app dirs (without optional 'appauthor')")
  535. dirs = AppDirs(appname)
  536. for prop in props:
  537. print("%s: %s" % (prop, getattr(dirs, prop)))
  538. print("\n-- app dirs (with disabled 'appauthor')")
  539. dirs = AppDirs(appname, appauthor=False)
  540. for prop in props:
  541. print("%s: %s" % (prop, getattr(dirs, prop)))