Skip to content

Commit 901db9c

Browse files
authored
Use a set for TargetPython.get_tags for performance (#12204)
1 parent d311e6e commit 901db9c

File tree

6 files changed

+29
-16
lines changed

6 files changed

+29
-16
lines changed

news/12204.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve use of datastructures to make candidate selection 1.6x faster

src/pip/_internal/commands/debug.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def show_tags(options: Values) -> None:
105105
tag_limit = 10
106106

107107
target_python = make_target_python(options)
108-
tags = target_python.get_tags()
108+
tags = target_python.get_sorted_tags()
109109

110110
# Display the target options that were explicitly provided.
111111
formatted_target = target_python.format_given()

src/pip/_internal/index/package_finder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def evaluate_link(self, link: Link) -> Tuple[LinkType, str]:
198198
reason = f"wrong project name (not {self.project_name})"
199199
return (LinkType.different_project, reason)
200200

201-
supported_tags = self._target_python.get_tags()
201+
supported_tags = self._target_python.get_unsorted_tags()
202202
if not wheel.supported(supported_tags):
203203
# Include the wheel's tags in the reason string to
204204
# simplify troubleshooting compatibility issues.
@@ -414,7 +414,7 @@ def create(
414414
if specifier is None:
415415
specifier = specifiers.SpecifierSet()
416416

417-
supported_tags = target_python.get_tags()
417+
supported_tags = target_python.get_sorted_tags()
418418

419419
return cls(
420420
project_name=project_name,

src/pip/_internal/models/target_python.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sys
2-
from typing import List, Optional, Tuple
2+
from typing import List, Optional, Set, Tuple
33

44
from pip._vendor.packaging.tags import Tag
55

@@ -22,6 +22,7 @@ class TargetPython:
2222
"py_version",
2323
"py_version_info",
2424
"_valid_tags",
25+
"_valid_tags_set",
2526
]
2627

2728
def __init__(
@@ -61,8 +62,9 @@ def __init__(
6162
self.py_version = py_version
6263
self.py_version_info = py_version_info
6364

64-
# This is used to cache the return value of get_tags().
65+
# This is used to cache the return value of get_(un)sorted_tags.
6566
self._valid_tags: Optional[List[Tag]] = None
67+
self._valid_tags_set: Optional[Set[Tag]] = None
6668

6769
def format_given(self) -> str:
6870
"""
@@ -84,7 +86,7 @@ def format_given(self) -> str:
8486
f"{key}={value!r}" for key, value in key_values if value is not None
8587
)
8688

87-
def get_tags(self) -> List[Tag]:
89+
def get_sorted_tags(self) -> List[Tag]:
8890
"""
8991
Return the supported PEP 425 tags to check wheel candidates against.
9092
@@ -108,3 +110,13 @@ def get_tags(self) -> List[Tag]:
108110
self._valid_tags = tags
109111

110112
return self._valid_tags
113+
114+
def get_unsorted_tags(self) -> Set[Tag]:
115+
"""Exactly the same as get_sorted_tags, but returns a set.
116+
117+
This is important for performance.
118+
"""
119+
if self._valid_tags_set is None:
120+
self._valid_tags_set = set(self.get_sorted_tags())
121+
122+
return self._valid_tags_set

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
132132
if not link.is_wheel:
133133
return
134134
wheel = Wheel(link.filename)
135-
if wheel.supported(self._finder.target_python.get_tags()):
135+
if wheel.supported(self._finder.target_python.get_unsorted_tags()):
136136
return
137137
msg = f"{link.filename} is not a supported wheel on this platform."
138138
raise UnsupportedWheel(msg)

tests/unit/test_target_python.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ def test_format_given(self, kwargs: Dict[str, Any], expected: str) -> None:
8888
((3, 7, 3), "37"),
8989
# Check a minor version with two digits.
9090
((3, 10, 1), "310"),
91-
# Check that versions=None is passed to get_tags().
91+
# Check that versions=None is passed to get_sorted_tags().
9292
(None, None),
9393
],
9494
)
9595
@mock.patch("pip._internal.models.target_python.get_supported")
96-
def test_get_tags(
96+
def test_get_sorted_tags(
9797
self,
9898
mock_get_supported: mock.Mock,
9999
py_version_info: Optional[Tuple[int, ...]],
@@ -102,7 +102,7 @@ def test_get_tags(
102102
mock_get_supported.return_value = ["tag-1", "tag-2"]
103103

104104
target_python = TargetPython(py_version_info=py_version_info)
105-
actual = target_python.get_tags()
105+
actual = target_python.get_sorted_tags()
106106
assert actual == ["tag-1", "tag-2"]
107107

108108
actual = mock_get_supported.call_args[1]["version"]
@@ -111,14 +111,14 @@ def test_get_tags(
111111
# Check that the value was cached.
112112
assert target_python._valid_tags == ["tag-1", "tag-2"]
113113

114-
def test_get_tags__uses_cached_value(self) -> None:
114+
def test_get_unsorted_tags__uses_cached_value(self) -> None:
115115
"""
116-
Test that get_tags() uses the cached value.
116+
Test that get_unsorted_tags() uses the cached value.
117117
"""
118118
target_python = TargetPython(py_version_info=None)
119-
target_python._valid_tags = [
119+
target_python._valid_tags_set = {
120120
Tag("py2", "none", "any"),
121121
Tag("py3", "none", "any"),
122-
]
123-
actual = target_python.get_tags()
124-
assert actual == [Tag("py2", "none", "any"), Tag("py3", "none", "any")]
122+
}
123+
actual = target_python.get_unsorted_tags()
124+
assert actual == {Tag("py2", "none", "any"), Tag("py3", "none", "any")}

0 commit comments

Comments
 (0)