Skip to content

Commit db062e8

Browse files
authored
Check wheel tags in pip check (#11088)
1 parent c2d706f commit db062e8

File tree

4 files changed

+78
-4
lines changed

4 files changed

+78
-4
lines changed

news/11054.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Check unsupported packages for the current platform.

src/pip/_internal/commands/check.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
from pip._internal.cli.base_command import Command
66
from pip._internal.cli.status_codes import ERROR, SUCCESS
7+
from pip._internal.metadata import get_default_environment
78
from pip._internal.operations.check import (
89
check_package_set,
10+
check_unsupported,
911
create_package_set_from_installed,
1012
)
13+
from pip._internal.utils.compatibility_tags import get_supported
1114
from pip._internal.utils.misc import write_output
1215

1316
logger = logging.getLogger(__name__)
@@ -23,6 +26,12 @@ class CheckCommand(Command):
2326
def run(self, options: Values, args: List[str]) -> int:
2427
package_set, parsing_probs = create_package_set_from_installed()
2528
missing, conflicting = check_package_set(package_set)
29+
unsupported = list(
30+
check_unsupported(
31+
get_default_environment().iter_installed_distributions(),
32+
get_supported(),
33+
)
34+
)
2635

2736
for project_name in missing:
2837
version = package_set[project_name].version
@@ -45,8 +54,13 @@ def run(self, options: Values, args: List[str]) -> int:
4554
dep_name,
4655
dep_version,
4756
)
48-
49-
if missing or conflicting or parsing_probs:
57+
for package in unsupported:
58+
write_output(
59+
"%s %s is not supported on this platform",
60+
package.raw_name,
61+
package.version,
62+
)
63+
if missing or conflicting or parsing_probs or unsupported:
5064
return ERROR
5165
else:
5266
write_output("No broken requirements found.")

src/pip/_internal/operations/check.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,30 @@
22
"""
33

44
import logging
5-
from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple
5+
from contextlib import suppress
6+
from email.parser import Parser
7+
from functools import reduce
8+
from typing import (
9+
Callable,
10+
Dict,
11+
FrozenSet,
12+
Generator,
13+
Iterable,
14+
List,
15+
NamedTuple,
16+
Optional,
17+
Set,
18+
Tuple,
19+
)
620

721
from pip._vendor.packaging.requirements import Requirement
22+
from pip._vendor.packaging.tags import Tag, parse_tag
823
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
924
from pip._vendor.packaging.version import Version
1025

1126
from pip._internal.distributions import make_distribution_for_install_requirement
1227
from pip._internal.metadata import get_default_environment
28+
from pip._internal.metadata.base import BaseDistribution
1329
from pip._internal.req.req_install import InstallRequirement
1430

1531
logger = logging.getLogger(__name__)
@@ -113,6 +129,22 @@ def check_install_conflicts(to_install: List[InstallRequirement]) -> ConflictDet
113129
)
114130

115131

132+
def check_unsupported(
133+
packages: Iterable[BaseDistribution],
134+
supported_tags: Iterable[Tag],
135+
) -> Generator[BaseDistribution, None, None]:
136+
for p in packages:
137+
with suppress(FileNotFoundError):
138+
wheel_file = p.read_text("WHEEL")
139+
wheel_tags: FrozenSet[Tag] = reduce(
140+
frozenset.union,
141+
map(parse_tag, Parser().parsestr(wheel_file).get_all("Tag", [])),
142+
frozenset(),
143+
)
144+
if wheel_tags.isdisjoint(supported_tags):
145+
yield p
146+
147+
116148
def _simulate_installation_of(
117149
to_install: List[InstallRequirement], package_set: PackageSet
118150
) -> Set[NormalizedName]:

tests/functional/test_check.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from typing import Collection
22

3-
from tests.lib import PipTestEnvironment, create_test_package_with_setup
3+
from tests.lib import (
4+
PipTestEnvironment,
5+
create_really_basic_wheel,
6+
create_test_package_with_setup,
7+
)
48

59

610
def matches_expected_lines(string: str, expected_lines: Collection[str]) -> bool:
@@ -321,3 +325,26 @@ def test_check_include_work_dir_pkg(script: PipTestEnvironment) -> None:
321325
expected_lines = ("simple 1.0 requires missing, which is not installed.",)
322326
assert matches_expected_lines(result.stdout, expected_lines)
323327
assert result.returncode == 1
328+
329+
330+
def test_check_unsupported(
331+
script: PipTestEnvironment,
332+
) -> None:
333+
script.scratch_path.joinpath("base-0.1.0-py2.py3-none-any.whl").write_bytes(
334+
create_really_basic_wheel("base", "0.1.0")
335+
)
336+
script.pip(
337+
"install",
338+
"--no-cache-dir",
339+
"--no-index",
340+
"--find-links",
341+
script.scratch_path,
342+
"base==0.1.0",
343+
)
344+
with open(
345+
script.site_packages_path.joinpath("base-0.1.0.dist-info/WHEEL"), "a"
346+
) as f:
347+
f.write("\nTag: cp310-cp310-musllinux_1_1_x86_64\n")
348+
result = script.pip("check", expect_error=True)
349+
assert "base 0.1.0 is not supported on this platform" in result.stdout
350+
assert result.returncode == 1

0 commit comments

Comments
 (0)