From ebc3883daccc6ee041928accd7d176c13a260c06 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Sun, 22 Oct 2023 11:26:16 -0400 Subject: [PATCH] Vendor pip 23.3.1 --- pipenv/patched/patched.txt | 2 +- pipenv/patched/pip/__init__.py | 2 +- pipenv/patched/pip/_internal/network/cache.py | 28 ++++++++++++++++--- .../pip/_internal/self_outdated_check.py | 11 +++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/pipenv/patched/patched.txt b/pipenv/patched/patched.txt index b4051e35..5a4ea846 100644 --- a/pipenv/patched/patched.txt +++ b/pipenv/patched/patched.txt @@ -1,2 +1,2 @@ -pip==23.3 +pip==23.3.1 safety==2.3.2 diff --git a/pipenv/patched/pip/__init__.py b/pipenv/patched/pip/__init__.py index 4259bb5c..d41caa79 100644 --- a/pipenv/patched/pip/__init__.py +++ b/pipenv/patched/pip/__init__.py @@ -1,6 +1,6 @@ from typing import List, Optional -__version__ = "23.3" +__version__ = "23.3.1" def main(args: Optional[List[str]] = None) -> int: diff --git a/pipenv/patched/pip/_internal/network/cache.py b/pipenv/patched/pip/_internal/network/cache.py index ab5c5ee3..f6f5beab 100644 --- a/pipenv/patched/pip/_internal/network/cache.py +++ b/pipenv/patched/pip/_internal/network/cache.py @@ -33,6 +33,18 @@ class SafeFileCache(SeparateBodyBaseCache): """ A file based cache which is safe to use even when the target directory may not be accessible or writable. + + There is a race condition when two processes try to write and/or read the + same entry at the same time, since each entry consists of two separate + files (https://github.com/psf/cachecontrol/issues/324). We therefore have + additional logic that makes sure that both files to be present before + returning an entry; this fixes the read side of the race condition. + + For the write side, we assume that the server will only ever return the + same data for the same URL, which ought to be the case for files pip is + downloading. PyPI does not have a mechanism to swap out a wheel for + another wheel, for example. If this assumption is not true, the + CacheControl issue will need to be fixed. """ def __init__(self, directory: str) -> None: @@ -49,9 +61,13 @@ class SafeFileCache(SeparateBodyBaseCache): return os.path.join(self.directory, *parts) def get(self, key: str) -> Optional[bytes]: - path = self._get_cache_path(key) + # The cache entry is only valid if both metadata and body exist. + metadata_path = self._get_cache_path(key) + body_path = metadata_path + ".body" + if not (os.path.exists(metadata_path) and os.path.exists(body_path)): + return None with suppressed_cache_errors(): - with open(path, "rb") as f: + with open(metadata_path, "rb") as f: return f.read() def _write(self, path: str, data: bytes) -> None: @@ -77,9 +93,13 @@ class SafeFileCache(SeparateBodyBaseCache): os.remove(path + ".body") def get_body(self, key: str) -> Optional[BinaryIO]: - path = self._get_cache_path(key) + ".body" + # The cache entry is only valid if both metadata and body exist. + metadata_path = self._get_cache_path(key) + body_path = metadata_path + ".body" + if not (os.path.exists(metadata_path) and os.path.exists(body_path)): + return None with suppressed_cache_errors(): - return open(path, "rb") + return open(body_path, "rb") def set_body(self, key: str, body: bytes) -> None: path = self._get_cache_path(key) + ".body" diff --git a/pipenv/patched/pip/_internal/self_outdated_check.py b/pipenv/patched/pip/_internal/self_outdated_check.py index 654eb73f..7ec8d6bb 100644 --- a/pipenv/patched/pip/_internal/self_outdated_check.py +++ b/pipenv/patched/pip/_internal/self_outdated_check.py @@ -39,6 +39,15 @@ def _get_statefile_name(key: str) -> str: return name +def _convert_date(isodate: str) -> datetime.datetime: + """Convert an ISO format string to a date. + + Handles the format 2020-01-22T14:24:01Z (trailing Z) + which is not supported by older versions of fromisoformat. + """ + return datetime.datetime.fromisoformat(isodate.replace("Z", "+00:00")) + + class SelfCheckState: def __init__(self, cache_dir: str) -> None: self._state: Dict[str, Any] = {} @@ -73,7 +82,7 @@ class SelfCheckState: return None # Determine if we need to refresh the state - last_check = datetime.datetime.fromisoformat(self._state["last_check"]) + last_check = _convert_date(self._state["last_check"]) time_since_last_check = current_time - last_check if time_since_last_check > _WEEK: return None