_appdirs.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Copyright (c) 2005-2010 ActiveState Software Inc.
  4. # Copyright (c) 2013 Eddy Petrișor
  5. # flake8: noqa
  6. """
  7. This file is directly from
  8. https://github.com/ActiveState/appdirs/blob/3fe6a83776843a46f20c2e5587afcffe05e03b39/appdirs.py
  9. The license of https://github.com/ActiveState/appdirs copied below:
  10. # This is the MIT license
  11. Copyright (c) 2010 ActiveState Software Inc.
  12. Permission is hereby granted, free of charge, to any person obtaining a
  13. copy of this software and associated documentation files (the
  14. "Software"), to deal in the Software without restriction, including
  15. without limitation the rights to use, copy, modify, merge, publish,
  16. distribute, sublicense, and/or sell copies of the Software, and to
  17. permit persons to whom the Software is furnished to do so, subject to
  18. the following conditions:
  19. The above copyright notice and this permission notice shall be included
  20. in all copies or substantial portions of the Software.
  21. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  22. OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  24. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  25. CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  26. TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  27. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. """
  29. """Utilities for determining application-specific dirs.
  30. See <https://github.com/ActiveState/appdirs> for details and usage.
  31. """
  32. # Dev Notes:
  33. # - Windows "Known Folders": https://learn.microsoft.com/en-us/windows/win32/shell/csidl
  34. # - macOS File System Programming Guide: https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/Introduction/Introduction.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 os
  39. import sys
  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_data_dir(appname=None, appauthor=None, version=None, roaming=False):
  56. r"""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. "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. "roaming" (boolean, default False) can be set True to use the Windows
  69. roaming appdata directory. That means that for users on a Windows
  70. network setup for roaming profiles, this user data will be
  71. sync'd on login. See
  72. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  73. for a discussion of issues.
  74. Typical user data directories are:
  75. Mac OS X: ~/Library/Application Support/<AppName>
  76. Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
  77. Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
  78. Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
  79. Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
  80. Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
  81. For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
  82. That means, by default "~/.local/share/<AppName>".
  83. """
  84. if system == "win32":
  85. if appauthor is None:
  86. appauthor = appname
  87. const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
  88. path = os.path.normpath(_get_win_folder(const))
  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. elif system == "darwin":
  95. path = os.path.expanduser("~/Library/Application Support/")
  96. if appname:
  97. path = os.path.join(path, appname)
  98. else:
  99. path = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share"))
  100. if appname:
  101. path = os.path.join(path, appname)
  102. if appname and version:
  103. path = os.path.join(path, version)
  104. return path
  105. def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
  106. r"""Return full path to the user-shared data dir for this application.
  107. "appname" is the name of application.
  108. If None, just the system directory is returned.
  109. "appauthor" (only used on Windows) is the name of the
  110. appauthor or distributing body for this application. Typically
  111. it is the owning company name. This falls back to appname. You may
  112. pass False to disable it.
  113. "version" is an optional version path element to append to the
  114. path. You might want to use this if you want multiple versions
  115. of your app to be able to run independently. If used, this
  116. would typically be "<major>.<minor>".
  117. Only applied when appname is present.
  118. "multipath" is an optional parameter only applicable to *nix
  119. which indicates that the entire list of data dirs should be
  120. returned. By default, the first item from XDG_DATA_DIRS is
  121. returned, or '/usr/local/share/<AppName>',
  122. if XDG_DATA_DIRS is not set
  123. Typical site data directories are:
  124. Mac OS X: /Library/Application Support/<AppName>
  125. Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
  126. Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
  127. Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
  128. Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
  129. For Unix, this is using the $XDG_DATA_DIRS[0] default.
  130. WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
  131. """
  132. if system == "win32":
  133. if appauthor is None:
  134. appauthor = appname
  135. path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
  136. if appname:
  137. if appauthor is not False:
  138. path = os.path.join(path, appauthor, appname)
  139. else:
  140. path = os.path.join(path, appname)
  141. elif system == "darwin":
  142. path = os.path.expanduser("/Library/Application Support")
  143. if appname:
  144. path = os.path.join(path, appname)
  145. else:
  146. # XDG default for $XDG_DATA_DIRS
  147. # only first, if multipath is False
  148. path = os.getenv(
  149. "XDG_DATA_DIRS", os.pathsep.join(["/usr/local/share", "/usr/share"])
  150. )
  151. pathlist = [
  152. os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)
  153. ]
  154. if appname:
  155. if version:
  156. appname = os.path.join(appname, version)
  157. pathlist = [os.sep.join([x, appname]) for x in pathlist]
  158. if multipath:
  159. path = os.pathsep.join(pathlist)
  160. else:
  161. path = pathlist[0]
  162. return path
  163. if appname and version:
  164. path = os.path.join(path, version)
  165. return path
  166. def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
  167. r"""Return full path to the user-specific config dir for this application.
  168. "appname" is the name of application.
  169. If None, just the system directory is returned.
  170. "appauthor" (only used on Windows) is the name of the
  171. appauthor or distributing body for this application. Typically
  172. it is the owning company name. This falls back to appname. You may
  173. pass False to disable it.
  174. "version" is an optional version path element to append to the
  175. path. You might want to use this if you want multiple versions
  176. of your app to be able to run independently. If used, this
  177. would typically be "<major>.<minor>".
  178. Only applied when appname is present.
  179. "roaming" (boolean, default False) can be set True to use the Windows
  180. roaming appdata directory. That means that for users on a Windows
  181. network setup for roaming profiles, this user data will be
  182. sync'd on login. See
  183. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  184. for a discussion of issues.
  185. Typical user config directories are:
  186. Mac OS X: ~/Library/Preferences/<AppName>
  187. Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
  188. Win *: same as user_data_dir
  189. For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
  190. That means, by default "~/.config/<AppName>".
  191. """
  192. if system == "win32":
  193. path = user_data_dir(appname, appauthor, None, roaming)
  194. elif system == "darwin":
  195. path = os.path.expanduser("~/Library/Preferences/")
  196. if appname:
  197. path = os.path.join(path, appname)
  198. else:
  199. path = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
  200. if appname:
  201. path = os.path.join(path, appname)
  202. if appname and version:
  203. path = os.path.join(path, version)
  204. return path
  205. def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
  206. r"""Return full path to the user-shared data dir for this application.
  207. "appname" is the name of application.
  208. If None, just the system directory is returned.
  209. "appauthor" (only used on Windows) is the name of the
  210. appauthor or distributing body for this application. Typically
  211. it is the owning company name. This falls back to appname. You may
  212. pass False to disable it.
  213. "version" is an optional version path element to append to the
  214. path. You might want to use this if you want multiple versions
  215. of your app to be able to run independently. If used, this
  216. would typically be "<major>.<minor>".
  217. Only applied when appname is present.
  218. "multipath" is an optional parameter only applicable to *nix
  219. which indicates that the entire list of config dirs should be
  220. returned. By default, the first item from XDG_CONFIG_DIRS is
  221. returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
  222. Typical site config directories are:
  223. Mac OS X: same as site_data_dir
  224. Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
  225. $XDG_CONFIG_DIRS
  226. Win *: same as site_data_dir
  227. Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
  228. For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
  229. WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
  230. """
  231. if system == "win32":
  232. path = site_data_dir(appname, appauthor)
  233. if appname and version:
  234. path = os.path.join(path, version)
  235. elif system == "darwin":
  236. path = os.path.expanduser("/Library/Preferences")
  237. if appname:
  238. path = os.path.join(path, appname)
  239. else:
  240. # XDG default for $XDG_CONFIG_DIRS
  241. # only first, if multipath is False
  242. path = os.getenv("XDG_CONFIG_DIRS", "/etc/xdg")
  243. pathlist = [
  244. os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)
  245. ]
  246. if appname:
  247. if version:
  248. appname = os.path.join(appname, version)
  249. pathlist = [os.sep.join([x, appname]) for x in pathlist]
  250. if multipath:
  251. path = os.pathsep.join(pathlist)
  252. else:
  253. path = pathlist[0]
  254. return path
  255. def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
  256. r"""Return full path to the user-specific cache dir for this application.
  257. "appname" is the name of application.
  258. If None, just the system directory is returned.
  259. "appauthor" (only used on Windows) is the name of the
  260. appauthor or distributing body for this application. Typically
  261. it is the owning company name. This falls back to appname. You may
  262. pass False to disable it.
  263. "version" is an optional version path element to append to the
  264. path. You might want to use this if you want multiple versions
  265. of your app to be able to run independently. If used, this
  266. would typically be "<major>.<minor>".
  267. Only applied when appname is present.
  268. "opinion" (boolean) can be False to disable the appending of
  269. "Cache" to the base app data dir for Windows. See
  270. discussion below.
  271. Typical user cache directories are:
  272. Mac OS X: ~/Library/Caches/<AppName>
  273. Unix: ~/.cache/<AppName> (XDG default)
  274. Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
  275. Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
  276. On Windows the only suggestion in the MSDN docs is that local settings go in
  277. the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
  278. app data dir (the default returned by `user_data_dir` above). Apps typically
  279. put cache data somewhere *under* the given dir here. Some examples:
  280. ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
  281. ...\Acme\SuperApp\Cache\1.0
  282. OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
  283. This can be disabled with the `opinion=False` option.
  284. """
  285. if system == "win32":
  286. if appauthor is None:
  287. appauthor = appname
  288. path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
  289. if appname:
  290. if appauthor is not False:
  291. path = os.path.join(path, appauthor, appname)
  292. else:
  293. path = os.path.join(path, appname)
  294. if opinion:
  295. path = os.path.join(path, "Cache")
  296. elif system == "darwin":
  297. path = os.path.expanduser("~/Library/Caches")
  298. if appname:
  299. path = os.path.join(path, appname)
  300. else:
  301. path = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
  302. if appname:
  303. path = os.path.join(path, appname)
  304. if appname and version:
  305. path = os.path.join(path, version)
  306. return path
  307. def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
  308. r"""Return full path to the user-specific state dir for this application.
  309. "appname" is the name of application.
  310. If None, just the system directory is returned.
  311. "appauthor" (only used on Windows) is the name of the
  312. appauthor or distributing body for this application. Typically
  313. it is the owning company name. This falls back to appname. You may
  314. pass False to disable it.
  315. "version" is an optional version path element to append to the
  316. path. You might want to use this if you want multiple versions
  317. of your app to be able to run independently. If used, this
  318. would typically be "<major>.<minor>".
  319. Only applied when appname is present.
  320. "roaming" (boolean, default False) can be set True to use the Windows
  321. roaming appdata directory. That means that for users on a Windows
  322. network setup for roaming profiles, this user data will be
  323. sync'd on login. See
  324. <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
  325. for a discussion of issues.
  326. Typical user state directories are:
  327. Mac OS X: same as user_data_dir
  328. Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined
  329. Win *: same as user_data_dir
  330. For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
  331. to extend the XDG spec and support $XDG_STATE_HOME.
  332. That means, by default "~/.local/state/<AppName>".
  333. """
  334. if system in ["win32", "darwin"]:
  335. path = user_data_dir(appname, appauthor, None, roaming)
  336. else:
  337. path = os.getenv("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
  338. if appname:
  339. path = os.path.join(path, appname)
  340. if appname and version:
  341. path = os.path.join(path, version)
  342. return path
  343. def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
  344. r"""Return full path to the user-specific log dir for this application.
  345. "appname" is the name of application.
  346. If None, just the system directory is returned.
  347. "appauthor" (only used on Windows) is the name of the
  348. appauthor or distributing body for this application. Typically
  349. it is the owning company name. This falls back to appname. You may
  350. pass False to disable it.
  351. "version" is an optional version path element to append to the
  352. path. You might want to use this if you want multiple versions
  353. of your app to be able to run independently. If used, this
  354. would typically be "<major>.<minor>".
  355. Only applied when appname is present.
  356. "opinion" (boolean) can be False to disable the appending of
  357. "Logs" to the base app data dir for Windows, and "log" to the
  358. base cache dir for Unix. See discussion below.
  359. Typical user log directories are:
  360. Mac OS X: ~/Library/Logs/<AppName>
  361. Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
  362. Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
  363. Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
  364. On Windows the only suggestion in the MSDN docs is that local settings
  365. go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
  366. examples of what some windows apps use for a logs dir.)
  367. OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
  368. value for Windows and appends "log" to the user cache dir for Unix.
  369. This can be disabled with the `opinion=False` option.
  370. """
  371. if system == "darwin":
  372. path = os.path.join(os.path.expanduser("~/Library/Logs"), appname)
  373. elif system == "win32":
  374. path = user_data_dir(appname, appauthor, version)
  375. version = False
  376. if opinion:
  377. path = os.path.join(path, "Logs")
  378. else:
  379. path = user_cache_dir(appname, appauthor, version)
  380. version = False
  381. if opinion:
  382. path = os.path.join(path, "log")
  383. if appname and version:
  384. path = os.path.join(path, version)
  385. return path
  386. class AppDirs:
  387. """Convenience wrapper for getting application dirs."""
  388. def __init__(
  389. self, appname=None, appauthor=None, version=None, roaming=False, multipath=False
  390. ):
  391. self.appname = appname
  392. self.appauthor = appauthor
  393. self.version = version
  394. self.roaming = roaming
  395. self.multipath = multipath
  396. @property
  397. def user_data_dir(self):
  398. return user_data_dir(
  399. self.appname, self.appauthor, version=self.version, roaming=self.roaming
  400. )
  401. @property
  402. def site_data_dir(self):
  403. return site_data_dir(
  404. self.appname, self.appauthor, version=self.version, multipath=self.multipath
  405. )
  406. @property
  407. def user_config_dir(self):
  408. return user_config_dir(
  409. self.appname, self.appauthor, version=self.version, roaming=self.roaming
  410. )
  411. @property
  412. def site_config_dir(self):
  413. return site_config_dir(
  414. self.appname, self.appauthor, version=self.version, multipath=self.multipath
  415. )
  416. @property
  417. def user_cache_dir(self):
  418. return user_cache_dir(self.appname, self.appauthor, version=self.version)
  419. @property
  420. def user_state_dir(self):
  421. return user_state_dir(self.appname, self.appauthor, version=self.version)
  422. @property
  423. def user_log_dir(self):
  424. return user_log_dir(self.appname, self.appauthor, version=self.version)
  425. # ---- internal support stuff
  426. def _get_win_folder_from_registry(csidl_name):
  427. """This is a fallback technique at best. I'm not sure if using the
  428. registry for this guarantees us the correct answer for all CSIDL_*
  429. names.
  430. """
  431. import winreg as _winreg
  432. shell_folder_name = {
  433. "CSIDL_APPDATA": "AppData",
  434. "CSIDL_COMMON_APPDATA": "Common AppData",
  435. "CSIDL_LOCAL_APPDATA": "Local AppData",
  436. }[csidl_name]
  437. key = _winreg.OpenKey(
  438. _winreg.HKEY_CURRENT_USER,
  439. r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders",
  440. )
  441. dir, _type = _winreg.QueryValueEx(key, shell_folder_name)
  442. return dir
  443. def _get_win_folder_with_pywin32(csidl_name):
  444. from win32com.shell import shell, shellcon
  445. dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
  446. # Try to make this a unicode path because SHGetFolderPath does
  447. # not return unicode strings when there is unicode data in the
  448. # path.
  449. try:
  450. dir = unicode(dir)
  451. # Downgrade to short path name if have highbit chars. See
  452. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  453. has_high_char = False
  454. for c in dir:
  455. if ord(c) > 255:
  456. has_high_char = True
  457. break
  458. if has_high_char:
  459. try:
  460. import win32api
  461. dir = win32api.GetShortPathName(dir)
  462. except ImportError:
  463. pass
  464. except UnicodeError:
  465. pass
  466. return dir
  467. def _get_win_folder_with_ctypes(csidl_name):
  468. import ctypes
  469. csidl_const = {
  470. "CSIDL_APPDATA": 26,
  471. "CSIDL_COMMON_APPDATA": 35,
  472. "CSIDL_LOCAL_APPDATA": 28,
  473. }[csidl_name]
  474. buf = ctypes.create_unicode_buffer(1024)
  475. ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
  476. # Downgrade to short path name if have highbit chars. See
  477. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  478. has_high_char = False
  479. for c in buf:
  480. if ord(c) > 255:
  481. has_high_char = True
  482. break
  483. if has_high_char:
  484. buf2 = ctypes.create_unicode_buffer(1024)
  485. if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
  486. buf = buf2
  487. return buf.value
  488. def _get_win_folder_with_jna(csidl_name):
  489. import array
  490. from com.sun import jna
  491. from com.sun.jna.platform import win32
  492. buf_size = win32.WinDef.MAX_PATH * 2
  493. buf = array.zeros("c", buf_size)
  494. shell = win32.Shell32.INSTANCE
  495. shell.SHGetFolderPath(
  496. None,
  497. getattr(win32.ShlObj, csidl_name),
  498. None,
  499. win32.ShlObj.SHGFP_TYPE_CURRENT,
  500. buf,
  501. )
  502. dir = jna.Native.toString(buf.tostring()).rstrip("\0")
  503. # Downgrade to short path name if have highbit chars. See
  504. # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
  505. has_high_char = False
  506. for c in dir:
  507. if ord(c) > 255:
  508. has_high_char = True
  509. break
  510. if has_high_char:
  511. buf = array.zeros("c", buf_size)
  512. kernel = win32.Kernel32.INSTANCE
  513. if kernel.GetShortPathName(dir, buf, buf_size):
  514. dir = jna.Native.toString(buf.tostring()).rstrip("\0")
  515. return dir
  516. if system == "win32":
  517. try:
  518. import win32com.shell
  519. _get_win_folder = _get_win_folder_with_pywin32
  520. except ImportError:
  521. try:
  522. from ctypes import windll
  523. _get_win_folder = _get_win_folder_with_ctypes
  524. except ImportError:
  525. try:
  526. import com.sun.jna
  527. _get_win_folder = _get_win_folder_with_jna
  528. except ImportError:
  529. _get_win_folder = _get_win_folder_from_registry
  530. # ---- self test code
  531. if __name__ == "__main__":
  532. appname = "MyApp"
  533. appauthor = "MyCompany"
  534. props = (
  535. "user_data_dir",
  536. "user_config_dir",
  537. "user_cache_dir",
  538. "user_state_dir",
  539. "user_log_dir",
  540. "site_data_dir",
  541. "site_config_dir",
  542. )
  543. print(f"-- app dirs {__version__} --")
  544. print("-- app dirs (with optional 'version')")
  545. dirs = AppDirs(appname, appauthor, version="1.0")
  546. for prop in props:
  547. print(f"{prop}: {getattr(dirs, prop)}")
  548. print("\n-- app dirs (without optional 'version')")
  549. dirs = AppDirs(appname, appauthor)
  550. for prop in props:
  551. print(f"{prop}: {getattr(dirs, prop)}")
  552. print("\n-- app dirs (without optional 'appauthor')")
  553. dirs = AppDirs(appname)
  554. for prop in props:
  555. print(f"{prop}: {getattr(dirs, prop)}")
  556. print("\n-- app dirs (with disabled 'appauthor')")
  557. dirs = AppDirs(appname, appauthor=False)
  558. for prop in props:
  559. print(f"{prop}: {getattr(dirs, prop)}")