import sys import os import re def user_download_dir(): r"""Return full path to the user-specific download dir. Typical user data directories are: Mac OS X: ~/Downloads Unix: ~/Downloads # or in $XDG_DOWNLOAD_DIR, if defined Win 7: C:\Users\\Downloads For Unix, we follow the XDG spec and support $XDG_DOWNLOAD_DIR. That means, by default "~/Downloads". """ if sys.platform == "win32": return os.path.normpath(_get_win_download_folder()) elif sys.platform == "darwin": return os.path.expanduser('~/Downloads') else: try: config_dirs = os.path.join(user_config_dir(), 'user-dirs.dirs') with open(config_dirs) as dirs_file: path_match = re.search(r'XDG_DOWNLOAD_DIR=(.+)', dirs_file.read()) cleaned_path = path_match.group(1).replace('"', '').replace('$HOME', '~') return os.path.expanduser(cleaned_path) except Exception: pass return os.getenv('XDG_DOWNLOAD_DIR', os.path.expanduser("~/Downloads")) def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): r"""Return full path to the user-specific data dir for this application. "appname" is the name of application. If None, just the sys.platform directory is returned. "appauthor" (only used on Windows) is the name of the appauthor or distributing body for this application. Typically it is the owning company name. This falls back to appname. You may pass False to disable it. "version" is an optional version path element to append to the path. You might want to use this if you want multiple versions of your app to be able to run independently. If used, this would typically be ".". Only applied when appname is present. "roaming" (boolean, default False) can be set True to use the Windows roaming appdata directory. That means that for users on a Windows network setup for roaming profiles, this user data will be sync'd on login. See for a discussion of issues. Typical user data directories are: Mac OS X: ~/Library/Application Support/ Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined Win XP: C:\Documents and Settings\\Application Data\\ Win 7: C:\Users\\AppData\Local\\ For Unix, we follow the XDG spec and support $XDG_DATA_HOME. That means, by default "~/.local/share/". """ if sys.platform == "win32": if appauthor is None: appauthor = appname const = "CSIDL_APPDATA" if roaming else "CSIDL_LOCAL_APPDATA" path = os.path.normpath(_get_win_folder(const)) if appname: if appauthor is not False: path = os.path.join(path, appauthor, appname) else: path = os.path.join(path, appname) elif sys.platform == "darwin": path = os.path.expanduser("~/Library/Application Support/") if appname: path = os.path.join(path, appname) else: path = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share")) if appname: path = os.path.join(path, appname) if appname and version: path = os.path.join(path, version) return path def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): r"""Return full path to the user-specific config dir for this application. "appname" is the name of application. If None, just the sys.platform directory is returned. "appauthor" (only used on Windows) is the name of the appauthor or distributing body for this application. Typically it is the owning company name. This falls back to appname. You may pass False to disable it. "version" is an optional version path element to append to the path. You might want to use this if you want multiple versions of your app to be able to run independently. If used, this would typically be ".". Only applied when appname is present. "roaming" (boolean, default False) can be set True to use the Windows roaming appdata directory. That means that for users on a Windows network setup for roaming profiles, this user data will be sync'd on login. See for a discussion of issues. Typical user config directories are: Mac OS X: ~/Library/Preferences/ Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined Win *: same as user_data_dir For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. That means, by default "~/.config/". """ if sys.platform == "win32": path = user_data_dir(appname, appauthor, None, roaming) elif sys.platform == "darwin": path = os.path.expanduser("~/Library/Preferences/") if appname: path = os.path.join(path, appname) else: path = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) if appname: path = os.path.join(path, appname) if appname and version: path = os.path.join(path, version) return path def _get_win_folder(csidl_name): import ctypes # pylint: disable=import-outside-toplevel csidl_const = { "CSIDL_APPDATA": 26, "CSIDL_COMMON_APPDATA": 35, "CSIDL_LOCAL_APPDATA": 28, }[csidl_name] buf = ctypes.create_unicode_buffer(1024) ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) # Downgrade to short path name if have highbit chars. See # . has_high_char = False for c in buf: if ord(c) > 255: has_high_char = True break if has_high_char: buf2 = ctypes.create_unicode_buffer(1024) if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): buf = buf2 return buf.value def _get_win_download_folder(): import ctypes # pylint: disable=import-outside-toplevel from ctypes import windll, wintypes # pylint: disable=import-outside-toplevel from uuid import UUID # pylint: disable=import-outside-toplevel class GUID(ctypes.Structure): _fields_ = [ ("data1", wintypes.DWORD), ("data2", wintypes.WORD), ("data3", wintypes.WORD), ("data4", wintypes.BYTE * 8) ] def __init__(self, uuidstr): ctypes.Structure.__init__(self) uuid = UUID(uuidstr) self.data1, self.data2, self.data3, \ self.data4[0], self.data4[1], rest = uuid.fields for i in range(2, 8): self.data4[i] = rest >> (8-i-1)*8 & 0xff SHGetKnownFolderPath = windll.shell32.SHGetKnownFolderPath # pylint: disable=invalid-name SHGetKnownFolderPath.argtypes = [ ctypes.POINTER(GUID), wintypes.DWORD, wintypes.HANDLE, ctypes.POINTER(ctypes.c_wchar_p) ] FOLDERID_Downloads = '{374DE290-123F-4565-9164-39C4925E467B}' # pylint: disable=invalid-name guid = GUID(FOLDERID_Downloads) pathptr = ctypes.c_wchar_p() if SHGetKnownFolderPath(ctypes.byref(guid), 0, 0, ctypes.byref(pathptr)): raise Exception('Failed to get download directory.') return pathptr.value