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