Reasons to do this: - setuptools is no longer the only game in town. When cibuildwheel was created, setuptools was the only way to make wheels. these days, there are many build backends so special casing setuptools doesn't make so much sense - we don't need to preinstall `wheel` any more, setuptools will do that itself when it needs it. - projects with pyproject.toml (i.e. the vast majority of projects going forward) don't benefit from the pin anyway, because both `pip` and `build` create an isolated environment to install `build-system.requires` into. - this makes it easier for end users (such as @webknjaz) to control build-system versions using `PIP_CONSTRAINT`, because there's less potential for a user-specified version to conflict with one of our pins. - (long-term, I'd love `build` to [support this directly](https://github.com/pypa/build/issues/292), and then we could have a proper way to keep pyproject.toml dependencies loose and forward-compatible while retaining build determinism. But PIP_CONSTRAINT is the best we have for now. PIP_CONSTRAINT isn't ideal because it affects other things, like the test virtualenv). This PR also adds a pin for pypa/build. It appears that was missing before, but we should pin it.
163 lines
5.1 KiB
Python
163 lines
5.1 KiB
Python
from __future__ import annotations
|
|
|
|
import platform
|
|
import re
|
|
import textwrap
|
|
|
|
import pytest
|
|
|
|
import cibuildwheel.util
|
|
|
|
from . import test_projects, utils
|
|
|
|
project_with_expected_version_checks = test_projects.new_c_project(
|
|
setup_py_add=textwrap.dedent(
|
|
r"""
|
|
import subprocess
|
|
import os
|
|
|
|
versions_output_text = subprocess.check_output(
|
|
['pip', 'freeze', '--all', '-qq'],
|
|
universal_newlines=True,
|
|
)
|
|
versions = versions_output_text.strip().splitlines()
|
|
|
|
# `versions` now looks like:
|
|
# ['pip==x.x.x', 'setuptools==x.x.x', 'wheel==x.x.x']
|
|
|
|
print('Gathered versions', versions)
|
|
|
|
packages_to_test = ['pip']
|
|
|
|
for package_name in packages_to_test:
|
|
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
|
expected_version = os.environ[env_name]
|
|
|
|
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
|
'error: {} version should equal {}'.format(package_name, expected_version)
|
|
)
|
|
"""
|
|
)
|
|
)
|
|
|
|
|
|
VERSION_REGEX = r"([\w-]+)==([^\s]+)"
|
|
|
|
|
|
def get_versions_from_constraint_file(constraint_file):
|
|
constraint_file_text = constraint_file.read_text(encoding="utf8")
|
|
|
|
return dict(re.findall(VERSION_REGEX, constraint_file_text))
|
|
|
|
|
|
@pytest.mark.parametrize("python_version", ["3.6", "3.8", "3.10"])
|
|
def test_pinned_versions(tmp_path, python_version, build_frontend_env):
|
|
if utils.platform == "linux":
|
|
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
|
|
|
project_dir = tmp_path / "project"
|
|
project_with_expected_version_checks.generate(project_dir)
|
|
|
|
build_environment = {}
|
|
|
|
if python_version == "3.6":
|
|
if utils.platform == "macos" and platform.machine() == "arm64":
|
|
pytest.skip("macOS arm64 does not support Python 3.6")
|
|
constraint_filename = "constraints-python36.txt"
|
|
build_pattern = "[cp]p36-*"
|
|
elif python_version == "3.7":
|
|
constraint_filename = "constraints-python37.txt"
|
|
build_pattern = "[cp]p37-*"
|
|
elif python_version == "3.8":
|
|
constraint_filename = "constraints-python38.txt"
|
|
build_pattern = "[cp]p38-*"
|
|
else:
|
|
constraint_filename = "constraints-python310.txt"
|
|
build_pattern = "[cp]p310-*"
|
|
|
|
constraint_file = cibuildwheel.util.resources_dir / constraint_filename
|
|
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
|
|
|
for package in ["pip"]:
|
|
env_name = f"EXPECTED_{package.upper()}_VERSION"
|
|
build_environment[env_name] = constraint_versions[package]
|
|
|
|
cibw_environment_option = " ".join(f"{k}={v}" for k, v in build_environment.items())
|
|
|
|
# build and test the wheels
|
|
actual_wheels = utils.cibuildwheel_run(
|
|
project_dir,
|
|
add_env={
|
|
"CIBW_BUILD": build_pattern,
|
|
"CIBW_ENVIRONMENT": cibw_environment_option,
|
|
**build_frontend_env,
|
|
},
|
|
)
|
|
|
|
# also check that we got the right wheels
|
|
if python_version == "3.6":
|
|
expected_wheels = [
|
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp36" in w or "-pp36" in w
|
|
]
|
|
elif python_version == "3.8":
|
|
expected_wheels = [
|
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp38" in w or "-pp38" in w
|
|
]
|
|
elif python_version == "3.10":
|
|
expected_wheels = [
|
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp310" in w or "-pp310" in w
|
|
]
|
|
else:
|
|
msg = "unhandled python version"
|
|
raise ValueError(msg)
|
|
|
|
assert set(actual_wheels) == set(expected_wheels)
|
|
|
|
|
|
def test_dependency_constraints_file(tmp_path, build_frontend_env):
|
|
if utils.platform == "linux":
|
|
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
|
|
|
project_dir = tmp_path / "project"
|
|
project_with_expected_version_checks.generate(project_dir)
|
|
|
|
tool_versions = {
|
|
"pip": "23.1.2",
|
|
"delocate": "0.10.3",
|
|
}
|
|
|
|
constraints_file = tmp_path / "constraints file.txt"
|
|
constraints_file.write_text(
|
|
textwrap.dedent(
|
|
"""
|
|
pip=={pip}
|
|
delocate=={delocate}
|
|
importlib-metadata<3,>=0.12; python_version < "3.8"
|
|
""".format(**tool_versions)
|
|
)
|
|
)
|
|
|
|
build_environment = {}
|
|
|
|
for package_name, version in tool_versions.items():
|
|
env_name = f"EXPECTED_{package_name.upper()}_VERSION"
|
|
build_environment[env_name] = version
|
|
|
|
cibw_environment_option = " ".join(f"{k}={v}" for k, v in build_environment.items())
|
|
|
|
# build and test the wheels
|
|
actual_wheels = utils.cibuildwheel_run(
|
|
project_dir,
|
|
add_env={
|
|
"CIBW_ENVIRONMENT": cibw_environment_option,
|
|
"CIBW_DEPENDENCY_VERSIONS": str(constraints_file),
|
|
"CIBW_SKIP": "cp36-*",
|
|
**build_frontend_env,
|
|
},
|
|
)
|
|
|
|
# also check that we got the right wheels
|
|
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "-cp36" not in w]
|
|
|
|
assert set(actual_wheels) == set(expected_wheels)
|