Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tests/validate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import zipfile

import pytest
from packaging.specifiers import SpecifierSet
from packaging.tags import parse_tag

import validate
Expand Down Expand Up @@ -87,3 +88,10 @@ def test_top_imports_record(tmp_path):

expected = ["distlib", "_distlib_backend", "distlib_top"]
assert validate._top_imports(str(whl)) == expected


def test_pythons_to_check_with_python_versions_constraint():
tag = parse_tag("py2.py3-none-any")
constraint = SpecifierSet(">=3.12")
ret = validate._pythons_to_check(tag, constraint)
assert ret == ("python3.12", "python3.13")
48 changes: 37 additions & 11 deletions validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
from typing import NamedTuple

import packaging.tags
from packaging.specifiers import SpecifierSet
from packaging.tags import Tag
from packaging.utils import parse_wheel_filename
from packaging.version import Version

from build import Package

PYTHONS = ((3, 11), (3, 12), (3, 13))
DIST_INFO_RE = re.compile(r"^[^/]+.dist-info/[^/]+$")

Expand Down Expand Up @@ -44,28 +47,47 @@ def _py_exe(major: int, minor: int) -> str:
return f"python{major}.{minor}"


def _pythons_to_check(tags: frozenset[Tag]) -> tuple[str, ...]:
ret: set[str] = set()
def _pythons_to_check(
tags: frozenset[Tag], python_versions: SpecifierSet | None = None
) -> tuple[str, ...]:
tag_compatible_pythons: set[str] = set()
for tag in tags:
if tag.abi == "abi3" and tag.interpreter.startswith("cp"):
min_py = _parse_cp_tag(tag.interpreter)
ret.update(_py_exe(*py) for py in PYTHONS if py >= min_py)
tag_compatible_pythons.update(
_py_exe(*py) for py in PYTHONS if py >= min_py
)
elif tag.interpreter.startswith("cp"):
ret.add(_py_exe(*_parse_cp_tag(tag.interpreter)))
tag_compatible_pythons.add(_py_exe(*_parse_cp_tag(tag.interpreter)))
elif tag.interpreter == "py2":
continue
elif tag.interpreter.startswith("py3"):
for py in PYTHONS:
if tag not in packaging.tags.compatible_tags(py):
raise AssertionError(f"{tag} is not compatible with python {py}")
ret.update(_py_exe(*py) for py in PYTHONS)
tag_compatible_pythons.update(_py_exe(*py) for py in PYTHONS)
else:
raise AssertionError(f"unexpected tag: {tag}")

if not ret:
if not tag_compatible_pythons:
raise AssertionError(f"no interpreters found for {tags}")
else:
return tuple(sorted(ret))

if not python_versions:
return tuple(sorted(tag_compatible_pythons))

package_compatible_pythons: set[str] = set()
for python_exe in tag_compatible_pythons:
py_version_str = python_exe.replace("python", "")
if py_version_str in python_versions:
package_compatible_pythons.add(python_exe)

if not package_compatible_pythons:
raise AssertionError(
f"no interpreters found for {tags} after applying package constraints {python_versions}. "
f"Wheel supports: {sorted(tag_compatible_pythons)}"
)

return tuple(sorted(package_compatible_pythons))


def _top_imports(whl: str) -> list[str]:
Expand Down Expand Up @@ -157,14 +179,18 @@ def main() -> int:
raise SystemExit(f"{args.packages_ini}: not found")

packages = {}
validate_infos = {}
for k in cfg.sections():
pkg, _, version_s = k.partition("==")
packages[(pkg, Version(version_s))] = Info.from_dct(cfg[k])
key = (pkg, Version(version_s))
packages[key] = Package.make(k, cfg[k])
validate_infos[key] = Info.from_dct(cfg[k])

for filename in sorted(os.listdir(args.dist)):
name, version, _, wheel_tags = parse_wheel_filename(filename)
info = packages[(name, version)]
for python in _pythons_to_check(wheel_tags):
package = packages[(name, version)]
info = validate_infos[(name, version)]
for python in _pythons_to_check(wheel_tags, package.python_versions):
_validate(
python=python,
filename=os.path.join(args.dist, filename),
Expand Down
Loading