python (3.11.7)
       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  
       8  See <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_info__ = (1, 4, 3)
      17  __version__ = '.'.join(map(str, __version_info__))
      18  
      19  
      20  import sys
      21  import os
      22  
      23  PY3 = sys.version_info[0] == 3
      24  
      25  if PY3:
      26      unicode = str
      27  
      28  if 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'
      40  else:
      41      system = sys.platform
      42  
      43  
      44  
      45  def 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  
     100  def 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  
     166  def 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  
     206  def 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  
     257  def 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  
     314  def 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  
     356  def 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  
     407  class ESC[4;38;5;81mAppDirs(ESC[4;38;5;149mobject):
     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  
     455  def _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  
     479  def _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  
     506  def _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  
     532  def _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  
     558  if 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  
     576  if __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)))