diff --git a/news/7333.bugfix b/news/7333.bugfix new file mode 100644 index 000000000..8ddcba76a --- /dev/null +++ b/news/7333.bugfix @@ -0,0 +1 @@ +Include ``subdirectory`` URL fragments in cache keys. diff --git a/src/pip/_internal/cache.py b/src/pip/_internal/cache.py index c5431e14d..b3327ea9a 100644 --- a/src/pip/_internal/cache.py +++ b/src/pip/_internal/cache.py @@ -58,6 +58,10 @@ class Cache(object): key_parts = [link.url_without_fragment] if link.hash_name is not None and link.hash is not None: key_parts.append("=".join([link.hash_name, link.hash])) + if link.subdirectory_fragment: + key_parts.append( + "=".join(["subdirectory", link.subdirectory_fragment]) + ) key_url = "#".join(key_parts) # Encode our key url with sha224, we'll use this because it has similar @@ -168,11 +172,17 @@ class SimpleWheelCache(Cache): # type: (...) -> Link candidates = [] + canonical_package_name = None + if package_name: + canonical_package_name = canonicalize_name(package_name) for wheel_name in self._get_candidates(link, package_name): try: wheel = Wheel(wheel_name) except InvalidWheelFilename: continue + assert canonical_package_name + if wheel.name != canonical_package_name: + continue if not wheel.supported(supported_tags): # Built for a different python/arch/etc continue diff --git a/tests/unit/test_cache.py b/tests/unit/test_cache.py index d75cd2c65..79e4f624d 100644 --- a/tests/unit/test_cache.py +++ b/tests/unit/test_cache.py @@ -1,13 +1,44 @@ +import os + from pip._internal.cache import WheelCache +from pip._internal.models.format_control import FormatControl +from pip._internal.models.link import Link from pip._internal.utils.compat import expanduser +from pip._internal.utils.misc import ensure_dir -class TestWheelCache: +def test_expands_path(): + wc = WheelCache("~/.foo/", None) + assert wc.cache_dir == expanduser("~/.foo/") - def test_expands_path(self): - wc = WheelCache("~/.foo/", None) - assert wc.cache_dir == expanduser("~/.foo/") - def test_falsey_path_none(self): - wc = WheelCache(False, None) - assert wc.cache_dir is None +def test_falsey_path_none(): + wc = WheelCache(False, None) + assert wc.cache_dir is None + + +def test_subdirectory_fragment(): + """ + Test the subdirectory URL fragment is part of the cache key. + """ + wc = WheelCache("~/.foo/", None) + link1 = Link("git+https://g.c/o/r#subdirectory=d1") + link2 = Link("git+https://g.c/o/r#subdirectory=d2") + assert wc.get_path_for_link(link1) != wc.get_path_for_link(link2) + + +def test_wheel_name_filter(tmpdir): + """ + Test the wheel cache filters on wheel name when several wheels + for different package are stored under the same cache directory. + """ + wc = WheelCache(tmpdir, FormatControl()) + link = Link("https://g.c/package.tar.gz") + cache_path = wc.get_path_for_link(link) + ensure_dir(cache_path) + with open(os.path.join(cache_path, "package-1.0-py3-none-any.whl"), "w"): + pass + # package matches wheel name + assert wc.get(link, "package", [("py3", "none", "any")]) is not link + # package2 does not match wheel name + assert wc.get(link, "package2", [("py3", "none", "any")]) is link