From 4fc297ff7b214eeaa2727a0925afcbbdc61b762b Mon Sep 17 00:00:00 2001 From: David Hotham Date: Sat, 17 Sep 2022 14:33:47 +0100 Subject: [PATCH] Prefer compatible wheels over source distribution --- src/poetry/repositories/http.py | 20 ++++++++-- tests/repositories/test_legacy_repository.py | 42 ++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/poetry/repositories/http.py b/src/poetry/repositories/http.py index c63f2b19281..bd34bdb6a12 100644 --- a/src/poetry/repositories/http.py +++ b/src/poetry/repositories/http.py @@ -13,6 +13,8 @@ import requests +from packaging.tags import Tag +from packaging.tags import sys_tags from poetry.core.packages.dependency import Dependency from poetry.core.packages.utils.link import Link from poetry.core.semver.helpers import parse_constraint @@ -111,7 +113,7 @@ def _get_info_from_urls(self, urls: dict[str, list[str]]) -> PackageInfo: universal_wheel = None universal_python2_wheel = None universal_python3_wheel = None - platform_specific_wheels = [] + platform_specific_wheels = {} for wheel in wheels: link = Link(wheel) m = wheel_file_re.match(link.filename) @@ -131,7 +133,12 @@ def _get_info_from_urls(self, urls: dict[str, list[str]]) -> PackageInfo: else: universal_python3_wheel = wheel else: - platform_specific_wheels.append(wheel) + pyvers = pyver.split(".") + abis = abi.split(".") + plats = plat.split(".") + tags = (Tag(x, y, z) for x in pyvers for y in abis for z in plats) + for tag in tags: + platform_specific_wheels[tag] = wheel if universal_wheel is not None: return self._get_info_from_wheel(universal_wheel) @@ -195,9 +202,16 @@ def _get_info_from_urls(self, urls: dict[str, list[str]]) -> PackageInfo: if universal_python2_wheel: return self._get_info_from_wheel(universal_python2_wheel) + # Prefer compatible platform wheel over sdist + system_tags = set(sys_tags()) + for tag, wheel in platform_specific_wheels.items(): + if tag in system_tags: + return self._get_info_from_wheel(wheel) + if platform_specific_wheels and "sdist" not in urls: # Pick the first wheel available and hope for the best - return self._get_info_from_wheel(platform_specific_wheels[0]) + first_wheel = next(iter(platform_specific_wheels.values())) + return self._get_info_from_wheel(first_wheel) return self._get_info_from_sdist(urls["sdist"][0]) diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 042e8064a52..9cd2867d4a2 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -10,11 +10,13 @@ import pytest import requests +from packaging.tags import Tag from packaging.utils import canonicalize_name from poetry.core.packages.dependency import Dependency from poetry.core.semver.version import Version from poetry.factory import Factory +from poetry.inspection.info import PackageInfo from poetry.repositories.exceptions import PackageNotFound from poetry.repositories.exceptions import RepositoryError from poetry.repositories.legacy_repository import LegacyRepository @@ -30,6 +32,7 @@ import httpretty from _pytest.monkeypatch import MonkeyPatch + from pytest_mock import MockerFixture from poetry.config.config import Config @@ -246,6 +249,45 @@ def test_find_packages_yanked(constraint: str, expected: list[str]) -> None: assert [str(p.version) for p in packages] == expected +@pytest.mark.parametrize( + "tags,bdist", + [ + (("cp37", "cp37m", "win32"), True), + (("cp310", "cp310", "manylinux_2_7_x86_64"), False), + ], +) +def test_get_package_dependencies_with_sdist_and_bdist_platform_compatible( + tags: tuple[str, str, str], bdist: bool, mocker: MockerFixture +) -> None: + get_info_from_wheel = mocker.patch( + "poetry.repositories.legacy_repository.LegacyRepository._get_info_from_wheel" + ) + get_info_from_sdist = mocker.patch( + "poetry.repositories.legacy_repository.LegacyRepository._get_info_from_sdist" + ) + name, version = "pyyaml", "3.13" + get_info_from_wheel.return_value = PackageInfo(name=name, version=version) + get_info_from_sdist.return_value = PackageInfo(name=name, version=version) + + sys_tags = mocker.patch("poetry.repositories.http.sys_tags") + sys_tags.return_value = [Tag(*tags)] + + name, version = "pyyaml", "3.13" + + repo = MockRepository() + package = repo.package(name, Version.parse(version)) + + assert package.name == name + assert package.version.text == version + + if bdist: + assert get_info_from_wheel.called + assert not get_info_from_sdist.called + else: + assert not get_info_from_wheel.called + assert get_info_from_sdist.called + + def test_get_package_information_chooses_correct_distribution() -> None: repo = MockRepository()