From 6236a12a3c8ea431be55bf01a4c3db635b475277 Mon Sep 17 00:00:00 2001 From: Jeongwon Kim Date: Wed, 3 Sep 2025 17:23:41 +0900 Subject: [PATCH 1/4] Fix duplicate best_compatible_tag_index call --- micropip/package_index.py | 16 +++++++++++++--- micropip/transaction.py | 5 ++++- micropip/wheelinfo.py | 3 +++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/micropip/package_index.py b/micropip/package_index.py index c7e4d086..5321e315 100644 --- a/micropip/package_index.py +++ b/micropip/package_index.py @@ -10,7 +10,11 @@ from urllib.parse import urljoin, urlparse from ._compat import CompatibilityLayer -from ._utils import is_package_compatible, parse_version +from ._utils import ( + best_compatible_tag_index, + parse_tags, + parse_version, +) from ._vendored.mousebender.simple import from_project_details_html from ._vendored.packaging.src.packaging.utils import InvalidWheelFilename from ._vendored.packaging.src.packaging.version import InvalidVersion, Version @@ -147,8 +151,13 @@ def _compatible_wheels( # Checking compatibility takes a bit of time, # so we use a generator to avoid doing it for all files. - compatible = is_package_compatible(filename) - if not compatible: + try: + tags = parse_tags(filename) + except (InvalidVersion, InvalidWheelFilename): + continue + + tag_index = best_compatible_tag_index(tags) + if tag_index is None: continue # JSON API has a "digests" key, while Simple API has a "hashes" key. @@ -177,6 +186,7 @@ def _compatible_wheels( size=size, core_metadata=core_metadata, yanked_reason=yanked_reason, + best_tag_index=tag_index, ) @classmethod diff --git a/micropip/transaction.py b/micropip/transaction.py index 3f6f1f8c..7084102e 100644 --- a/micropip/transaction.py +++ b/micropip/transaction.py @@ -413,7 +413,10 @@ def _find_best_wheel(wheels: Iterable[WheelInfo]) -> WheelInfo | None: best_wheel = None best_tag_index = float("infinity") for wheel in wheels: - tag_index = best_compatible_tag_index(wheel.tags) + tag_index = wheel._best_tag_index + if tag_index is None: + tag_index = best_compatible_tag_index(wheel.tags) + wheel._best_tag_index = tag_index if tag_index is not None and tag_index < best_tag_index: best_wheel = wheel best_tag_index = tag_index diff --git a/micropip/wheelinfo.py b/micropip/wheelinfo.py index d545dad9..f0ce5082 100644 --- a/micropip/wheelinfo.py +++ b/micropip/wheelinfo.py @@ -47,6 +47,7 @@ class WheelInfo: yanked_reason: str | bool = ( False # Whether the wheel has been yanked and the reason (if given) (PEP-592) ) + _best_tag_index: int | None = field(default=None, repr=False, compare=False) # Fields below are only available after downloading the wheel, i.e. after calling `download()`. @@ -101,6 +102,7 @@ def from_package_index( size: int | None, core_metadata: DistributionMetadata = None, yanked_reason: str | bool = False, + best_tag_index: int | None = None, ) -> "WheelInfo": """Extract available metadata from response received from package index""" parsed_url = urlparse(url) @@ -118,6 +120,7 @@ def from_package_index( size=size, core_metadata=core_metadata, yanked_reason=yanked_reason, + _best_tag_index=best_tag_index, ) async def install(self, target: Path) -> None: From a5e09993b6a8369b248c21c5ea6fb67bf12c7add Mon Sep 17 00:00:00 2001 From: Jeongwon Kim Date: Sat, 13 Sep 2025 22:50:33 +0900 Subject: [PATCH 2/4] Update is_package_compatible signature --- micropip/_utils.py | 14 ++++++-------- micropip/package_index.py | 15 +++------------ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/micropip/_utils.py b/micropip/_utils.py index 9abab9da..8bbc62e1 100644 --- a/micropip/_utils.py +++ b/micropip/_utils.py @@ -117,7 +117,7 @@ def best_compatible_tag_index(tags: frozenset[Tag]) -> int | None: return None -def is_package_compatible(filename: str) -> bool: +def is_package_compatible(filename: str) -> tuple[bool, int | None]: """ Check if a package is compatible with the current platform. @@ -128,17 +128,15 @@ def is_package_compatible(filename: str) -> bool: """ if not filename.endswith(".whl"): - return False - - if filename.endswith("py3-none-any.whl"): - return True + return False, None try: tags = parse_tags(filename) except (InvalidVersion, InvalidWheelFilename): - return False + return False, None - return best_compatible_tag_index(tags) is not None + tag_index = best_compatible_tag_index(tags) + return (tag_index is not None), tag_index def check_compatible(filename: str) -> None: @@ -146,7 +144,7 @@ def check_compatible(filename: str) -> None: Check if a package is compatible with the current platform. If not, raise an exception with a error message that explains why. """ - compatible = is_package_compatible(filename) + compatible, _ = is_package_compatible(filename) if compatible: return diff --git a/micropip/package_index.py b/micropip/package_index.py index 5321e315..b433a26e 100644 --- a/micropip/package_index.py +++ b/micropip/package_index.py @@ -10,11 +10,7 @@ from urllib.parse import urljoin, urlparse from ._compat import CompatibilityLayer -from ._utils import ( - best_compatible_tag_index, - parse_tags, - parse_version, -) +from ._utils import is_package_compatible, parse_version from ._vendored.mousebender.simple import from_project_details_html from ._vendored.packaging.src.packaging.utils import InvalidWheelFilename from ._vendored.packaging.src.packaging.version import InvalidVersion, Version @@ -151,13 +147,8 @@ def _compatible_wheels( # Checking compatibility takes a bit of time, # so we use a generator to avoid doing it for all files. - try: - tags = parse_tags(filename) - except (InvalidVersion, InvalidWheelFilename): - continue - - tag_index = best_compatible_tag_index(tags) - if tag_index is None: + compatible, tag_index = is_package_compatible(filename) + if not compatible: continue # JSON API has a "digests" key, while Simple API has a "hashes" key. From cd816f6b4b8df58bbd7d1a5eeea1600fcafac216 Mon Sep 17 00:00:00 2001 From: Jeongwon Kim Date: Sat, 13 Sep 2025 22:51:28 +0900 Subject: [PATCH 3/4] Create getter property best_tag_index --- micropip/transaction.py | 6 +----- micropip/wheelinfo.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/micropip/transaction.py b/micropip/transaction.py index 7084102e..abadffbe 100644 --- a/micropip/transaction.py +++ b/micropip/transaction.py @@ -10,7 +10,6 @@ from . import package_index from ._compat import CompatibilityLayer from ._utils import ( - best_compatible_tag_index, check_compatible, constrain_requirement, validate_constraints, @@ -413,10 +412,7 @@ def _find_best_wheel(wheels: Iterable[WheelInfo]) -> WheelInfo | None: best_wheel = None best_tag_index = float("infinity") for wheel in wheels: - tag_index = wheel._best_tag_index - if tag_index is None: - tag_index = best_compatible_tag_index(wheel.tags) - wheel._best_tag_index = tag_index + tag_index = wheel.best_tag_index if tag_index is not None and tag_index < best_tag_index: best_wheel = wheel best_tag_index = tag_index diff --git a/micropip/wheelinfo.py b/micropip/wheelinfo.py index f0ce5082..854e05b2 100644 --- a/micropip/wheelinfo.py +++ b/micropip/wheelinfo.py @@ -13,7 +13,7 @@ loadedPackages, to_js, ) -from ._utils import parse_wheel_filename +from ._utils import parse_wheel_filename, best_compatible_tag_index from ._vendored.packaging.src.packaging.requirements import Requirement from ._vendored.packaging.src.packaging.tags import Tag from ._vendored.packaging.src.packaging.version import Version @@ -63,6 +63,15 @@ def __post_init__(self): self.metadata_url = self.url + ".metadata" self.yanked = bool(self.yanked_reason) + @property + def best_tag_index(self) -> int | None: + """ + Returns an index if a compatible tag exists, otherwise None. + """ + if self._best_tag_index is None: + self._best_tag_index = best_compatible_tag_index(self.tags) + return self._best_tag_index + @classmethod def from_url(cls, url: str) -> "WheelInfo": """Parse wheels URL and extract available metadata From f64fc60822ded6a30f97e7b1d7e2f9f2f7fe1615 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 13 Sep 2025 13:52:30 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- micropip/wheelinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropip/wheelinfo.py b/micropip/wheelinfo.py index 854e05b2..43cf378d 100644 --- a/micropip/wheelinfo.py +++ b/micropip/wheelinfo.py @@ -13,7 +13,7 @@ loadedPackages, to_js, ) -from ._utils import parse_wheel_filename, best_compatible_tag_index +from ._utils import best_compatible_tag_index, parse_wheel_filename from ._vendored.packaging.src.packaging.requirements import Requirement from ._vendored.packaging.src.packaging.tags import Tag from ._vendored.packaging.src.packaging.version import Version