Skip to content

Commit e744949

Browse files
committed
Record origin url in wheel cache
1 parent c6baa75 commit e744949

File tree

6 files changed

+40
-2
lines changed

6 files changed

+40
-2
lines changed

news/11137.feature.rst

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Record in wheel cache entries the URL of the original artifiact that was downloaded
2+
to build the cached wheels. The record is named ``origin.json`` and uses the PEP 610
3+
Direct URL format.

src/pip/_internal/cache.py

+25
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
import json
66
import logging
77
import os
8+
from pathlib import Path
89
from typing import Any, Dict, List, Optional, Set
910

1011
from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
1112
from pip._vendor.packaging.utils import canonicalize_name
1213

1314
from pip._internal.exceptions import InvalidWheelFilename
15+
from pip._internal.models.direct_url import DirectUrl
1416
from pip._internal.models.format_control import FormatControl
1517
from pip._internal.models.link import Link
1618
from pip._internal.models.wheel import Wheel
@@ -19,6 +21,8 @@
1921

2022
logger = logging.getLogger(__name__)
2123

24+
ORIGIN_JSON_NAME = "origin.json"
25+
2226

2327
def _hash_dict(d: Dict[str, str]) -> str:
2428
"""Return a stable sha224 of a dictionary."""
@@ -204,6 +208,10 @@ def __init__(
204208
):
205209
self.link = link
206210
self.persistent = persistent
211+
self.origin: Optional[DirectUrl] = None
212+
origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
213+
if origin_direct_url_path.exists():
214+
self.origin = DirectUrl.from_json(origin_direct_url_path.read_text())
207215

208216

209217
class WheelCache(Cache):
@@ -262,3 +270,20 @@ def get_cache_entry(
262270
return CacheEntry(retval, persistent=False)
263271

264272
return None
273+
274+
@staticmethod
275+
def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
276+
origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
277+
if origin_path.is_file():
278+
origin = DirectUrl.from_json(origin_path.read_text())
279+
# TODO: use DirectUrl.equivalent when https://github.com/pypa/pip/pull/10564
280+
# is merged.
281+
if origin.url != download_info.url:
282+
logger.warning(
283+
"Origin URL %s in cache entry %s does not match download URL %s. "
284+
"This is likely a pip bug or a cache corruption issue.",
285+
origin.url,
286+
cache_dir,
287+
download_info.url,
288+
)
289+
origin_path.write_text(download_info.to_json())

src/pip/_internal/resolution/legacy/resolver.py

+2
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,8 @@ def _populate_link(self, req: InstallRequirement) -> None:
431431
logger.debug("Using cached wheel link: %s", cache_entry.link)
432432
if req.link is req.original_link and cache_entry.persistent:
433433
req.original_link_is_in_wheel_cache = True
434+
if cache_entry.origin is not None:
435+
req.download_info = cache_entry.origin
434436
req.link = cache_entry.link
435437

436438
def _get_dist_for(self, req: InstallRequirement) -> BaseDistribution:

src/pip/_internal/resolution/resolvelib/candidates.py

+2
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ def __init__(
287287
and template.link is template.original_link
288288
):
289289
ireq.original_link_is_in_wheel_cache = True
290+
if cache_entry.origin is not None:
291+
ireq.download_info = cache_entry.origin
290292

291293
super().__init__(
292294
link=link,

src/pip/_internal/wheel_builder.py

+6
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,12 @@ def build(
354354
req.editable and req.permit_editable_wheels,
355355
)
356356
if wheel_file:
357+
# Record the download origin in the cache
358+
if req.download_info is not None:
359+
# download_info is guaranteed to be set because when we build an
360+
# InstallRequirement it has been through the preparer before, but
361+
# let's be cautious.
362+
wheel_cache.record_download_origin(cache_dir, req.download_info)
357363
# Update the link for this.
358364
req.link = Link(path_to_url(wheel_file))
359365
req.local_file_path = req.link.file_path

tests/functional/test_install.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1550,9 +1550,9 @@ def test_install_builds_wheels(script: PipTestEnvironment, data: TestData) -> No
15501550
)
15511551
# Must have installed it all
15521552
assert expected in str(res), str(res)
1553-
wheels = []
1553+
wheels: List[str] = []
15541554
for _, _, files in os.walk(wheels_cache):
1555-
wheels.extend(files)
1555+
wheels.extend(f for f in files if f.endswith(".whl"))
15561556
# and built wheels for upper and wheelbroken
15571557
assert "Building wheel for upper" in str(res), str(res)
15581558
assert "Building wheel for wheelb" in str(res), str(res)

0 commit comments

Comments
 (0)