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