diff --git a/news/11527.bugfix.rst b/news/11527.bugfix.rst new file mode 100644 index 00000000000..0185a804ff7 --- /dev/null +++ b/news/11527.bugfix.rst @@ -0,0 +1,2 @@ +Wheel cache behavior is restored to match previous versions, allowing the +cache to find existing entries. diff --git a/src/pip/_internal/models/link.py b/src/pip/_internal/models/link.py index c792d128bcf..ffac2fecad8 100644 --- a/src/pip/_internal/models/link.py +++ b/src/pip/_internal/models/link.py @@ -248,6 +248,12 @@ def from_json( yanked_reason = file_data.get("yanked") dist_info_metadata = file_data.get("dist-info-metadata") hashes = file_data.get("hashes", {}) + link_hash = None + if hashes: + for hash_name in _SUPPORTED_HASHES: + if hash_name in hashes: + link_hash = LinkHash(name=hash_name, value=hashes[hash_name]) + break # The Link.yanked_reason expects an empty string instead of a boolean. if yanked_reason and not isinstance(yanked_reason, str): @@ -262,6 +268,7 @@ def from_json( requires_python=pyrequire, yanked_reason=yanked_reason, hashes=hashes, + link_hash=link_hash, dist_info_metadata=dist_info_metadata, ) diff --git a/tests/unit/test_cache.py b/tests/unit/test_cache.py index f1f0141c708..f27daa266c8 100644 --- a/tests/unit/test_cache.py +++ b/tests/unit/test_cache.py @@ -1,7 +1,7 @@ import os from pathlib import Path -from pip._vendor.packaging.tags import Tag +from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version from pip._internal.cache import WheelCache, _hash_dict from pip._internal.models.format_control import FormatControl @@ -52,6 +52,48 @@ def test_cache_hash() -> None: assert h == "f83b32dfa27a426dec08c21bf006065dd003d0aac78e7fc493d9014d" +def test_link_to_cache(tmpdir: Path) -> None: + """ + Test that Link.from_json() produces Links with consistent cache + locations + """ + wc = WheelCache(os.fspath(tmpdir), FormatControl()) + # Define our expectations for stable cache path. + i_name = interpreter_name() + i_version = interpreter_version() + key_parts = { + "url": "https://files.pythonhosted.org/packages/a6/91/" + "86a6eac449ddfae239e93ffc1918cf33fd9bab35c04d1e963b311e347a73/" + "netifaces-0.11.0.tar.gz", + "sha256": "043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32", + "interpreter_name": i_name, + "interpreter_version": i_version, + } + expected_hash = _hash_dict(key_parts) + parts = [ + expected_hash[:2], + expected_hash[2:4], + expected_hash[4:6], + expected_hash[6:], + ] + pathed_hash = os.path.join(*parts) + # Check working from a Link produces the same result. + file_data = { + "filename": "netifaces-0.11.0.tar.gz", + "hashes": { + "sha256": key_parts["sha256"], + }, + "requires-python": "", + "url": key_parts["url"], + "yanked": False, + } + page_url = "https://pypi.org/simple/netifaces/" + link = Link.from_json(file_data=file_data, page_url=page_url) + assert link + path = wc.get_path_for_link(link) + assert pathed_hash in path + + def test_get_cache_entry(tmpdir: Path) -> None: wc = WheelCache(os.fspath(tmpdir), FormatControl()) persi_link = Link("https://g.c/o/r/persi")