Skip to content

Commit 351aa56

Browse files
reuse should_cache(req) logic
1 parent 0f20f62 commit 351aa56

File tree

6 files changed

+90
-96
lines changed

6 files changed

+90
-96
lines changed

src/pip/_internal/cache.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import json
77
import logging
88
import os
9+
import re
910
from pathlib import Path
1011
from typing import Any, Dict, List, Optional
1112

@@ -16,14 +17,58 @@
1617
from pip._internal.models.direct_url import DirectUrl
1718
from pip._internal.models.link import Link
1819
from pip._internal.models.wheel import Wheel
20+
from pip._internal.req.req_install import InstallRequirement
1921
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
2022
from pip._internal.utils.urls import path_to_url
23+
from pip._internal.vcs import vcs
2124

2225
logger = logging.getLogger(__name__)
2326

27+
_egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE)
28+
2429
ORIGIN_JSON_NAME = "origin.json"
2530

2631

32+
def _contains_egg_info(s: str) -> bool:
33+
"""Determine whether the string looks like an egg_info.
34+
35+
:param s: The string to parse. E.g. foo-2.1
36+
"""
37+
return bool(_egg_info_re.search(s))
38+
39+
40+
def should_cache(
41+
req: InstallRequirement,
42+
) -> bool:
43+
"""
44+
Return whether a built InstallRequirement can be stored in the persistent
45+
wheel cache, assuming the wheel cache is available, and _should_build()
46+
has determined a wheel needs to be built.
47+
"""
48+
if req.editable or not req.source_dir:
49+
# never cache editable requirements
50+
return False
51+
52+
if req.link and req.link.is_vcs:
53+
# VCS checkout. Do not cache
54+
# unless it points to an immutable commit hash.
55+
assert not req.editable
56+
assert req.source_dir
57+
vcs_backend = vcs.get_backend_for_scheme(req.link.scheme)
58+
assert vcs_backend
59+
if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir):
60+
return True
61+
return False
62+
63+
assert req.link
64+
base, ext = req.link.splitext()
65+
if _contains_egg_info(base):
66+
return True
67+
68+
# Otherwise, do not cache.
69+
return False
70+
71+
2772
def _hash_dict(d: Dict[str, str]) -> str:
2873
"""Return a stable sha224 of a dictionary."""
2974
s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True)

src/pip/_internal/network/download.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def _get_http_response_filename(resp: Response, link: Link) -> str:
113113

114114

115115
def _http_get_download(session: PipSession, link: Link) -> Response:
116-
target_url = link.url.split("#", 1)[0]
116+
target_url = link.url_without_fragment
117117
resp = session.get(target_url, headers=HEADERS, stream=True)
118118
raise_for_status(resp)
119119
return resp

src/pip/_internal/operations/prepare.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
from typing import Any, Dict, Iterable, List, Optional
1515

1616
from pip._vendor.packaging.utils import canonicalize_name
17-
from pip._vendor.requests.exceptions import InvalidSchema
1817

19-
from pip._internal.cache import LinkMetadataCache
18+
from pip._internal.cache import LinkMetadataCache, should_cache
2019
from pip._internal.distributions import make_distribution_for_install_requirement
2120
from pip._internal.exceptions import (
2221
DirectoryUrlHashUnsupported,
@@ -416,7 +415,7 @@ def _fetch_metadata_only(
416415
)
417416
return None
418417

419-
cached_metadata = self._fetch_cached_metadata(req.link)
418+
cached_metadata = self._fetch_cached_metadata(req)
420419
if cached_metadata is not None:
421420
return cached_metadata
422421

@@ -425,29 +424,30 @@ def _fetch_metadata_only(
425424
) or self._fetch_metadata_using_lazy_wheel(req.link)
426425
# Populate the metadata cache.
427426
if computed_metadata is not None:
428-
self._cache_metadata(req.link, computed_metadata)
427+
self._cache_metadata(req, computed_metadata)
429428
return computed_metadata
430429

431430
def _fetch_cached_metadata(
432-
self,
433-
link: Link,
431+
self, req: InstallRequirement
434432
) -> Optional[BaseDistribution]:
435433
if self._metadata_cache is None:
436434
return None
435+
if not should_cache(req):
436+
return None
437437
try:
438-
cached_path = self._metadata_cache.cache_path(link)
438+
cached_path = self._metadata_cache.cache_path(req.link)
439439
os.makedirs(str(cached_path.parent), exist_ok=True)
440440
with cached_path.open("rb") as f:
441441
logger.debug(
442-
"found cached metadata for link %s at %s", link.url, f.name
442+
"found cached metadata for link %s at %s", req.link, f.name
443443
)
444444
args = json.load(f)
445445
cached_dist = CacheableDist.from_json(args)
446446
return cached_dist.to_dist()
447447
except (OSError, json.JSONDecodeError, KeyError) as e:
448448
logger.debug(
449449
"no cached metadata for link %s at %s %s(%s)",
450-
link.url,
450+
req.link,
451451
cached_path,
452452
e.__class__.__name__,
453453
str(e),
@@ -456,24 +456,26 @@ def _fetch_cached_metadata(
456456

457457
def _cache_metadata(
458458
self,
459-
link: Link,
459+
req: InstallRequirement,
460460
metadata_dist: BaseDistribution,
461461
) -> None:
462462
if self._metadata_cache is None:
463463
return
464+
if not should_cache(req):
465+
return
464466
try:
465-
cached_path = self._metadata_cache.cache_path(link)
467+
cached_path = self._metadata_cache.cache_path(req.link)
466468
os.makedirs(str(cached_path.parent), exist_ok=True)
467469
with cached_path.open("w") as f:
468-
cacheable_dist = CacheableDist.from_dist(link, metadata_dist)
470+
cacheable_dist = CacheableDist.from_dist(req.link, metadata_dist)
469471
args = cacheable_dist.to_json()
470-
logger.debug("caching metadata for link %s at %s", link.url, f.name)
472+
logger.debug("caching metadata for link %s at %s", req.link, f.name)
471473
json.dump(args, f)
472474
except (OSError, email.errors.HeaderParseError) as e:
473475
logger.debug(
474476
"could not cache metadata for dist %s from %s: %s(%s)",
475477
metadata_dist,
476-
link,
478+
req.link,
477479
e.__class__.__name__,
478480
str(e),
479481
)
@@ -564,17 +566,7 @@ def _complete_partial_requirements(
564566
links_to_fully_download: Dict[Link, InstallRequirement] = {}
565567
for req in partially_downloaded_reqs:
566568
assert req.link
567-
# If this is e.g. a git url, we don't know how to handle that in the
568-
# BatchDownloader, so leave it for self._prepare_linked_requirement() at the
569-
# end of this method, which knows how to handle any URL.
570-
can_simply_download = True
571-
try:
572-
# This will raise InvalidSchema if our Session can't download it.
573-
self._session.get_adapter(req.link.url)
574-
except InvalidSchema:
575-
can_simply_download = False
576-
if can_simply_download:
577-
links_to_fully_download[req.link] = req
569+
links_to_fully_download[req.link] = req
578570

579571
batch_download = self._batch_download(
580572
links_to_fully_download.keys(),
@@ -642,7 +634,7 @@ def prepare_linked_requirement(
642634
# None of the optimizations worked, fully prepare the requirement
643635
result = self._prepare_linked_requirement(req, parallel_builds)
644636
# Cache the metadata for later.
645-
self._cache_metadata(req.link, result)
637+
self._cache_metadata(req, result)
646638
return result
647639

648640
def _ensure_download_info(self, reqs: Iterable[InstallRequirement]) -> None:

src/pip/_internal/wheel_builder.py

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33

44
import logging
55
import os.path
6-
import re
76
import shutil
87
from typing import Iterable, List, Optional, Tuple
98

109
from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version
1110
from pip._vendor.packaging.version import InvalidVersion, Version
1211

13-
from pip._internal.cache import WheelCache
12+
from pip._internal.cache import WheelCache, should_cache
1413
from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel
1514
from pip._internal.metadata import FilesystemWheel, get_wheel_distribution
1615
from pip._internal.models.link import Link
@@ -25,23 +24,12 @@
2524
from pip._internal.utils.subprocess import call_subprocess
2625
from pip._internal.utils.temp_dir import TempDirectory
2726
from pip._internal.utils.urls import path_to_url
28-
from pip._internal.vcs import vcs
2927

3028
logger = logging.getLogger(__name__)
3129

32-
_egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE)
33-
3430
BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]]
3531

3632

37-
def _contains_egg_info(s: str) -> bool:
38-
"""Determine whether the string looks like an egg_info.
39-
40-
:param s: The string to parse. E.g. foo-2.1
41-
"""
42-
return bool(_egg_info_re.search(s))
43-
44-
4533
def _should_build(
4634
req: InstallRequirement,
4735
need_wheel: bool,
@@ -87,38 +75,6 @@ def should_build_for_install_command(
8775
return _should_build(req, need_wheel=False)
8876

8977

90-
def _should_cache(
91-
req: InstallRequirement,
92-
) -> Optional[bool]:
93-
"""
94-
Return whether a built InstallRequirement can be stored in the persistent
95-
wheel cache, assuming the wheel cache is available, and _should_build()
96-
has determined a wheel needs to be built.
97-
"""
98-
if req.editable or not req.source_dir:
99-
# never cache editable requirements
100-
return False
101-
102-
if req.link and req.link.is_vcs:
103-
# VCS checkout. Do not cache
104-
# unless it points to an immutable commit hash.
105-
assert not req.editable
106-
assert req.source_dir
107-
vcs_backend = vcs.get_backend_for_scheme(req.link.scheme)
108-
assert vcs_backend
109-
if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir):
110-
return True
111-
return False
112-
113-
assert req.link
114-
base, ext = req.link.splitext()
115-
if _contains_egg_info(base):
116-
return True
117-
118-
# Otherwise, do not cache.
119-
return False
120-
121-
12278
def _get_cache_dir(
12379
req: InstallRequirement,
12480
wheel_cache: WheelCache,
@@ -128,7 +84,7 @@ def _get_cache_dir(
12884
"""
12985
cache_available = bool(wheel_cache.cache_dir)
13086
assert req.link
131-
if cache_available and _should_cache(req):
87+
if cache_available and should_cache(req):
13288
cache_dir = wheel_cache.get_path_for_link(req.link)
13389
else:
13490
cache_dir = wheel_cache.get_ephem_path_for_link(req.link)

tests/unit/test_cache.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
import os
22
from pathlib import Path
33

4+
import pytest
45
from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
56

6-
from pip._internal.cache import WheelCache, _hash_dict
7+
from pip._internal.cache import WheelCache, _contains_egg_info, _hash_dict
78
from pip._internal.models.link import Link
89
from pip._internal.utils.misc import ensure_dir
910

1011

12+
@pytest.mark.parametrize(
13+
"s, expected",
14+
[
15+
# Trivial.
16+
("pip-18.0", True),
17+
# Ambiguous.
18+
("foo-2-2", True),
19+
("im-valid", True),
20+
# Invalid.
21+
("invalid", False),
22+
("im_invalid", False),
23+
],
24+
)
25+
def test_contains_egg_info(s: str, expected: bool) -> None:
26+
result = _contains_egg_info(s)
27+
assert result == expected
28+
29+
1130
def test_falsey_path_none() -> None:
1231
wc = WheelCache("")
1332
assert wc.cache_dir is None

tests/unit/test_wheel_builder.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,14 @@
55

66
import pytest
77

8-
from pip._internal import wheel_builder
8+
from pip._internal import cache, wheel_builder
99
from pip._internal.models.link import Link
1010
from pip._internal.operations.build.wheel_legacy import format_command_result
1111
from pip._internal.req.req_install import InstallRequirement
1212
from pip._internal.vcs.git import Git
1313
from tests.lib import _create_test_package
1414

1515

16-
@pytest.mark.parametrize(
17-
"s, expected",
18-
[
19-
# Trivial.
20-
("pip-18.0", True),
21-
# Ambiguous.
22-
("foo-2-2", True),
23-
("im-valid", True),
24-
# Invalid.
25-
("invalid", False),
26-
("im_invalid", False),
27-
],
28-
)
29-
def test_contains_egg_info(s: str, expected: bool) -> None:
30-
result = wheel_builder._contains_egg_info(s)
31-
assert result == expected
32-
33-
3416
class ReqMock:
3517
def __init__(
3618
self,
@@ -128,7 +110,7 @@ def test_should_build_for_wheel_command(req: ReqMock, expected: bool) -> None:
128110
],
129111
)
130112
def test_should_cache(req: ReqMock, expected: bool) -> None:
131-
assert wheel_builder._should_cache(cast(InstallRequirement, req)) is expected
113+
assert cache.should_cache(cast(InstallRequirement, req)) is expected
132114

133115

134116
def test_should_cache_git_sha(tmpdir: Path) -> None:
@@ -138,12 +120,12 @@ def test_should_cache_git_sha(tmpdir: Path) -> None:
138120
# a link referencing a sha should be cached
139121
url = "git+https://g.c/o/r@" + commit + "#egg=mypkg"
140122
req = ReqMock(link=Link(url), source_dir=repo_path)
141-
assert wheel_builder._should_cache(cast(InstallRequirement, req))
123+
assert cache.should_cache(cast(InstallRequirement, req))
142124

143125
# a link not referencing a sha should not be cached
144126
url = "git+https://g.c/o/r@master#egg=mypkg"
145127
req = ReqMock(link=Link(url), source_dir=repo_path)
146-
assert not wheel_builder._should_cache(cast(InstallRequirement, req))
128+
assert not cache.should_cache(cast(InstallRequirement, req))
147129

148130

149131
def test_format_command_result__INFO(caplog: pytest.LogCaptureFixture) -> None:

0 commit comments

Comments
 (0)