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""" 10from __future__ import print_function 11# Dev Notes: 12# - MSDN on where to store app data files: 13# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 14# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html 15# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 16 17__version_info__ = (1, 3, 0) 18__version__ = '.'.join(str(v) for v in __version_info__) 19 20 21import sys 22import os 23 24 25def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): 26 r"""Return full path to the user-specific data dir for this application. 27 28 "appname" is the name of application. 29 If None, just the system directory is returned. 30 "appauthor" (only required and used on Windows) is the name of the 31 appauthor or distributing body for this application. Typically 32 it is the owning company name. This falls back to appname. 33 "version" is an optional version path element to append to the 34 path. You might want to use this if you want multiple versions 35 of your app to be able to run independently. If used, this 36 would typically be "<major>.<minor>". 37 Only applied when appname is present. 38 "roaming" (boolean, default False) can be set True to use the Windows 39 roaming appdata directory. That means that for users on a Windows 40 network setup for roaming profiles, this user data will be 41 sync'd on login. See 42 <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> 43 for a discussion of issues. 44 45 Typical user data directories are: 46 Mac OS X: ~/Library/Application Support/<AppName> 47 Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined 48 Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName> 49 Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> 50 Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> 51 Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> 52 53 For Unix, we follow the XDG spec and support $XDG_DATA_HOME. 54 That means, by default "~/.local/share/<AppName>". 55 """ 56 if sys.platform == "win32": 57 if appauthor is None: 58 appauthor = appname 59 const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" 60 path = os.path.normpath(_get_win_folder(const)) 61 if appname: 62 path = os.path.join(path, appauthor, appname) 63 elif sys.platform == 'darwin': 64 path = os.path.expanduser('~/Library/Application Support/') 65 if appname: 66 path = os.path.join(path, appname) 67 else: 68 path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) 69 if appname: 70 path = os.path.join(path, appname) 71 if appname and version: 72 path = os.path.join(path, version) 73 return path 74 75 76def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): 77 """Return full path to the user-shared data dir for this application. 78 79 "appname" is the name of application. 80 If None, just the system directory is returned. 81 "appauthor" (only required and used on Windows) is the name of the 82 appauthor or distributing body for this application. Typically 83 it is the owning company name. This falls back to appname. 84 "version" is an optional version path element to append to the 85 path. You might want to use this if you want multiple versions 86 of your app to be able to run independently. If used, this 87 would typically be "<major>.<minor>". 88 Only applied when appname is present. 89 "multipath" is an optional parameter only applicable to *nix 90 which indicates that the entire list of data dirs should be 91 returned. By default, the first item from XDG_DATA_DIRS is 92 returned, or '/usr/local/share/<AppName>', 93 if XDG_DATA_DIRS is not set 94 95 Typical user data directories are: 96 Mac OS X: /Library/Application Support/<AppName> 97 Unix: /usr/local/share/<AppName> or /usr/share/<AppName> 98 Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> 99 Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) 100 Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. 101 102 For Unix, this is using the $XDG_DATA_DIRS[0] default. 103 104 WARNING: Do not use this on Windows. See the Vista-Fail note above for why. 105 """ 106 if sys.platform == "win32": 107 if appauthor is None: 108 appauthor = appname 109 path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) 110 if appname: 111 path = os.path.join(path, appauthor, appname) 112 elif sys.platform == 'darwin': 113 path = os.path.expanduser('/Library/Application Support') 114 if appname: 115 path = os.path.join(path, appname) 116 else: 117 # XDG default for $XDG_DATA_DIRS 118 # only first, if multipath is False 119 path = os.getenv('XDG_DATA_DIRS', 120 os.pathsep.join(['/usr/local/share', '/usr/share'])) 121 pathlist = [ os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) ] 122 if appname: 123 if version: 124 appname = os.path.join(appname, version) 125 pathlist = [ os.sep.join([x, appname]) for x in pathlist ] 126 127 if multipath: 128 path = os.pathsep.join(pathlist) 129 else: 130 path = pathlist[0] 131 return path 132 133 if appname and version: 134 path = os.path.join(path, version) 135 return path 136 137 138def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): 139 r"""Return full path to the user-specific config dir for this application. 140 141 "appname" is the name of application. 142 If None, just the system directory is returned. 143 "appauthor" (only required and used on Windows) is the name of the 144 appauthor or distributing body for this application. Typically 145 it is the owning company name. This falls back to appname. 146 "version" is an optional version path element to append to the 147 path. You might want to use this if you want multiple versions 148 of your app to be able to run independently. If used, this 149 would typically be "<major>.<minor>". 150 Only applied when appname is present. 151 "roaming" (boolean, default False) can be set True to use the Windows 152 roaming appdata directory. That means that for users on a Windows 153 network setup for roaming profiles, this user data will be 154 sync'd on login. See 155 <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> 156 for a discussion of issues. 157 158 Typical user data directories are: 159 Mac OS X: same as user_data_dir 160 Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined 161 Win *: same as user_data_dir 162 163 For Unix, we follow the XDG spec and support $XDG_DATA_HOME. 164 That means, by default "~/.local/share/<AppName>". 165 """ 166 if sys.platform in [ "win32", "darwin" ]: 167 path = user_data_dir(appname, appauthor, None, roaming) 168 else: 169 path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) 170 if appname: 171 path = os.path.join(path, appname) 172 if appname and version: 173 path = os.path.join(path, version) 174 return path 175 176 177def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): 178 """Return full path to the user-shared data dir for this application. 179 180 "appname" is the name of application. 181 If None, just the system directory is returned. 182 "appauthor" (only required and used on Windows) is the name of the 183 appauthor or distributing body for this application. Typically 184 it is the owning company name. This falls back to appname. 185 "version" is an optional version path element to append to the 186 path. You might want to use this if you want multiple versions 187 of your app to be able to run independently. If used, this 188 would typically be "<major>.<minor>". 189 Only applied when appname is present. 190 "multipath" is an optional parameter only applicable to *nix 191 which indicates that the entire list of config dirs should be 192 returned. By default, the first item from XDG_CONFIG_DIRS is 193 returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set 194 195 Typical user data directories are: 196 Mac OS X: same as site_data_dir 197 Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in 198 $XDG_CONFIG_DIRS 199 Win *: same as site_data_dir 200 Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) 201 202 For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False 203 204 WARNING: Do not use this on Windows. See the Vista-Fail note above for why. 205 """ 206 if sys.platform in [ "win32", "darwin" ]: 207 path = site_data_dir(appname, appauthor) 208 if appname and version: 209 path = os.path.join(path, version) 210 else: 211 # XDG default for $XDG_CONFIG_DIRS 212 # only first, if multipath is False 213 path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') 214 pathlist = [ os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) ] 215 if appname: 216 if version: 217 appname = os.path.join(appname, version) 218 pathlist = [ os.sep.join([x, appname]) for x in pathlist ] 219 220 if multipath: 221 path = os.pathsep.join(pathlist) 222 else: 223 path = pathlist[0] 224 return path 225 226def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): 227 r"""Return full path to the user-specific cache dir for this application. 228 229 "appname" is the name of application. 230 If None, just the system directory is returned. 231 "appauthor" (only required and used on Windows) is the name of the 232 appauthor or distributing body for this application. Typically 233 it is the owning company name. This falls back to appname. 234 "version" is an optional version path element to append to the 235 path. You might want to use this if you want multiple versions 236 of your app to be able to run independently. If used, this 237 would typically be "<major>.<minor>". 238 Only applied when appname is present. 239 "opinion" (boolean) can be False to disable the appending of 240 "Cache" to the base app data dir for Windows. See 241 discussion below. 242 243 Typical user cache directories are: 244 Mac OS X: ~/Library/Caches/<AppName> 245 Unix: ~/.cache/<AppName> (XDG default) 246 Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache 247 Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache 248 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 sys.platform == "win32": 259 if appauthor is None: 260 appauthor = appname 261 path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) 262 if appname: 263 path = os.path.join(path, appauthor, appname) 264 if opinion: 265 path = os.path.join(path, "Cache") 266 elif sys.platform == 'darwin': 267 path = os.path.expanduser('~/Library/Caches') 268 if appname: 269 path = os.path.join(path, appname) 270 else: 271 path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) 272 if appname: 273 path = os.path.join(path, appname) 274 if appname and version: 275 path = os.path.join(path, version) 276 return path 277 278def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): 279 r"""Return full path to the user-specific log dir for this application. 280 281 "appname" is the name of application. 282 If None, just the system directory is returned. 283 "appauthor" (only required and used on Windows) is the name of the 284 appauthor or distributing body for this application. Typically 285 it is the owning company name. This falls back to appname. 286 "version" is an optional version path element to append to the 287 path. You might want to use this if you want multiple versions 288 of your app to be able to run independently. If used, this 289 would typically be "<major>.<minor>". 290 Only applied when appname is present. 291 "opinion" (boolean) can be False to disable the appending of 292 "Logs" to the base app data dir for Windows, and "log" to the 293 base cache dir for Unix. See discussion below. 294 295 Typical user cache directories are: 296 Mac OS X: ~/Library/Logs/<AppName> 297 Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined 298 Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs 299 Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs 300 301 On Windows the only suggestion in the MSDN docs is that local settings 302 go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in 303 examples of what some windows apps use for a logs dir.) 304 305 OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` 306 value for Windows and appends "log" to the user cache dir for Unix. 307 This can be disabled with the `opinion=False` option. 308 """ 309 if sys.platform == "darwin": 310 path = os.path.join( 311 os.path.expanduser('~/Library/Logs'), 312 appname) 313 elif sys.platform == "win32": 314 path = user_data_dir(appname, appauthor, version); version=False 315 if opinion: 316 path = os.path.join(path, "Logs") 317 else: 318 path = user_cache_dir(appname, appauthor, version); version=False 319 if opinion: 320 path = os.path.join(path, "log") 321 if appname and version: 322 path = os.path.join(path, version) 323 return path 324 325 326class AppDirs(object): 327 """Convenience wrapper for getting application dirs.""" 328 def __init__(self, appname, appauthor=None, version=None, 329 roaming=False, multipath=False): 330 self.appname = appname 331 self.appauthor = appauthor 332 self.version = version 333 self.roaming = roaming 334 self.multipath = multipath 335 @property 336 def user_data_dir(self): 337 return user_data_dir(self.appname, self.appauthor, 338 version=self.version, roaming=self.roaming) 339 @property 340 def site_data_dir(self): 341 return site_data_dir(self.appname, self.appauthor, 342 version=self.version, multipath=self.multipath) 343 @property 344 def user_config_dir(self): 345 return user_config_dir(self.appname, self.appauthor, 346 version=self.version, roaming=self.roaming) 347 @property 348 def site_config_dir(self): 349 return site_data_dir(self.appname, self.appauthor, 350 version=self.version, multipath=self.multipath) 351 @property 352 def user_cache_dir(self): 353 return user_cache_dir(self.appname, self.appauthor, 354 version=self.version) 355 @property 356 def user_log_dir(self): 357 return user_log_dir(self.appname, self.appauthor, 358 version=self.version) 359 360 361 362 363#---- internal support stuff 364 365def _get_win_folder_from_registry(csidl_name): 366 """This is a fallback technique at best. I'm not sure if using the 367 registry for this guarantees us the correct answer for all CSIDL_* 368 names. 369 """ 370 import winreg as _winreg 371 372 shell_folder_name = { 373 "CSIDL_APPDATA": "AppData", 374 "CSIDL_COMMON_APPDATA": "Common AppData", 375 "CSIDL_LOCAL_APPDATA": "Local AppData", 376 }[csidl_name] 377 378 key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 379 r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") 380 dir, type = _winreg.QueryValueEx(key, shell_folder_name) 381 return dir 382 383def _get_win_folder_with_pywin32(csidl_name): 384 from win32com.shell import shellcon, shell 385 dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) 386 # Try to make this a unicode path because SHGetFolderPath does 387 # not return unicode strings when there is unicode data in the 388 # path. 389 try: 390 dir = str(dir) 391 392 # Downgrade to short path name if have highbit chars. See 393 # <http://bugs.activestate.com/show_bug.cgi?id=85099>. 394 has_high_char = False 395 for c in dir: 396 if ord(c) > 255: 397 has_high_char = True 398 break 399 if has_high_char: 400 try: 401 import win32api 402 dir = win32api.GetShortPathName(dir) 403 except ImportError: 404 pass 405 except UnicodeError: 406 pass 407 return dir 408 409def _get_win_folder_with_ctypes(csidl_name): 410 import ctypes 411 412 csidl_const = { 413 "CSIDL_APPDATA": 26, 414 "CSIDL_COMMON_APPDATA": 35, 415 "CSIDL_LOCAL_APPDATA": 28, 416 }[csidl_name] 417 418 buf = ctypes.create_unicode_buffer(1024) 419 ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) 420 421 # Downgrade to short path name if have highbit chars. See 422 # <http://bugs.activestate.com/show_bug.cgi?id=85099>. 423 has_high_char = False 424 for c in buf: 425 if ord(c) > 255: 426 has_high_char = True 427 break 428 if has_high_char: 429 buf2 = ctypes.create_unicode_buffer(1024) 430 if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): 431 buf = buf2 432 433 return buf.value 434 435if sys.platform == "win32": 436 try: 437 import win32com.shell 438 _get_win_folder = _get_win_folder_with_pywin32 439 except ImportError: 440 try: 441 import ctypes 442 _get_win_folder = _get_win_folder_with_ctypes 443 except ImportError: 444 _get_win_folder = _get_win_folder_from_registry 445 446 447 448#---- self test code 449 450if __name__ == "__main__": 451 appname = "MyApp" 452 appauthor = "MyCompany" 453 454 props = ("user_data_dir", "site_data_dir", 455 "user_config_dir", "site_config_dir", 456 "user_cache_dir", "user_log_dir") 457 458 print("-- app dirs (with optional 'version')") 459 dirs = AppDirs(appname, appauthor, version="1.0") 460 for prop in props: 461 print("%s: %s" % (prop, getattr(dirs, prop))) 462 463 print("\n-- app dirs (without optional 'version')") 464 dirs = AppDirs(appname, appauthor) 465 for prop in props: 466 print("%s: %s" % (prop, getattr(dirs, prop))) 467 468 print("\n-- app dirs (without optional 'appauthor')") 469 dirs = AppDirs(appname) 470 for prop in props: 471 print("%s: %s" % (prop, getattr(dirs, prop))) 472