xref: /aosp_15_r20/external/pytorch/torch/_appdirs.py (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright (c) 2005-2010 ActiveState Software Inc.
4# Copyright (c) 2013 Eddy Petrișor
5
6# flake8: noqa
7
8"""
9This file is directly from
10https://github.com/ActiveState/appdirs/blob/3fe6a83776843a46f20c2e5587afcffe05e03b39/appdirs.py
11
12The license of https://github.com/ActiveState/appdirs copied below:
13
14
15# This is the MIT license
16
17Copyright (c) 2010 ActiveState Software Inc.
18
19Permission is hereby granted, free of charge, to any person obtaining a
20copy of this software and associated documentation files (the
21"Software"), to deal in the Software without restriction, including
22without limitation the rights to use, copy, modify, merge, publish,
23distribute, sublicense, and/or sell copies of the Software, and to
24permit persons to whom the Software is furnished to do so, subject to
25the following conditions:
26
27The above copyright notice and this permission notice shall be included
28in all copies or substantial portions of the Software.
29
30THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
31OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
33IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
34CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
35TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
36SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37"""
38
39"""Utilities for determining application-specific dirs.
40
41See <https://github.com/ActiveState/appdirs> for details and usage.
42"""
43# Dev Notes:
44# - MSDN on where to store app data files:
45#   http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
46# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
47# - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
48
49__version__ = "1.4.4"
50__version_info__ = tuple(int(segment) for segment in __version__.split("."))
51
52
53import os
54import sys
55
56
57unicode = str
58
59if sys.platform.startswith("java"):
60    import platform
61
62    os_name = platform.java_ver()[3][0]
63    if os_name.startswith("Windows"):  # "Windows XP", "Windows 7", etc.
64        system = "win32"
65    elif os_name.startswith("Mac"):  # "Mac OS X", etc.
66        system = "darwin"
67    else:  # "Linux", "SunOS", "FreeBSD", etc.
68        # Setting this to "linux2" is not ideal, but only Windows or Mac
69        # are actually checked for and the rest of the module expects
70        # *sys.platform* style strings.
71        system = "linux2"
72else:
73    system = sys.platform
74
75
76def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
77    r"""Return full path to the user-specific 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 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. You may
84            pass False to disable it.
85        "version" is an optional version path element to append to the
86            path. You might want to use this if you want multiple versions
87            of your app to be able to run independently. If used, this
88            would typically be "<major>.<minor>".
89            Only applied when appname is present.
90        "roaming" (boolean, default False) can be set True to use the Windows
91            roaming appdata directory. That means that for users on a Windows
92            network setup for roaming profiles, this user data will be
93            sync'd on login. See
94            <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
95            for a discussion of issues.
96
97    Typical user data directories are:
98        Mac OS X:               ~/Library/Application Support/<AppName>
99        Unix:                   ~/.local/share/<AppName>    # or in $XDG_DATA_HOME, if defined
100        Win XP (not roaming):   C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
101        Win XP (roaming):       C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
102        Win 7  (not roaming):   C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
103        Win 7  (roaming):       C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
104
105    For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
106    That means, by default "~/.local/share/<AppName>".
107    """
108    if system == "win32":
109        if appauthor is None:
110            appauthor = appname
111        const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
112        path = os.path.normpath(_get_win_folder(const))
113        if appname:
114            if appauthor is not False:
115                path = os.path.join(path, appauthor, appname)
116            else:
117                path = os.path.join(path, appname)
118    elif system == "darwin":
119        path = os.path.expanduser("~/Library/Application Support/")
120        if appname:
121            path = os.path.join(path, appname)
122    else:
123        path = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share"))
124        if appname:
125            path = os.path.join(path, appname)
126    if appname and version:
127        path = os.path.join(path, version)
128    return path
129
130
131def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
132    r"""Return full path to the user-shared data dir for this application.
133
134        "appname" is the name of application.
135            If None, just the system directory is returned.
136        "appauthor" (only used on Windows) is the name of the
137            appauthor or distributing body for this application. Typically
138            it is the owning company name. This falls back to appname. You may
139            pass False to disable it.
140        "version" is an optional version path element to append to the
141            path. You might want to use this if you want multiple versions
142            of your app to be able to run independently. If used, this
143            would typically be "<major>.<minor>".
144            Only applied when appname is present.
145        "multipath" is an optional parameter only applicable to *nix
146            which indicates that the entire list of data dirs should be
147            returned. By default, the first item from XDG_DATA_DIRS is
148            returned, or '/usr/local/share/<AppName>',
149            if XDG_DATA_DIRS is not set
150
151    Typical site data directories are:
152        Mac OS X:   /Library/Application Support/<AppName>
153        Unix:       /usr/local/share/<AppName> or /usr/share/<AppName>
154        Win XP:     C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
155        Vista:      (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
156        Win 7:      C:\ProgramData\<AppAuthor>\<AppName>   # Hidden, but writeable on Win 7.
157
158    For Unix, this is using the $XDG_DATA_DIRS[0] default.
159
160    WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
161    """
162    if system == "win32":
163        if appauthor is None:
164            appauthor = appname
165        path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
166        if appname:
167            if appauthor is not False:
168                path = os.path.join(path, appauthor, appname)
169            else:
170                path = os.path.join(path, appname)
171    elif system == "darwin":
172        path = os.path.expanduser("/Library/Application Support")
173        if appname:
174            path = os.path.join(path, appname)
175    else:
176        # XDG default for $XDG_DATA_DIRS
177        # only first, if multipath is False
178        path = os.getenv(
179            "XDG_DATA_DIRS", os.pathsep.join(["/usr/local/share", "/usr/share"])
180        )
181        pathlist = [
182            os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)
183        ]
184        if appname:
185            if version:
186                appname = os.path.join(appname, version)
187            pathlist = [os.sep.join([x, appname]) for x in pathlist]
188
189        if multipath:
190            path = os.pathsep.join(pathlist)
191        else:
192            path = pathlist[0]
193        return path
194
195    if appname and version:
196        path = os.path.join(path, version)
197    return path
198
199
200def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
201    r"""Return full path to the user-specific config dir for this application.
202
203        "appname" is the name of application.
204            If None, just the system directory is returned.
205        "appauthor" (only used on Windows) is the name of the
206            appauthor or distributing body for this application. Typically
207            it is the owning company name. This falls back to appname. You may
208            pass False to disable it.
209        "version" is an optional version path element to append to the
210            path. You might want to use this if you want multiple versions
211            of your app to be able to run independently. If used, this
212            would typically be "<major>.<minor>".
213            Only applied when appname is present.
214        "roaming" (boolean, default False) can be set True to use the Windows
215            roaming appdata directory. That means that for users on a Windows
216            network setup for roaming profiles, this user data will be
217            sync'd on login. See
218            <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
219            for a discussion of issues.
220
221    Typical user config directories are:
222        Mac OS X:               ~/Library/Preferences/<AppName>
223        Unix:                   ~/.config/<AppName>     # or in $XDG_CONFIG_HOME, if defined
224        Win *:                  same as user_data_dir
225
226    For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
227    That means, by default "~/.config/<AppName>".
228    """
229    if system == "win32":
230        path = user_data_dir(appname, appauthor, None, roaming)
231    elif system == "darwin":
232        path = os.path.expanduser("~/Library/Preferences/")
233        if appname:
234            path = os.path.join(path, appname)
235    else:
236        path = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
237        if appname:
238            path = os.path.join(path, appname)
239    if appname and version:
240        path = os.path.join(path, version)
241    return path
242
243
244def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
245    r"""Return full path to the user-shared data dir for this application.
246
247        "appname" is the name of application.
248            If None, just the system directory is returned.
249        "appauthor" (only used on Windows) is the name of the
250            appauthor or distributing body for this application. Typically
251            it is the owning company name. This falls back to appname. You may
252            pass False to disable it.
253        "version" is an optional version path element to append to the
254            path. You might want to use this if you want multiple versions
255            of your app to be able to run independently. If used, this
256            would typically be "<major>.<minor>".
257            Only applied when appname is present.
258        "multipath" is an optional parameter only applicable to *nix
259            which indicates that the entire list of config dirs should be
260            returned. By default, the first item from XDG_CONFIG_DIRS is
261            returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
262
263    Typical site config directories are:
264        Mac OS X:   same as site_data_dir
265        Unix:       /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
266                    $XDG_CONFIG_DIRS
267        Win *:      same as site_data_dir
268        Vista:      (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
269
270    For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
271
272    WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
273    """
274    if system == "win32":
275        path = site_data_dir(appname, appauthor)
276        if appname and version:
277            path = os.path.join(path, version)
278    elif system == "darwin":
279        path = os.path.expanduser("/Library/Preferences")
280        if appname:
281            path = os.path.join(path, appname)
282    else:
283        # XDG default for $XDG_CONFIG_DIRS
284        # only first, if multipath is False
285        path = os.getenv("XDG_CONFIG_DIRS", "/etc/xdg")
286        pathlist = [
287            os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)
288        ]
289        if appname:
290            if version:
291                appname = os.path.join(appname, version)
292            pathlist = [os.sep.join([x, appname]) for x in pathlist]
293
294        if multipath:
295            path = os.pathsep.join(pathlist)
296        else:
297            path = pathlist[0]
298    return path
299
300
301def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
302    r"""Return full path to the user-specific cache dir for this application.
303
304        "appname" is the name of application.
305            If None, just the system directory is returned.
306        "appauthor" (only used on Windows) is the name of the
307            appauthor or distributing body for this application. Typically
308            it is the owning company name. This falls back to appname. You may
309            pass False to disable it.
310        "version" is an optional version path element to append to the
311            path. You might want to use this if you want multiple versions
312            of your app to be able to run independently. If used, this
313            would typically be "<major>.<minor>".
314            Only applied when appname is present.
315        "opinion" (boolean) can be False to disable the appending of
316            "Cache" to the base app data dir for Windows. See
317            discussion below.
318
319    Typical user cache directories are:
320        Mac OS X:   ~/Library/Caches/<AppName>
321        Unix:       ~/.cache/<AppName> (XDG default)
322        Win XP:     C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
323        Vista:      C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
324
325    On Windows the only suggestion in the MSDN docs is that local settings go in
326    the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
327    app data dir (the default returned by `user_data_dir` above). Apps typically
328    put cache data somewhere *under* the given dir here. Some examples:
329        ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
330        ...\Acme\SuperApp\Cache\1.0
331    OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
332    This can be disabled with the `opinion=False` option.
333    """
334    if system == "win32":
335        if appauthor is None:
336            appauthor = appname
337        path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
338        if appname:
339            if appauthor is not False:
340                path = os.path.join(path, appauthor, appname)
341            else:
342                path = os.path.join(path, appname)
343            if opinion:
344                path = os.path.join(path, "Cache")
345    elif system == "darwin":
346        path = os.path.expanduser("~/Library/Caches")
347        if appname:
348            path = os.path.join(path, appname)
349    else:
350        path = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
351        if appname:
352            path = os.path.join(path, appname)
353    if appname and version:
354        path = os.path.join(path, version)
355    return path
356
357
358def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
359    r"""Return full path to the user-specific state dir for this application.
360
361        "appname" is the name of application.
362            If None, just the system directory is returned.
363        "appauthor" (only used on Windows) is the name of the
364            appauthor or distributing body for this application. Typically
365            it is the owning company name. This falls back to appname. You may
366            pass False to disable it.
367        "version" is an optional version path element to append to the
368            path. You might want to use this if you want multiple versions
369            of your app to be able to run independently. If used, this
370            would typically be "<major>.<minor>".
371            Only applied when appname is present.
372        "roaming" (boolean, default False) can be set True to use the Windows
373            roaming appdata directory. That means that for users on a Windows
374            network setup for roaming profiles, this user data will be
375            sync'd on login. See
376            <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
377            for a discussion of issues.
378
379    Typical user state directories are:
380        Mac OS X:  same as user_data_dir
381        Unix:      ~/.local/state/<AppName>   # or in $XDG_STATE_HOME, if defined
382        Win *:     same as user_data_dir
383
384    For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
385    to extend the XDG spec and support $XDG_STATE_HOME.
386
387    That means, by default "~/.local/state/<AppName>".
388    """
389    if system in ["win32", "darwin"]:
390        path = user_data_dir(appname, appauthor, None, roaming)
391    else:
392        path = os.getenv("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
393        if appname:
394            path = os.path.join(path, appname)
395    if appname and version:
396        path = os.path.join(path, version)
397    return path
398
399
400def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
401    r"""Return full path to the user-specific log dir for this application.
402
403        "appname" is the name of application.
404            If None, just the system directory is returned.
405        "appauthor" (only used on Windows) is the name of the
406            appauthor or distributing body for this application. Typically
407            it is the owning company name. This falls back to appname. You may
408            pass False to disable it.
409        "version" is an optional version path element to append to the
410            path. You might want to use this if you want multiple versions
411            of your app to be able to run independently. If used, this
412            would typically be "<major>.<minor>".
413            Only applied when appname is present.
414        "opinion" (boolean) can be False to disable the appending of
415            "Logs" to the base app data dir for Windows, and "log" to the
416            base cache dir for Unix. See discussion below.
417
418    Typical user log directories are:
419        Mac OS X:   ~/Library/Logs/<AppName>
420        Unix:       ~/.cache/<AppName>/log  # or under $XDG_CACHE_HOME if defined
421        Win XP:     C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
422        Vista:      C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
423
424    On Windows the only suggestion in the MSDN docs is that local settings
425    go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
426    examples of what some windows apps use for a logs dir.)
427
428    OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
429    value for Windows and appends "log" to the user cache dir for Unix.
430    This can be disabled with the `opinion=False` option.
431    """
432    if system == "darwin":
433        path = os.path.join(os.path.expanduser("~/Library/Logs"), appname)
434    elif system == "win32":
435        path = user_data_dir(appname, appauthor, version)
436        version = False
437        if opinion:
438            path = os.path.join(path, "Logs")
439    else:
440        path = user_cache_dir(appname, appauthor, version)
441        version = False
442        if opinion:
443            path = os.path.join(path, "log")
444    if appname and version:
445        path = os.path.join(path, version)
446    return path
447
448
449class AppDirs(object):
450    """Convenience wrapper for getting application dirs."""
451
452    def __init__(
453        self, appname=None, appauthor=None, version=None, roaming=False, multipath=False
454    ):
455        self.appname = appname
456        self.appauthor = appauthor
457        self.version = version
458        self.roaming = roaming
459        self.multipath = multipath
460
461    @property
462    def user_data_dir(self):
463        return user_data_dir(
464            self.appname, self.appauthor, version=self.version, roaming=self.roaming
465        )
466
467    @property
468    def site_data_dir(self):
469        return site_data_dir(
470            self.appname, self.appauthor, version=self.version, multipath=self.multipath
471        )
472
473    @property
474    def user_config_dir(self):
475        return user_config_dir(
476            self.appname, self.appauthor, version=self.version, roaming=self.roaming
477        )
478
479    @property
480    def site_config_dir(self):
481        return site_config_dir(
482            self.appname, self.appauthor, version=self.version, multipath=self.multipath
483        )
484
485    @property
486    def user_cache_dir(self):
487        return user_cache_dir(self.appname, self.appauthor, version=self.version)
488
489    @property
490    def user_state_dir(self):
491        return user_state_dir(self.appname, self.appauthor, version=self.version)
492
493    @property
494    def user_log_dir(self):
495        return user_log_dir(self.appname, self.appauthor, version=self.version)
496
497
498# ---- internal support stuff
499
500
501def _get_win_folder_from_registry(csidl_name):
502    """This is a fallback technique at best. I'm not sure if using the
503    registry for this guarantees us the correct answer for all CSIDL_*
504    names.
505    """
506    import winreg as _winreg
507
508    shell_folder_name = {
509        "CSIDL_APPDATA": "AppData",
510        "CSIDL_COMMON_APPDATA": "Common AppData",
511        "CSIDL_LOCAL_APPDATA": "Local AppData",
512    }[csidl_name]
513
514    key = _winreg.OpenKey(
515        _winreg.HKEY_CURRENT_USER,
516        r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders",
517    )
518    dir, type = _winreg.QueryValueEx(key, shell_folder_name)
519    return dir
520
521
522def _get_win_folder_with_pywin32(csidl_name):
523    from win32com.shell import shell, shellcon
524
525    dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
526    # Try to make this a unicode path because SHGetFolderPath does
527    # not return unicode strings when there is unicode data in the
528    # path.
529    try:
530        dir = unicode(dir)
531
532        # Downgrade to short path name if have highbit chars. See
533        # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
534        has_high_char = False
535        for c in dir:
536            if ord(c) > 255:
537                has_high_char = True
538                break
539        if has_high_char:
540            try:
541                import win32api
542
543                dir = win32api.GetShortPathName(dir)
544            except ImportError:
545                pass
546    except UnicodeError:
547        pass
548    return dir
549
550
551def _get_win_folder_with_ctypes(csidl_name):
552    import ctypes
553
554    csidl_const = {
555        "CSIDL_APPDATA": 26,
556        "CSIDL_COMMON_APPDATA": 35,
557        "CSIDL_LOCAL_APPDATA": 28,
558    }[csidl_name]
559
560    buf = ctypes.create_unicode_buffer(1024)
561    ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
562
563    # Downgrade to short path name if have highbit chars. See
564    # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
565    has_high_char = False
566    for c in buf:
567        if ord(c) > 255:
568            has_high_char = True
569            break
570    if has_high_char:
571        buf2 = ctypes.create_unicode_buffer(1024)
572        if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
573            buf = buf2
574
575    return buf.value
576
577
578def _get_win_folder_with_jna(csidl_name):
579    import array
580
581    from com.sun import jna
582    from com.sun.jna.platform import win32
583
584    buf_size = win32.WinDef.MAX_PATH * 2
585    buf = array.zeros("c", buf_size)
586    shell = win32.Shell32.INSTANCE
587    shell.SHGetFolderPath(
588        None,
589        getattr(win32.ShlObj, csidl_name),
590        None,
591        win32.ShlObj.SHGFP_TYPE_CURRENT,
592        buf,
593    )
594    dir = jna.Native.toString(buf.tostring()).rstrip("\0")
595
596    # Downgrade to short path name if have highbit chars. See
597    # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
598    has_high_char = False
599    for c in dir:
600        if ord(c) > 255:
601            has_high_char = True
602            break
603    if has_high_char:
604        buf = array.zeros("c", buf_size)
605        kernel = win32.Kernel32.INSTANCE
606        if kernel.GetShortPathName(dir, buf, buf_size):
607            dir = jna.Native.toString(buf.tostring()).rstrip("\0")
608
609    return dir
610
611
612if system == "win32":
613    try:
614        import win32com.shell
615
616        _get_win_folder = _get_win_folder_with_pywin32
617    except ImportError:
618        try:
619            from ctypes import windll
620
621            _get_win_folder = _get_win_folder_with_ctypes
622        except ImportError:
623            try:
624                import com.sun.jna
625
626                _get_win_folder = _get_win_folder_with_jna
627            except ImportError:
628                _get_win_folder = _get_win_folder_from_registry
629
630
631# ---- self test code
632
633if __name__ == "__main__":
634    appname = "MyApp"
635    appauthor = "MyCompany"
636
637    props = (
638        "user_data_dir",
639        "user_config_dir",
640        "user_cache_dir",
641        "user_state_dir",
642        "user_log_dir",
643        "site_data_dir",
644        "site_config_dir",
645    )
646
647    print(f"-- app dirs {__version__} --")
648
649    print("-- app dirs (with optional 'version')")
650    dirs = AppDirs(appname, appauthor, version="1.0")
651    for prop in props:
652        print(f"{prop}: {getattr(dirs, prop)}")
653
654    print("\n-- app dirs (without optional 'version')")
655    dirs = AppDirs(appname, appauthor)
656    for prop in props:
657        print(f"{prop}: {getattr(dirs, prop)}")
658
659    print("\n-- app dirs (without optional 'appauthor')")
660    dirs = AppDirs(appname)
661    for prop in props:
662        print(f"{prop}: {getattr(dirs, prop)}")
663
664    print("\n-- app dirs (with disabled 'appauthor')")
665    dirs = AppDirs(appname, appauthor=False)
666    for prop in props:
667        print(f"{prop}: {getattr(dirs, prop)}")
668