summaryrefslogtreecommitdiff
path: root/src/pip/_internal/operations/prepare.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/pip/_internal/operations/prepare.py')
-rw-r--r--src/pip/_internal/operations/prepare.py64
1 files changed, 54 insertions, 10 deletions
diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py
index 343a01bef..227331523 100644
--- a/src/pip/_internal/operations/prepare.py
+++ b/src/pip/_internal/operations/prepare.py
@@ -179,7 +179,10 @@ def unpack_url(
def _check_download_dir(
- link: Link, download_dir: str, hashes: Optional[Hashes]
+ link: Link,
+ download_dir: str,
+ hashes: Optional[Hashes],
+ warn_on_hash_mismatch: bool = True,
) -> Optional[str]:
"""Check download_dir for previously downloaded file with correct hash
If a correct file is found return its path else None
@@ -195,10 +198,11 @@ def _check_download_dir(
try:
hashes.check_against_path(download_path)
except HashMismatch:
- logger.warning(
- "Previously-downloaded file %s has bad hash. Re-downloading.",
- download_path,
- )
+ if warn_on_hash_mismatch:
+ logger.warning(
+ "Previously-downloaded file %s has bad hash. Re-downloading.",
+ download_path,
+ )
os.unlink(download_path)
return None
return download_path
@@ -263,7 +267,7 @@ class RequirementPreparer:
def _log_preparing_link(self, req: InstallRequirement) -> None:
"""Provide context for the requirement being prepared."""
- if req.link.is_file and not req.original_link_is_in_wheel_cache:
+ if req.link.is_file and not req.is_wheel_from_cache:
message = "Processing %s"
information = str(display_path(req.link.file_path))
else:
@@ -284,7 +288,7 @@ class RequirementPreparer:
self._previous_requirement_header = (message, information)
logger.info(message, information)
- if req.original_link_is_in_wheel_cache:
+ if req.is_wheel_from_cache:
with indent_log():
logger.info("Using cached %s", req.link.filename)
@@ -485,7 +489,18 @@ class RequirementPreparer:
file_path = None
if self.download_dir is not None and req.link.is_wheel:
hashes = self._get_linked_req_hashes(req)
- file_path = _check_download_dir(req.link, self.download_dir, hashes)
+ file_path = _check_download_dir(
+ req.link,
+ self.download_dir,
+ hashes,
+ # When a locally built wheel has been found in cache, we don't warn
+ # about re-downloading when the already downloaded wheel hash does
+ # not match. This is because the hash must be checked against the
+ # original link, not the cached link. It that case the already
+ # downloaded file will be removed and re-fetched from cache (which
+ # implies a hash check against the cache entry's origin.json).
+ warn_on_hash_mismatch=not req.is_wheel_from_cache,
+ )
if file_path is not None:
# The file is already available, so mark it as downloaded
@@ -536,9 +551,35 @@ class RequirementPreparer:
assert req.link
link = req.link
- self._ensure_link_req_src_dir(req, parallel_builds)
hashes = self._get_linked_req_hashes(req)
+ if hashes and req.is_wheel_from_cache:
+ assert req.download_info is not None
+ assert link.is_wheel
+ assert link.is_file
+ # We need to verify hashes, and we have found the requirement in the cache
+ # of locally built wheels.
+ if (
+ isinstance(req.download_info.info, ArchiveInfo)
+ and req.download_info.info.hashes
+ and hashes.has_one_of(req.download_info.info.hashes)
+ ):
+ # At this point we know the requirement was built from a hashable source
+ # artifact, and we verified that the cache entry's hash of the original
+ # artifact matches one of the hashes we expect. We don't verify hashes
+ # against the cached wheel, because the wheel is not the original.
+ hashes = None
+ else:
+ logger.warning(
+ "The hashes of the source archive found in cache entry "
+ "don't match, ignoring cached built wheel "
+ "and re-downloading source."
+ )
+ req.link = req.cached_wheel_source_link
+ link = req.link
+
+ self._ensure_link_req_src_dir(req, parallel_builds)
+
if link.is_existing_dir():
local_file = None
elif link.url not in self._downloaded:
@@ -571,12 +612,15 @@ class RequirementPreparer:
# Make sure we have a hash in download_info. If we got it as part of the
# URL, it will have been verified and we can rely on it. Otherwise we
# compute it from the downloaded file.
+ # FIXME: https://github.com/pypa/pip/issues/11943
if (
isinstance(req.download_info.info, ArchiveInfo)
- and not req.download_info.info.hash
+ and not req.download_info.info.hashes
and local_file
):
hash = hash_file(local_file.path)[0].hexdigest()
+ # We populate info.hash for backward compatibility.
+ # This will automatically populate info.hashes.
req.download_info.info.hash = f"sha256={hash}"
# For use in later processing,