Files

208 lines
6.9 KiB
Python
Raw Permalink Normal View History

import json
2025-04-15 14:49:17 -04:00
import platform
2020-02-18 09:33:07 +00:00
import re
import subprocess
import textwrap
2024-10-22 10:16:59 -04:00
from pathlib import Path
2021-01-06 13:50:58 -05:00
import pytest
2025-01-27 20:35:56 +01:00
from cibuildwheel.util import resources
2020-02-18 09:33:07 +00:00
2021-01-06 13:50:58 -05:00
from . import test_projects, utils
2020-05-02 16:54:19 +01:00
VERSION_REGEX = r"([\w-]+)==([^\s]+)"
2020-05-02 16:54:19 +01:00
CHECK_VERSIONS_SCRIPT = """\
'''
Checks that the versions in the env var EXPECTED_VERSIONS match those
installed in the active venv.
'''
import os, subprocess, sys, json
2020-05-02 16:54:19 +01:00
versions_raw = json.loads(
subprocess.check_output([
sys.executable, '-m', 'pip', 'list', '--format=json',
], text=True)
2020-05-02 16:54:19 +01:00
)
versions = {item['name']: item['version'] for item in versions_raw}
expected_versions = json.loads(os.environ['EXPECTED_VERSIONS'])
2020-05-02 16:54:19 +01:00
for name, expected_version in expected_versions.items():
if name not in versions:
continue
if versions[name] != expected_version:
raise SystemExit(f'error: {name} version should equal {expected_version}. Versions: {versions}')
"""
2020-02-18 09:33:07 +00:00
2026-04-01 10:23:02 -04:00
def test_check_versions_script(
tmp_path: Path, build_frontend_env_nouv: dict[str, str], capfd: pytest.CaptureFixture[str]
) -> None:
if utils.get_platform() == "linux":
pytest.skip("we don't test dependency versions on linux, refer to other tests")
# sanity check that the CHECK_VERSIONS_SCRIPT fails when it should
project_dir = tmp_path / "project"
test_projects.new_c_project().generate(project_dir)
expected_versions = {
"pip": "0.0.1",
"build": "0.0.2",
}
script = project_dir / "check_versions.py"
script.write_text(CHECK_VERSIONS_SCRIPT)
with pytest.raises(subprocess.CalledProcessError):
utils.cibuildwheel_run(
project_dir,
add_env={
"CIBW_BEFORE_BUILD": f"python {script.name}",
"EXPECTED_VERSIONS": json.dumps(expected_versions),
**build_frontend_env_nouv,
},
)
captured = capfd.readouterr()
assert (
"error: pip version should equal 0.0.1" in captured.err
or "error: build version should equal 0.0.2" in captured.err
)
2020-02-18 09:33:07 +00:00
2024-10-22 10:16:59 -04:00
def get_versions_from_constraint_file(constraint_file: Path) -> dict[str, str]:
constraint_file_text = constraint_file.read_text(encoding="utf-8")
2020-02-18 09:33:07 +00:00
return dict(re.findall(VERSION_REGEX, constraint_file_text))
2020-02-18 09:33:07 +00:00
2025-02-25 20:23:01 +01:00
@pytest.mark.parametrize("python_version", ["3.8", "3.12"])
2026-04-01 10:23:02 -04:00
def test_pinned_versions(
tmp_path: Path, python_version: str, build_frontend_env_nouv: dict[str, str]
) -> None:
2025-05-16 11:11:16 +01:00
if utils.get_platform() == "linux":
2021-05-03 11:45:43 -04:00
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
2025-05-16 11:11:16 +01:00
if python_version != "3.12" and utils.get_platform() == "pyodide":
pytest.skip(f"pyodide does not support Python {python_version}")
2025-05-16 11:11:16 +01:00
if (
python_version == "3.8"
and utils.get_platform() == "windows"
and platform.machine() == "ARM64"
):
2025-04-15 14:49:17 -04:00
pytest.skip(f"Windows ARM64 does not support Python {python_version}")
2020-02-18 09:33:07 +00:00
2021-05-03 11:45:43 -04:00
project_dir = tmp_path / "project"
test_projects.new_c_project().generate(project_dir)
2020-02-18 09:33:07 +00:00
# read the expected versions from the appropriate constraint file
version_no_dot = python_version.replace(".", "")
# create cross-platform Python before-build script to verify versions pre-build
before_build_script = project_dir / "check_versions.py"
before_build_script.write_text(CHECK_VERSIONS_SCRIPT)
if utils.get_platform() == "pyodide":
constraint_filename = f"constraints-pyodide{version_no_dot}.txt"
else:
constraint_filename = f"constraints-python{version_no_dot}.txt"
2025-01-27 20:35:56 +01:00
constraint_file = resources.PATH / constraint_filename
2020-03-01 12:32:41 +00:00
constraint_versions = get_versions_from_constraint_file(constraint_file)
2020-02-18 09:33:07 +00:00
# build and test the wheels (dependency version check occurs before-build)
2021-04-30 17:56:34 -04:00
actual_wheels = utils.cibuildwheel_run(
project_dir,
add_env={
"CIBW_BUILD": f"[cp]p{version_no_dot}-*",
"CIBW_BEFORE_BUILD": f"python {before_build_script.name}",
"EXPECTED_VERSIONS": json.dumps(constraint_versions),
2024-06-09 15:45:31 -04:00
**build_frontend_env_nouv,
2021-04-30 17:56:34 -04:00
},
)
2020-02-18 09:33:07 +00:00
# also check that we got the right wheels
expected_wheels = [
w
for w in utils.expected_wheels("spam", "0.1.0")
if f"-cp{version_no_dot}" in w or f"-pp{version_no_dot}" in w
]
2020-02-18 20:21:08 +00:00
2020-02-18 09:33:07 +00:00
assert set(actual_wheels) == set(expected_wheels)
2025-03-10 21:45:54 +00:00
@pytest.mark.parametrize("method", ["inline", "file"])
2026-04-01 10:23:02 -04:00
def test_dependency_constraints(
method: str, tmp_path: Path, build_frontend_env_nouv: dict[str, str]
) -> None:
2025-05-16 11:11:16 +01:00
if utils.get_platform() == "linux":
2021-05-03 11:45:43 -04:00
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
2021-05-03 11:45:43 -04:00
project_dir = tmp_path / "project"
test_projects.new_c_project().generate(project_dir)
tool_versions = {
2023-05-26 21:14:55 -07:00
"pip": "23.1.2",
"build": "1.2.2",
"delocate": "0.10.3",
}
2025-03-10 21:45:54 +00:00
if method == "file":
constraints_file = tmp_path / "constraints file.txt"
constraints_file.write_text(
textwrap.dedent(
"""
pip=={pip}
build=={build}
2025-03-10 21:45:54 +00:00
delocate=={delocate}
""".format(**tool_versions)
)
2021-04-30 17:56:34 -04:00
)
2025-03-10 21:45:54 +00:00
dependency_version_option = str(constraints_file)
elif method == "inline":
dependency_version_option = "packages: " + " ".join(
f"{k}=={v}" for k, v in tool_versions.items()
)
else:
msg = f"Unknown method: {method}"
raise ValueError(msg)
skip = ""
2025-04-29 00:25:17 +02:00
if (
2025-05-16 11:11:16 +01:00
utils.get_platform() == "windows"
2025-04-29 00:25:17 +02:00
and method == "file"
and build_frontend_env_nouv["CIBW_BUILD_FRONTEND"] == "build"
):
# GraalPy 24 fails to discover its standard library when a venv is created
2025-04-29 00:25:17 +02:00
# from a virtualenv seeded executable. See
# https://github.com/oracle/graalpython/issues/491 and remove this once
# GraalPy 24 is dropped
skip = "gp311*"
2025-04-29 00:25:17 +02:00
# cross-platform Python script for dependency constraint checks
before_build_script = project_dir / "check_versions.py"
before_build_script.write_text(CHECK_VERSIONS_SCRIPT)
# build and test the wheels (dependency version check occurs pre-build)
2021-04-30 17:56:34 -04:00
actual_wheels = utils.cibuildwheel_run(
project_dir,
add_env={
"CIBW_SKIP": skip,
2025-03-10 21:45:54 +00:00
"CIBW_DEPENDENCY_VERSIONS": dependency_version_option,
"CIBW_BEFORE_BUILD": f"python {before_build_script.name}",
"EXPECTED_VERSIONS": json.dumps(tool_versions),
2024-06-09 15:45:31 -04:00
**build_frontend_env_nouv,
2021-04-30 17:56:34 -04:00
},
single_python=True,
2021-04-30 17:56:34 -04:00
)
# also check that we got the right wheels
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
if skip == "gp*":
2025-04-29 00:25:17 +02:00
# See reference to https://github.com/oracle/graalpython/issues/491
# above
expected_wheels = [w for w in expected_wheels if "graalpy311" not in w]
2025-04-29 00:25:17 +02:00
assert set(actual_wheels) == set(expected_wheels)