Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

6"""Utilities for determining application-specific dirs. 

7 

8See <http://github.com/ActiveState/appdirs> for details and usage. 

9""" 

10# Dev Notes: 

11# - MSDN on where to store app data files: 

12# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 

13# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html 

14# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 

15 

16__version_info__ = (1, 4, 0) 

17__version__ = '.'.join(map(str, __version_info__)) 

18 

19 

20import sys 

21import os 

22 

23PY3 = sys.version_info[0] == 3 

24 

25if PY3: 

26 unicode = str 

27 

28if sys.platform.startswith('java'): 

29 import platform 

30 os_name = platform.java_ver()[3][0] 

31 if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. 

32 system = 'win32' 

33 elif os_name.startswith('Mac'): # "Mac OS X", etc. 

34 system = 'darwin' 

35 else: # "Linux", "SunOS", "FreeBSD", etc. 

36 # Setting this to "linux2" is not ideal, but only Windows or Mac 

37 # are actually checked for and the rest of the module expects 

38 # *sys.platform* style strings. 

39 system = 'linux2' 

40else: 

41 system = sys.platform 

42 

43 

44 

45def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): 

46 r"""Return full path to the user-specific data dir for this application. 

47 

48 "appname" is the name of application. 

49 If None, just the system directory is returned. 

50 "appauthor" (only used on Windows) is the name of the 

51 appauthor or distributing body for this application. Typically 

52 it is the owning company name. This falls back to appname. You may 

53 pass False to disable it. 

54 "version" is an optional version path element to append to the 

55 path. You might want to use this if you want multiple versions 

56 of your app to be able to run independently. If used, this 

57 would typically be "<major>.<minor>". 

58 Only applied when appname is present. 

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 

66 Typical user data directories are: 

67 Mac OS X: ~/Library/Application Support/<AppName> 

68 Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined 

69 Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName> 

70 Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> 

71 Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> 

72 Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> 

73 

74 For Unix, we follow the XDG spec and support $XDG_DATA_HOME. 

75 That means, by default "~/.local/share/<AppName>". 

76 """ 

77 if system == "win32": 

78 if appauthor is None: 

79 appauthor = appname 

80 const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" 

81 path = os.path.normpath(_get_win_folder(const)) 

82 if appname: 

83 if appauthor is not False: 

84 path = os.path.join(path, appauthor, appname) 

85 else: 

86 path = os.path.join(path, appname) 

87 elif system == 'darwin': 

88 path = os.path.expanduser('~/Library/Application Support/') 

89 if appname: 

90 path = os.path.join(path, appname) 

91 else: 

92 path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) 

93 if appname: 

94 path = os.path.join(path, appname) 

95 if appname and version: 

96 path = os.path.join(path, version) 

97 return path 

98 

99 

100def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): 

101 r"""Return full path to the user-shared data dir for this application. 

102 

103 "appname" is the name of application. 

104 If None, just the system directory is returned. 

105 "appauthor" (only used on Windows) is the name of the 

106 appauthor or distributing body for this application. Typically 

107 it is the owning company name. This falls back to appname. You may 

108 pass False to disable it. 

109 "version" is an optional version path element to append to the 

110 path. You might want to use this if you want multiple versions 

111 of your app to be able to run independently. If used, this 

112 would typically be "<major>.<minor>". 

113 Only applied when appname is present. 

114 "multipath" is an optional parameter only applicable to *nix 

115 which indicates that the entire list of data dirs should be 

116 returned. By default, the first item from XDG_DATA_DIRS is 

117 returned, or '/usr/local/share/<AppName>', 

118 if XDG_DATA_DIRS is not set 

119 

120 Typical user data directories are: 

121 Mac OS X: /Library/Application Support/<AppName> 

122 Unix: /usr/local/share/<AppName> or /usr/share/<AppName> 

123 Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> 

124 Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) 

125 Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. 

126 

127 For Unix, this is using the $XDG_DATA_DIRS[0] default. 

128 

129 WARNING: Do not use this on Windows. See the Vista-Fail note above for why. 

130 """ 

131 if system == "win32": 

132 if appauthor is None: 

133 appauthor = appname 

134 path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) 

135 if appname: 

136 if appauthor is not False: 

137 path = os.path.join(path, appauthor, appname) 

138 else: 

139 path = os.path.join(path, appname) 

140 elif system == 'darwin': 

141 path = os.path.expanduser('/Library/Application Support') 

142 if appname: 

143 path = os.path.join(path, appname) 

144 else: 

145 # XDG default for $XDG_DATA_DIRS 

146 # only first, if multipath is False 

147 path = os.getenv('XDG_DATA_DIRS', 

148 os.pathsep.join(['/usr/local/share', '/usr/share'])) 

149 pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] 

150 if appname: 

151 if version: 

152 appname = os.path.join(appname, version) 

153 pathlist = [os.sep.join([x, appname]) for x in pathlist] 

154 

155 if multipath: 

156 path = os.pathsep.join(pathlist) 

157 else: 

158 path = pathlist[0] 

159 return path 

160 

161 if appname and version: 

162 path = os.path.join(path, version) 

163 return path 

164 

165 

166def 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 

169 "appname" is the name of application. 

170 If None, just the system directory is returned. 

171 "appauthor" (only used on Windows) is the name of the 

172 appauthor or distributing body for this application. Typically 

173 it is the owning company name. This falls back to appname. You may 

174 pass False to disable it. 

175 "version" is an optional version path element to append to the 

176 path. You might want to use this if you want multiple versions 

177 of your app to be able to run independently. If used, this 

178 would typically be "<major>.<minor>". 

179 Only applied when appname is present. 

180 "roaming" (boolean, default False) can be set True to use the Windows 

181 roaming appdata directory. That means that for users on a Windows 

182 network setup for roaming profiles, this user data will be 

183 sync'd on login. See 

184 <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> 

185 for a discussion of issues. 

186 

187 Typical user data directories are: 

188 Mac OS X: same as user_data_dir 

189 Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined 

190 Win *: same as user_data_dir 

191 

192 For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. 

193 That means, by deafult "~/.config/<AppName>". 

194 """ 

195 if system in ["win32", "darwin"]: 

196 path = user_data_dir(appname, appauthor, None, roaming) 

197 else: 

198 path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) 

199 if appname: 

200 path = os.path.join(path, appname) 

201 if appname and version: 

202 path = os.path.join(path, version) 

203 return path 

204 

205 

206def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): 

207 r"""Return full path to the user-shared data dir for this application. 

208 

209 "appname" is the name of application. 

210 If None, just the system directory is returned. 

211 "appauthor" (only used on Windows) is the name of the 

212 appauthor or distributing body for this application. Typically 

213 it is the owning company name. This falls back to appname. You may 

214 pass False to disable it. 

215 "version" is an optional version path element to append to the 

216 path. You might want to use this if you want multiple versions 

217 of your app to be able to run independently. If used, this 

218 would typically be "<major>.<minor>". 

219 Only applied when appname is present. 

220 "multipath" is an optional parameter only applicable to *nix 

221 which indicates that the entire list of config dirs should be 

222 returned. By default, the first item from XDG_CONFIG_DIRS is 

223 returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set 

224 

225 Typical user data directories are: 

226 Mac OS X: same as site_data_dir 

227 Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in 

228 $XDG_CONFIG_DIRS 

229 Win *: same as site_data_dir 

230 Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) 

231 

232 For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False 

233 

234 WARNING: Do not use this on Windows. See the Vista-Fail note above for why. 

235 """ 

236 if system in ["win32", "darwin"]: 

237 path = site_data_dir(appname, appauthor) 

238 if appname and version: 

239 path = os.path.join(path, version) 

240 else: 

241 # XDG default for $XDG_CONFIG_DIRS 

242 # only first, if multipath is False 

243 path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') 

244 pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] 

245 if appname: 

246 if version: 

247 appname = os.path.join(appname, version) 

248 pathlist = [os.sep.join([x, appname]) for x in pathlist] 

249 

250 if multipath: 

251 path = os.pathsep.join(pathlist) 

252 else: 

253 path = pathlist[0] 

254 return path 

255 

256 

257def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): 

258 r"""Return full path to the user-specific cache dir for this application. 

259 

260 "appname" is the name of application. 

261 If None, just the system directory is returned. 

262 "appauthor" (only used on Windows) is the name of the 

263 appauthor or distributing body for this application. Typically 

264 it is the owning company name. This falls back to appname. You may 

265 pass False to disable it. 

266 "version" is an optional version path element to append to the 

267 path. You might want to use this if you want multiple versions 

268 of your app to be able to run independently. If used, this 

269 would typically be "<major>.<minor>". 

270 Only applied when appname is present. 

271 "opinion" (boolean) can be False to disable the appending of 

272 "Cache" to the base app data dir for Windows. See 

273 discussion below. 

274 

275 Typical user cache directories are: 

276 Mac OS X: ~/Library/Caches/<AppName> 

277 Unix: ~/.cache/<AppName> (XDG default) 

278 Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache 

279 Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache 

280 

281 On Windows the only suggestion in the MSDN docs is that local settings go in 

282 the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming 

283 app data dir (the default returned by `user_data_dir` above). Apps typically 

284 put cache data somewhere *under* the given dir here. Some examples: 

285 ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache 

286 ...\Acme\SuperApp\Cache\1.0 

287 OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. 

288 This can be disabled with the `opinion=False` option. 

289 """ 

290 if system == "win32": 

291 if appauthor is None: 

292 appauthor = appname 

293 path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) 

294 if appname: 

295 if appauthor is not False: 

296 path = os.path.join(path, appauthor, appname) 

297 else: 

298 path = os.path.join(path, appname) 

299 if opinion: 

300 path = os.path.join(path, "Cache") 

301 elif system == 'darwin': 

302 path = os.path.expanduser('~/Library/Caches') 

303 if appname: 

304 path = os.path.join(path, appname) 

305 else: 

306 path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) 

307 if appname: 

308 path = os.path.join(path, appname) 

309 if appname and version: 

310 path = os.path.join(path, version) 

311 return path 

312 

313 

314def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): 

315 r"""Return full path to the user-specific log dir for this application. 

316 

317 "appname" is the name of application. 

318 If None, just the system directory is returned. 

319 "appauthor" (only used on Windows) is the name of the 

320 appauthor or distributing body for this application. Typically 

321 it is the owning company name. This falls back to appname. You may 

322 pass False to disable it. 

323 "version" is an optional version path element to append to the 

324 path. You might want to use this if you want multiple versions 

325 of your app to be able to run independently. If used, this 

326 would typically be "<major>.<minor>". 

327 Only applied when appname is present. 

328 "opinion" (boolean) can be False to disable the appending of 

329 "Logs" to the base app data dir for Windows, and "log" to the 

330 base cache dir for Unix. See discussion below. 

331 

332 Typical user cache directories are: 

333 Mac OS X: ~/Library/Logs/<AppName> 

334 Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined 

335 Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs 

336 Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs 

337 

338 On Windows the only suggestion in the MSDN docs is that local settings 

339 go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in 

340 examples of what some windows apps use for a logs dir.) 

341 

342 OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` 

343 value for Windows and appends "log" to the user cache dir for Unix. 

344 This can be disabled with the `opinion=False` option. 

345 """ 

346 if system == "darwin": 

347 path = os.path.join( 

348 os.path.expanduser('~/Library/Logs'), 

349 appname) 

350 elif system == "win32": 

351 path = user_data_dir(appname, appauthor, version) 

352 version = False 

353 if opinion: 

354 path = os.path.join(path, "Logs") 

355 else: 

356 path = user_cache_dir(appname, appauthor, version) 

357 version = False 

358 if opinion: 

359 path = os.path.join(path, "log") 

360 if appname and version: 

361 path = os.path.join(path, version) 

362 return path 

363 

364 

365class AppDirs(object): 

366 """Convenience wrapper for getting application dirs.""" 

367 def __init__(self, appname, appauthor=None, version=None, roaming=False, 

368 multipath=False): 

369 self.appname = appname 

370 self.appauthor = appauthor 

371 self.version = version 

372 self.roaming = roaming 

373 self.multipath = multipath 

374 

375 @property 

376 def user_data_dir(self): 

377 return user_data_dir(self.appname, self.appauthor, 

378 version=self.version, roaming=self.roaming) 

379 

380 @property 

381 def site_data_dir(self): 

382 return site_data_dir(self.appname, self.appauthor, 

383 version=self.version, multipath=self.multipath) 

384 

385 @property 

386 def user_config_dir(self): 

387 return user_config_dir(self.appname, self.appauthor, 

388 version=self.version, roaming=self.roaming) 

389 

390 @property 

391 def site_config_dir(self): 

392 return site_config_dir(self.appname, self.appauthor, 

393 version=self.version, multipath=self.multipath) 

394 

395 @property 

396 def user_cache_dir(self): 

397 return user_cache_dir(self.appname, self.appauthor, 

398 version=self.version) 

399 

400 @property 

401 def user_log_dir(self): 

402 return user_log_dir(self.appname, self.appauthor, 

403 version=self.version) 

404 

405 

406#---- internal support stuff 

407 

408def _get_win_folder_from_registry(csidl_name): 

409 """This is a fallback technique at best. I'm not sure if using the 

410 registry for this guarantees us the correct answer for all CSIDL_* 

411 names. 

412 """ 

413 import _winreg 

414 

415 shell_folder_name = { 

416 "CSIDL_APPDATA": "AppData", 

417 "CSIDL_COMMON_APPDATA": "Common AppData", 

418 "CSIDL_LOCAL_APPDATA": "Local AppData", 

419 }[csidl_name] 

420 

421 key = _winreg.OpenKey( 

422 _winreg.HKEY_CURRENT_USER, 

423 r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" 

424 ) 

425 dir, type = _winreg.QueryValueEx(key, shell_folder_name) 

426 return dir 

427 

428 

429def _get_win_folder_with_pywin32(csidl_name): 

430 from win32com.shell import shellcon, shell 

431 dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) 

432 # Try to make this a unicode path because SHGetFolderPath does 

433 # not return unicode strings when there is unicode data in the 

434 # path. 

435 try: 

436 dir = unicode(dir) 

437 

438 # Downgrade to short path name if have highbit chars. See 

439 # <http://bugs.activestate.com/show_bug.cgi?id=85099>. 

440 has_high_char = False 

441 for c in dir: 

442 if ord(c) > 255: 

443 has_high_char = True 

444 break 

445 if has_high_char: 

446 try: 

447 import win32api 

448 dir = win32api.GetShortPathName(dir) 

449 except ImportError: 

450 pass 

451 except UnicodeError: 

452 pass 

453 return dir 

454 

455 

456def _get_win_folder_with_ctypes(csidl_name): 

457 import ctypes 

458 

459 csidl_const = { 

460 "CSIDL_APPDATA": 26, 

461 "CSIDL_COMMON_APPDATA": 35, 

462 "CSIDL_LOCAL_APPDATA": 28, 

463 }[csidl_name] 

464 

465 buf = ctypes.create_unicode_buffer(1024) 

466 ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) 

467 

468 # Downgrade to short path name if have highbit chars. See 

469 # <http://bugs.activestate.com/show_bug.cgi?id=85099>. 

470 has_high_char = False 

471 for c in buf: 

472 if ord(c) > 255: 

473 has_high_char = True 

474 break 

475 if has_high_char: 

476 buf2 = ctypes.create_unicode_buffer(1024) 

477 if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): 

478 buf = buf2 

479 

480 return buf.value 

481 

482def _get_win_folder_with_jna(csidl_name): 

483 import array 

484 from com.sun import jna 

485 from com.sun.jna.platform import win32 

486 

487 buf_size = win32.WinDef.MAX_PATH * 2 

488 buf = array.zeros('c', buf_size) 

489 shell = win32.Shell32.INSTANCE 

490 shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) 

491 dir = jna.Native.toString(buf.tostring()).rstrip("\0") 

492 

493 # Downgrade to short path name if have highbit chars. See 

494 # <http://bugs.activestate.com/show_bug.cgi?id=85099>. 

495 has_high_char = False 

496 for c in dir: 

497 if ord(c) > 255: 

498 has_high_char = True 

499 break 

500 if has_high_char: 

501 buf = array.zeros('c', buf_size) 

502 kernel = win32.Kernel32.INSTANCE 

503 if kernal.GetShortPathName(dir, buf, buf_size): 

504 dir = jna.Native.toString(buf.tostring()).rstrip("\0") 

505 

506 return dir 

507 

508if system == "win32": 

509 try: 

510 import win32com.shell 

511 _get_win_folder = _get_win_folder_with_pywin32 

512 except ImportError: 

513 try: 

514 from ctypes import windll 

515 _get_win_folder = _get_win_folder_with_ctypes 

516 except ImportError: 

517 try: 

518 import com.sun.jna 

519 _get_win_folder = _get_win_folder_with_jna 

520 except ImportError: 

521 _get_win_folder = _get_win_folder_from_registry 

522 

523 

524#---- self test code 

525 

526if __name__ == "__main__": 

527 appname = "MyApp" 

528 appauthor = "MyCompany" 

529 

530 props = ("user_data_dir", "site_data_dir", 

531 "user_config_dir", "site_config_dir", 

532 "user_cache_dir", "user_log_dir") 

533 

534 print("-- app dirs (with optional 'version')") 

535 dirs = AppDirs(appname, appauthor, version="1.0") 

536 for prop in props: 

537 print("%s: %s" % (prop, getattr(dirs, prop))) 

538 

539 print("\n-- app dirs (without optional 'version')") 

540 dirs = AppDirs(appname, appauthor) 

541 for prop in props: 

542 print("%s: %s" % (prop, getattr(dirs, prop))) 

543 

544 print("\n-- app dirs (without optional 'appauthor')") 

545 dirs = AppDirs(appname) 

546 for prop in props: 

547 print("%s: %s" % (prop, getattr(dirs, prop))) 

548 

549 print("\n-- app dirs (with disabled 'appauthor')") 

550 dirs = AppDirs(appname, appauthor=False) 

551 for prop in props: 

552 print("%s: %s" % (prop, getattr(dirs, prop)))