diff --git a/src/pip/_internal/locations.py b/src/pip/_internal/locations.py index 5a20c92a8ee..bc21160936c 100644 --- a/src/pip/_internal/locations.py +++ b/src/pip/_internal/locations.py @@ -10,8 +10,9 @@ from distutils import sysconfig as distutils_sysconfig from distutils.command.install import SCHEME_KEYS, install # type: ignore +from pip._vendor import appdirs + from pip._internal.compat import WINDOWS, expanduser -from pip._internal.utils import appdirs # Application Directories USER_CACHE_DIR = appdirs.user_cache_dir("pip") @@ -125,8 +126,8 @@ def virtualenv_no_global(): bin_py = '/usr/local/bin' site_config_files = [ - os.path.join(path, config_basename) - for path in appdirs.site_config_dirs('pip') + os.path.join(pth, config_basename) + for pth in appdirs.site_config_dir('pip', multipath=True).split(os.pathsep) ] venv_config_file = os.path.join(sys.prefix, config_basename) diff --git a/src/pip/_internal/utils/appdirs.py b/src/pip/_internal/utils/appdirs.py deleted file mode 100644 index 28c5d4b3c75..00000000000 --- a/src/pip/_internal/utils/appdirs.py +++ /dev/null @@ -1,258 +0,0 @@ -""" -This code was taken from https://github.com/ActiveState/appdirs and modified -to suit our purposes. -""" -from __future__ import absolute_import - -import os -import sys - -from pip._vendor.six import PY2, text_type - -from pip._internal.compat import WINDOWS, expanduser - - -def user_cache_dir(appname): - r""" - Return full path to the user-specific cache dir for this application. - - "appname" is the name of application. - - Typical user cache directories are: - macOS: ~/Library/Caches/ - Unix: ~/.cache/ (XDG default) - Windows: C:\Users\\AppData\Local\\Cache - - On Windows the only suggestion in the MSDN docs is that local settings go - in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the - non-roaming app data dir (the default returned by `user_data_dir`). Apps - typically put cache data somewhere *under* the given dir here. Some - examples: - ...\Mozilla\Firefox\Profiles\\Cache - ...\Acme\SuperApp\Cache\1.0 - - OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. - """ - if WINDOWS: - # Get the base path - path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) - - # When using Python 2, return paths as bytes on Windows like we do on - # other operating systems. See helper function docs for more details. - if PY2 and isinstance(path, text_type): - path = _win_path_to_bytes(path) - - # Add our app name and Cache directory to it - path = os.path.join(path, appname, "Cache") - elif sys.platform == "darwin": - # Get the base path - path = expanduser("~/Library/Caches") - - # Add our app name to it - path = os.path.join(path, appname) - else: - # Get the base path - path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache")) - - # Add our app name to it - path = os.path.join(path, appname) - - return path - - -def user_data_dir(appname, 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 system directory is returned. - "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: - macOS: ~/Library/Application Support/ - if it exists, else ~/.config/ - Unix: ~/.local/share/ # or in - $XDG_DATA_HOME, if defined - Win XP (not roaming): C:\Documents and Settings\\ ... - ...Application Data\ - Win XP (roaming): C:\Documents and Settings\\Local ... - ...Settings\Application Data\ - Win 7 (not roaming): C:\\Users\\AppData\Local\ - Win 7 (roaming): C:\\Users\\AppData\Roaming\ - - For Unix, we follow the XDG spec and support $XDG_DATA_HOME. - That means, by default "~/.local/share/". - """ - if WINDOWS: - const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" - path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) - elif sys.platform == "darwin": - path = os.path.join( - expanduser('~/Library/Application Support/'), - appname, - ) if os.path.isdir(os.path.join( - expanduser('~/Library/Application Support/'), - appname, - ) - ) else os.path.join( - expanduser('~/.config/'), - appname, - ) - else: - path = os.path.join( - os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), - appname, - ) - - return path - - -def user_config_dir(appname, roaming=True): - """Return full path to the user-specific config dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "roaming" (boolean, default True) can be set False to not 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: - macOS: same as user_data_dir - Unix: ~/.config/ - Win *: same as user_data_dir - - For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. - That means, by default "~/.config/". - """ - if WINDOWS: - path = user_data_dir(appname, roaming=roaming) - elif sys.platform == "darwin": - path = user_data_dir(appname) - else: - path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config")) - path = os.path.join(path, appname) - - return path - - -# for the discussion regarding site_config_dirs locations -# see -def site_config_dirs(appname): - r"""Return a list of potential user-shared config dirs for this application. - - "appname" is the name of application. - - Typical user config directories are: - macOS: /Library/Application Support// - Unix: /etc or $XDG_CONFIG_DIRS[i]// for each value in - $XDG_CONFIG_DIRS - Win XP: C:\Documents and Settings\All Users\Application ... - ...Data\\ - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory - on Vista.) - Win 7: Hidden, but writeable on Win 7: - C:\ProgramData\\ - """ - if WINDOWS: - path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) - pathlist = [os.path.join(path, appname)] - elif sys.platform == 'darwin': - pathlist = [os.path.join('/Library/Application Support', appname)] - else: - # try looking in $XDG_CONFIG_DIRS - xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') - if xdg_config_dirs: - pathlist = [ - os.path.join(expanduser(x), appname) - for x in xdg_config_dirs.split(os.pathsep) - ] - else: - pathlist = [] - - # always look in /etc directly as well - pathlist.append('/etc') - - return pathlist - - -# -- Windows support functions -- - -def _get_win_folder_from_registry(csidl_name): - """ - This is a fallback technique at best. I'm not sure if using the - registry for this guarantees us the correct answer for all CSIDL_* - names. - """ - import _winreg - - shell_folder_name = { - "CSIDL_APPDATA": "AppData", - "CSIDL_COMMON_APPDATA": "Common AppData", - "CSIDL_LOCAL_APPDATA": "Local AppData", - }[csidl_name] - - key = _winreg.OpenKey( - _winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" - ) - directory, _type = _winreg.QueryValueEx(key, shell_folder_name) - return directory - - -def _get_win_folder_with_ctypes(csidl_name): - 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 - - -if WINDOWS: - try: - import ctypes - _get_win_folder = _get_win_folder_with_ctypes - except ImportError: - _get_win_folder = _get_win_folder_from_registry - - -def _win_path_to_bytes(path): - """Encode Windows paths to bytes. Only used on Python 2. - - Motivation is to be consistent with other operating systems where paths - are also returned as bytes. This avoids problems mixing bytes and Unicode - elsewhere in the codebase. For more details and discussion see - . - - If encoding using ASCII and MBCS fails, return the original Unicode path. - """ - for encoding in ('ASCII', 'MBCS'): - try: - return path.encode(encoding) - except (UnicodeEncodeError, LookupError): - pass - return path diff --git a/src/pip/_vendor/appdirs.py b/src/pip/_vendor/appdirs.py index ae67001af8b..2bd39110281 100644 --- a/src/pip/_vendor/appdirs.py +++ b/src/pip/_vendor/appdirs.py @@ -557,18 +557,14 @@ def _get_win_folder_with_jna(csidl_name): if system == "win32": try: - import win32com.shell - _get_win_folder = _get_win_folder_with_pywin32 + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes except ImportError: try: - from ctypes import windll - _get_win_folder = _get_win_folder_with_ctypes + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna except ImportError: - try: - import com.sun.jna - _get_win_folder = _get_win_folder_with_jna - except ImportError: - _get_win_folder = _get_win_folder_from_registry + _get_win_folder = _get_win_folder_from_registry #---- self test code diff --git a/src/pip/_vendor/appdirs.pyi b/src/pip/_vendor/appdirs.pyi deleted file mode 100644 index 80afcfce1a3..00000000000 --- a/src/pip/_vendor/appdirs.pyi +++ /dev/null @@ -1 +0,0 @@ -from appdirs import * \ No newline at end of file diff --git a/tasks/vendoring/__init__.py b/tasks/vendoring/__init__.py index 555e87a32df..36bbdec68b3 100644 --- a/tasks/vendoring/__init__.py +++ b/tasks/vendoring/__init__.py @@ -144,9 +144,11 @@ def update_stubs(ctx): print("[vendoring.update_stubs] Add mypy stubs") - # Some projects need stubs other than a simple .pyi extra_stubs_needed = { - "six": ["six.__init__", "six.moves"] + # Some projects need stubs other than a simple .pyi + "six": ["six.__init__", "six.moves"], + # Some projects should not have stubs coz they're single file modules + "appdirs": [], } for lib in vendored_libs: diff --git a/tasks/vendoring/patches/appdirs.patch b/tasks/vendoring/patches/appdirs.patch new file mode 100644 index 00000000000..73f9f2b743a --- /dev/null +++ b/tasks/vendoring/patches/appdirs.patch @@ -0,0 +1,28 @@ +diff --git a/src/pip/_vendor/appdirs.py b/src/pip/_vendor/appdirs.py +index ae67001a..2bd39110 100644 +--- a/src/pip/_vendor/appdirs.py ++++ b/src/pip/_vendor/appdirs.py +@@ -557,18 +557,14 @@ def _get_win_folder_with_jna(csidl_name): + + if system == "win32": + try: +- import win32com.shell +- _get_win_folder = _get_win_folder_with_pywin32 ++ from ctypes import windll ++ _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: +- from ctypes import windll +- _get_win_folder = _get_win_folder_with_ctypes ++ import com.sun.jna ++ _get_win_folder = _get_win_folder_with_jna + except ImportError: +- try: +- import com.sun.jna +- _get_win_folder = _get_win_folder_with_jna +- except ImportError: +- _get_win_folder = _get_win_folder_from_registry ++ _get_win_folder = _get_win_folder_from_registry + + + #---- self test code diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 066ba348bb9..532a73206f4 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -972,7 +972,7 @@ def test_cleanup_after_failed_wheel(script, data, common_wheels): def test_install_builds_wheels(script, data, common_wheels): # We need to use a subprocess to get the right value on Windows. res = script.run('python', '-c', ( - 'from pip._internal.utils import appdirs; ' + 'from pip._vendor import appdirs; ' 'print(appdirs.user_cache_dir("pip"))' )) wheels_cache = os.path.join(res.stdout.rstrip('\n'), 'wheels') diff --git a/tests/unit/test_appdirs.py b/tests/unit/test_appdirs.py deleted file mode 100644 index a306d56cffe..00000000000 --- a/tests/unit/test_appdirs.py +++ /dev/null @@ -1,302 +0,0 @@ -import ntpath -import os -import posixpath -import sys - -import pretend - -from pip._internal.utils import appdirs - - -class TestUserCacheDir: - - def test_user_cache_dir_win(self, monkeypatch): - @pretend.call_recorder - def _get_win_folder(base): - return "C:\\Users\\test\\AppData\\Local" - - monkeypatch.setattr( - appdirs, - "_get_win_folder", - _get_win_folder, - raising=False, - ) - monkeypatch.setattr(appdirs, "WINDOWS", True) - monkeypatch.setattr(os, "path", ntpath) - - assert (appdirs.user_cache_dir("pip") == - "C:\\Users\\test\\AppData\\Local\\pip\\Cache") - assert _get_win_folder.calls == [pretend.call("CSIDL_LOCAL_APPDATA")] - - def test_user_cache_dir_osx(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "darwin") - - assert appdirs.user_cache_dir("pip") == "/home/test/Library/Caches/pip" - - def test_user_cache_dir_linux(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.delenv("XDG_CACHE_HOME", raising=False) - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_cache_dir("pip") == "/home/test/.cache/pip" - - def test_user_cache_dir_linux_override(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setenv("XDG_CACHE_HOME", "/home/test/.other-cache") - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_cache_dir("pip") == "/home/test/.other-cache/pip" - - def test_user_cache_dir_linux_home_slash(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - # Verify that we are not affected by http://bugs.python.org/issue14768 - monkeypatch.delenv("XDG_CACHE_HOME", raising=False) - monkeypatch.setenv("HOME", "/") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_cache_dir("pip") == "/.cache/pip" - - def test_user_cache_dir_unicode(self, monkeypatch): - if sys.platform != 'win32': - return - - def my_get_win_folder(csidl_name): - return u"\u00DF\u00E4\u03B1\u20AC" - - monkeypatch.setattr(appdirs, "_get_win_folder", my_get_win_folder) - - # Do not use the isinstance expression directly in the - # assert statement, as the Unicode characters in the result - # cause pytest to fail with an internal error on Python 2.7 - result_is_str = isinstance(appdirs.user_cache_dir('test'), str) - assert result_is_str, "user_cache_dir did not return a str" - - # Test against regression #3463 - from pip._internal import create_main_parser - create_main_parser().print_help() # This should not crash - - -class TestSiteConfigDirs: - - def test_site_config_dirs_win(self, monkeypatch): - @pretend.call_recorder - def _get_win_folder(base): - return "C:\\ProgramData" - - monkeypatch.setattr( - appdirs, - "_get_win_folder", - _get_win_folder, - raising=False, - ) - monkeypatch.setattr(appdirs, "WINDOWS", True) - monkeypatch.setattr(os, "path", ntpath) - - assert appdirs.site_config_dirs("pip") == ["C:\\ProgramData\\pip"] - assert _get_win_folder.calls == [pretend.call("CSIDL_COMMON_APPDATA")] - - def test_site_config_dirs_osx(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "darwin") - - assert appdirs.site_config_dirs("pip") == \ - ["/Library/Application Support/pip"] - - def test_site_config_dirs_linux(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.delenv("XDG_CONFIG_DIRS", raising=False) - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.site_config_dirs("pip") == [ - '/etc/xdg/pip', - '/etc' - ] - - def test_site_config_dirs_linux_override(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setattr(os, "pathsep", ':') - monkeypatch.setenv("XDG_CONFIG_DIRS", "/spam:/etc:/etc/xdg") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.site_config_dirs("pip") == [ - '/spam/pip', - '/etc/pip', - '/etc/xdg/pip', - '/etc' - ] - - -class TestUserDataDir: - - def test_user_data_dir_win_no_roaming(self, monkeypatch): - @pretend.call_recorder - def _get_win_folder(base): - return "C:\\Users\\test\\AppData\\Local" - - monkeypatch.setattr( - appdirs, - "_get_win_folder", - _get_win_folder, - raising=False, - ) - monkeypatch.setattr(appdirs, "WINDOWS", True) - monkeypatch.setattr(os, "path", ntpath) - - assert (appdirs.user_data_dir("pip") == - "C:\\Users\\test\\AppData\\Local\\pip") - assert _get_win_folder.calls == [pretend.call("CSIDL_LOCAL_APPDATA")] - - def test_user_data_dir_win_yes_roaming(self, monkeypatch): - @pretend.call_recorder - def _get_win_folder(base): - return "C:\\Users\\test\\AppData\\Roaming" - - monkeypatch.setattr( - appdirs, - "_get_win_folder", - _get_win_folder, - raising=False, - ) - monkeypatch.setattr(appdirs, "WINDOWS", True) - monkeypatch.setattr(os, "path", ntpath) - - assert ( - appdirs.user_data_dir("pip", roaming=True) == - "C:\\Users\\test\\AppData\\Roaming\\pip" - ) - assert _get_win_folder.calls == [pretend.call("CSIDL_APPDATA")] - - def test_user_data_dir_osx(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "darwin") - - if os.path.isdir('/home/test/Library/Application Support/'): - assert (appdirs.user_data_dir("pip") == - "/home/test/Library/Application Support/pip") - else: - assert (appdirs.user_data_dir("pip") == - "/home/test/.config/pip") - - def test_user_data_dir_linux(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.delenv("XDG_DATA_HOME", raising=False) - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_data_dir("pip") == "/home/test/.local/share/pip" - - def test_user_data_dir_linux_override(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setenv("XDG_DATA_HOME", "/home/test/.other-share") - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_data_dir("pip") == "/home/test/.other-share/pip" - - def test_user_data_dir_linux_home_slash(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - # Verify that we are not affected by http://bugs.python.org/issue14768 - monkeypatch.delenv("XDG_DATA_HOME", raising=False) - monkeypatch.setenv("HOME", "/") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_data_dir("pip") == "/.local/share/pip" - - -class TestUserConfigDir: - - def test_user_config_dir_win_no_roaming(self, monkeypatch): - @pretend.call_recorder - def _get_win_folder(base): - return "C:\\Users\\test\\AppData\\Local" - - monkeypatch.setattr( - appdirs, - "_get_win_folder", - _get_win_folder, - raising=False, - ) - monkeypatch.setattr(appdirs, "WINDOWS", True) - monkeypatch.setattr(os, "path", ntpath) - - assert ( - appdirs.user_config_dir("pip", roaming=False) == - "C:\\Users\\test\\AppData\\Local\\pip" - ) - assert _get_win_folder.calls == [pretend.call("CSIDL_LOCAL_APPDATA")] - - def test_user_config_dir_win_yes_roaming(self, monkeypatch): - @pretend.call_recorder - def _get_win_folder(base): - return "C:\\Users\\test\\AppData\\Roaming" - - monkeypatch.setattr( - appdirs, - "_get_win_folder", - _get_win_folder, - raising=False, - ) - monkeypatch.setattr(appdirs, "WINDOWS", True) - monkeypatch.setattr(os, "path", ntpath) - - assert (appdirs.user_config_dir("pip") == - "C:\\Users\\test\\AppData\\Roaming\\pip") - assert _get_win_folder.calls == [pretend.call("CSIDL_APPDATA")] - - def test_user_config_dir_osx(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "darwin") - - if os.path.isdir('/home/test/Library/Application Support/'): - assert (appdirs.user_data_dir("pip") == - "/home/test/Library/Application Support/pip") - else: - assert (appdirs.user_data_dir("pip") == - "/home/test/.config/pip") - - def test_user_config_dir_linux(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.delenv("XDG_CONFIG_HOME", raising=False) - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_config_dir("pip") == "/home/test/.config/pip" - - def test_user_config_dir_linux_override(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - monkeypatch.setenv("XDG_CONFIG_HOME", "/home/test/.other-config") - monkeypatch.setenv("HOME", "/home/test") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_config_dir("pip") == "/home/test/.other-config/pip" - - def test_user_config_dir_linux_home_slash(self, monkeypatch): - monkeypatch.setattr(appdirs, "WINDOWS", False) - monkeypatch.setattr(os, "path", posixpath) - # Verify that we are not affected by http://bugs.python.org/issue14768 - monkeypatch.delenv("XDG_CONFIG_HOME", raising=False) - monkeypatch.setenv("HOME", "/") - monkeypatch.setattr(sys, "platform", "linux2") - - assert appdirs.user_config_dir("pip") == "/.config/pip"