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'
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 site 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 config 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 default "~/.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 site config 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_state_dir(appname=None, appauthor=None, version=None, roaming=False):
315    r"""Return full path to the user-specific state 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        "roaming" (boolean, default False) can be set True to use the Windows
329            roaming appdata directory. That means that for users on a Windows
330            network setup for roaming profiles, this user data will be
331            sync'd on login. See
332            <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
333            for a discussion of issues.
334
335    Typical user state directories are:
336        Mac OS X:  same as user_data_dir
337        Unix:      ~/.local/state/<AppName>   # or in $XDG_STATE_HOME, if defined
338        Win *:     same as user_data_dir
339
340    For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
341    to extend the XDG spec and support $XDG_STATE_HOME.
342
343    That means, by default "~/.local/state/<AppName>".
344    """
345    if system in ["win32", "darwin"]:
346        path = user_data_dir(appname, appauthor, None, roaming)
347    else:
348        path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state"))
349        if appname:
350            path = os.path.join(path, appname)
351    if appname and version:
352        path = os.path.join(path, version)
353    return path
354
355
356def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
357    r"""Return full path to the user-specific log dir for this application.
358
359        "appname" is the name of application.
360            If None, just the system directory is returned.
361        "appauthor" (only used on Windows) is the name of the
362            appauthor or distributing body for this application. Typically
363            it is the owning company name. This falls back to appname. You may
364            pass False to disable it.
365        "version" is an optional version path element to append to the
366            path. You might want to use this if you want multiple versions
367            of your app to be able to run independently. If used, this
368            would typically be "<major>.<minor>".
369            Only applied when appname is present.
370        "opinion" (boolean) can be False to disable the appending of
371            "Logs" to the base app data dir for Windows, and "log" to the
372            base cache dir for Unix. See discussion below.
373
374    Typical user log directories are:
375        Mac OS X:   ~/Library/Logs/<AppName>
376        Unix:       ~/.cache/<AppName>/log  # or under $XDG_CACHE_HOME if defined
377        Win XP:     C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
378        Vista:      C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
379
380    On Windows the only suggestion in the MSDN docs is that local settings
381    go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
382    examples of what some windows apps use for a logs dir.)
383
384    OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
385    value for Windows and appends "log" to the user cache dir for Unix.
386    This can be disabled with the `opinion=False` option.
387    """
388    if system == "darwin":
389        path = os.path.join(
390            os.path.expanduser('~/Library/Logs'),
391            appname)
392    elif system == "win32":
393        path = user_data_dir(appname, appauthor, version)
394        version = False
395        if opinion:
396            path = os.path.join(path, "Logs")
397    else:
398        path = user_cache_dir(appname, appauthor, version)
399        version = False
400        if opinion:
401            path = os.path.join(path, "log")
402    if appname and version:
403        path = os.path.join(path, version)
404    return path
405
406
407class AppDirs(object):
408    """Convenience wrapper for getting application dirs."""
409    def __init__(self, appname=None, appauthor=None, version=None,
410            roaming=False, multipath=False):
411        self.appname = appname
412        self.appauthor = appauthor
413        self.version = version
414        self.roaming = roaming
415        self.multipath = multipath
416
417    @property
418    def user_data_dir(self):
419        return user_data_dir(self.appname, self.appauthor,
420                             version=self.version, roaming=self.roaming)
421
422    @property
423    def site_data_dir(self):
424        return site_data_dir(self.appname, self.appauthor,
425                             version=self.version, multipath=self.multipath)
426
427    @property
428    def user_config_dir(self):
429        return user_config_dir(self.appname, self.appauthor,
430                               version=self.version, roaming=self.roaming)
431
432    @property
433    def site_config_dir(self):
434        return site_config_dir(self.appname, self.appauthor,
435                             version=self.version, multipath=self.multipath)
436
437    @property
438    def user_cache_dir(self):
439        return user_cache_dir(self.appname, self.appauthor,
440                              version=self.version)
441
442    @property
443    def user_state_dir(self):
444        return user_state_dir(self.appname, self.appauthor,
445                              version=self.version)
446
447    @property
448    def user_log_dir(self):
449        return user_log_dir(self.appname, self.appauthor,
450                            version=self.version)
451
452
453#---- internal support stuff
454
455def _get_win_folder_from_registry(csidl_name):
456    """This is a fallback technique at best. I'm not sure if using the
457    registry for this guarantees us the correct answer for all CSIDL_*
458    names.
459    """
460    if PY3:
461      import winreg as _winreg
462    else:
463      import _winreg
464
465    shell_folder_name = {
466        "CSIDL_APPDATA": "AppData",
467        "CSIDL_COMMON_APPDATA": "Common AppData",
468        "CSIDL_LOCAL_APPDATA": "Local AppData",
469    }[csidl_name]
470
471    key = _winreg.OpenKey(
472        _winreg.HKEY_CURRENT_USER,
473        r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
474    )
475    dir, type = _winreg.QueryValueEx(key, shell_folder_name)
476    return dir
477
478
479def _get_win_folder_with_pywin32(csidl_name):
480    from win32com.shell import shellcon, shell
481    dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
482    # Try to make this a unicode path because SHGetFolderPath does
483    # not return unicode strings when there is unicode data in the
484    # path.
485    try:
486        dir = unicode(dir)
487
488        # Downgrade to short path name if have highbit chars. See
489        # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
490        has_high_char = False
491        for c in dir:
492            if ord(c) > 255:
493                has_high_char = True
494                break
495        if has_high_char:
496            try:
497                import win32api
498                dir = win32api.GetShortPathName(dir)
499            except ImportError:
500                pass
501    except UnicodeError:
502        pass
503    return dir
504
505
506def _get_win_folder_with_ctypes(csidl_name):
507    import ctypes
508
509    csidl_const = {
510        "CSIDL_APPDATA": 26,
511        "CSIDL_COMMON_APPDATA": 35,
512        "CSIDL_LOCAL_APPDATA": 28,
513    }[csidl_name]
514
515    buf = ctypes.create_unicode_buffer(1024)
516    ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
517
518    # Downgrade to short path name if have highbit chars. See
519    # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
520    has_high_char = False
521    for c in buf:
522        if ord(c) > 255:
523            has_high_char = True
524            break
525    if has_high_char:
526        buf2 = ctypes.create_unicode_buffer(1024)
527        if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
528            buf = buf2
529
530    return buf.value
531
532def _get_win_folder_with_jna(csidl_name):
533    import array
534    from com.sun import jna
535    from com.sun.jna.platform import win32
536
537    buf_size = win32.WinDef.MAX_PATH * 2
538    buf = array.zeros('c', buf_size)
539    shell = win32.Shell32.INSTANCE
540    shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
541    dir = jna.Native.toString(buf.tostring()).rstrip("\0")
542
543    # Downgrade to short path name if have highbit chars. See
544    # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
545    has_high_char = False
546    for c in dir:
547        if ord(c) > 255:
548            has_high_char = True
549            break
550    if has_high_char:
551        buf = array.zeros('c', buf_size)
552        kernel = win32.Kernel32.INSTANCE
553        if kernel.GetShortPathName(dir, buf, buf_size):
554            dir = jna.Native.toString(buf.tostring()).rstrip("\0")
555
556    return dir
557
558if system == "win32":
559    try:
560        import win32com.shell
561        _get_win_folder = _get_win_folder_with_pywin32
562    except ImportError:
563        try:
564            from ctypes import windll
565            _get_win_folder = _get_win_folder_with_ctypes
566        except ImportError:
567            try:
568                import com.sun.jna
569                _get_win_folder = _get_win_folder_with_jna
570            except ImportError:
571                _get_win_folder = _get_win_folder_from_registry
572
573
574#---- self test code
575
576if __name__ == "__main__":
577    appname = "MyApp"
578    appauthor = "MyCompany"
579
580    props = ("user_data_dir",
581             "user_config_dir",
582             "user_cache_dir",
583             "user_state_dir",
584             "user_log_dir",
585             "site_data_dir",
586             "site_config_dir")
587
588    print("-- app dirs %s --" % __version__)
589
590    print("-- app dirs (with optional 'version')")
591    dirs = AppDirs(appname, appauthor, version="1.0")
592    for prop in props:
593        print("%s: %s" % (prop, getattr(dirs, prop)))
594
595    print("\n-- app dirs (without optional 'version')")
596    dirs = AppDirs(appname, appauthor)
597    for prop in props:
598        print("%s: %s" % (prop, getattr(dirs, prop)))
599
600    print("\n-- app dirs (without optional 'appauthor')")
601    dirs = AppDirs(appname)
602    for prop in props:
603        print("%s: %s" % (prop, getattr(dirs, prop)))
604
605    print("\n-- app dirs (with disabled 'appauthor')")
606    dirs = AppDirs(appname, appauthor=False)
607    for prop in props:
608        print("%s: %s" % (prop, getattr(dirs, prop)))
609