Skip to content

Commit ed7a296

Browse files
committed
Add support for verifying tags on install
Signed-off-by: Michał Górny <mgorny@gentoo.org>
1 parent 2cefb8a commit ed7a296

5 files changed

Lines changed: 75 additions & 3 deletions

File tree

gpep517/__main__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,16 @@ def add_install_args(parser):
161161
action="store_true",
162162
help="Symlink .pyc files between optimization levels "
163163
"if their contents match")
164-
165164
group.add_argument("--symlink-to",
166165
type=PurePath,
167166
help="Install symlinks to another directory rather "
168167
"than files if they match respective paths "
169168
"in the other directory (useful for deduplicating "
170169
"packages across Python implementations)")
170+
group.add_argument("--verify-tags",
171+
action="store_true",
172+
help="Verify wheel compatibility via platform "
173+
"compatibility tags (requires packaging)")
171174

172175

173176
def main(argv=sys.argv):

gpep517/install.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@
1212
from gpep517.utils import DEFAULT_PREFIX, install_scheme_dict, logger
1313

1414

15+
def verify_tags(wheel: Path) -> None:
16+
from packaging.tags import sys_tags
17+
from packaging.utils import parse_wheel_filename
18+
19+
wheel_tags = parse_wheel_filename(str(wheel.name))[-1]
20+
if not any(compatible_tag in wheel_tags
21+
for compatible_tag in sys_tags()):
22+
raise RuntimeError(
23+
f"Wheel {wheel} is not compatible with the system")
24+
25+
1526
def install_wheel_impl(args, wheel: Path):
1627
from installer import install
1728
from installer.destinations import SchemeDictionaryDestination
@@ -128,6 +139,9 @@ def _compile_bytecode(self,
128139
pyc2.unlink()
129140
pyc2.symlink_to(pyc1.name)
130141

142+
if args.verify_tags:
143+
verify_tags(wheel)
144+
131145
with WheelFile.open(wheel) as source:
132146
dest = DeduplicatingDestination(
133147
install_scheme_dict(args.prefix or DEFAULT_PREFIX,

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ dependencies = [
2525
]
2626

2727
[project.optional-dependencies]
28+
tags = [
29+
"packaging >= 20.9",
30+
]
2831
test = [
32+
# for abi3t tags
33+
"packaging >= 26.1",
2934
"pytest",
3035
]
3136
test-full = [

test/test_install.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
# (c) 2022-2025 Michał Górny
1+
# (c) 2022-2026 Michał Górny
22
# SPDX-License-Identifier: GPL-2.0-or-later
33

44
import contextlib
55
import importlib.machinery
66
import importlib.util
77
import os
88
import pathlib
9+
import shutil
10+
import sys
911
import sysconfig
1012
import typing
1113

14+
import packaging.tags
1215
import pytest
1316

1417
from gpep517 import __version__
@@ -379,3 +382,49 @@ def test_install_symlink_chain(tmp_path,
379382
assert expected_links == {path: tmp_path.joinpath(path).readlink()
380383
for path, path_data in expected.items()
381384
if path_data is not None and path_data[1]}
385+
386+
387+
BEST_TAG = next(packaging.tags.sys_tags())
388+
FREETHREADING = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
389+
IS_CPYTHON = sys.implementation.name == "cpython"
390+
391+
@pytest.mark.parametrize(
392+
("wheel_name", "expected"),
393+
[
394+
("test-1-py3-none-any.whl", True),
395+
("test-1-py2.py3-none-any.whl", True),
396+
("test-1-py38-none-any.whl", sys.version_info >= (3, 8)),
397+
(f"test-1-{BEST_TAG}.whl", True),
398+
(f"test-1-{BEST_TAG.interpreter}-none-any.whl", IS_CPYTHON),
399+
(f"test-1-{BEST_TAG.interpreter}-{BEST_TAG.abi}-any.whl", False),
400+
(f"test-1-{BEST_TAG.interpreter}-abi3-any.whl", False),
401+
(f"test-1-py3-none-{BEST_TAG.platform}.whl", True),
402+
(f"test-1-py38-none-{BEST_TAG.platform}.whl", sys.version_info >= (3, 8)),
403+
(f"test-1-{BEST_TAG.interpreter}-none-{BEST_TAG.platform}.whl", True),
404+
(f"test-1-{BEST_TAG.interpreter}-abi3-{BEST_TAG.platform}.whl", IS_CPYTHON and not FREETHREADING),
405+
(f"test-1-{BEST_TAG.interpreter}-abi3t-{BEST_TAG.platform}.whl", IS_CPYTHON and FREETHREADING and sys.version_info >= (3, 15)),
406+
(f"test-1-{BEST_TAG.interpreter}-abi3.abi3t-{BEST_TAG.platform}.whl", IS_CPYTHON),
407+
(f"test-1-cp38-abi3-{BEST_TAG.platform}.whl", IS_CPYTHON and sys.version_info >= (3, 8) and not FREETHREADING),
408+
(f"test-1-cp38-abi3.abi3t-{BEST_TAG.platform}.whl", IS_CPYTHON and sys.version_info >= (3, 8)),
409+
(f"test-1-cp315-abi3.abi3t-{BEST_TAG.platform}.whl", IS_CPYTHON and sys.version_info >= (3, 15)),
410+
(f"test-1-py3-none-win32.whl", sysconfig.get_platform() == "win32"),
411+
(f"test-1-py3-none-linux_x86_64.whl", sysconfig.get_platform() == "linux-x86_64"),
412+
]
413+
)
414+
def test_verify_tags(tmp_path,
415+
wheel_name: str,
416+
expected: bool,
417+
) -> None:
418+
shutil.copy("test/test-pkg/dist/test-1-py3-none-any.whl",
419+
tmp_path / wheel_name)
420+
421+
ctx_mgr = ( contextlib.nullcontext() if expected else
422+
pytest.raises(RuntimeError,
423+
match="not compatible with the system"))
424+
args = (["", "install-wheel",
425+
"--destdir", str(tmp_path),
426+
"--verify-tags",
427+
str(tmp_path / wheel_name)]
428+
)
429+
with ctx_mgr:
430+
assert 0 == main(args)

test/test_integration.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ def test_integration_install(tmp_path, buildsys, verify_zipfile_cleanup,
105105
with pushd(tmp_path):
106106
assert 0 == main(["", "install-from-source",
107107
"--destdir", str(destdir),
108-
"--prefix", "/usr"])
108+
"--prefix", "/usr",
109+
"--verify-tags"])
109110

110111
sitedir = destdir / (sysconfig.get_path("purelib", vars={"base": "/usr"})
111112
.lstrip(os.path.sep))

0 commit comments

Comments
 (0)