Inline dependency-versions syntax (#2122)

* Write docs for inline dependency-versions

* Change the `inline` keyword to `packages` for better readability

* Implement inline package constraints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add schema for TOML properties

* Change the parsing of the option to parse filenames as-is

* Add a unit test for table-parsing of the option

* Remove unneeded shlex.quote on the dependency-version test

* Tidy-ups, comments, docs fixes

* Add test for empty packages option value

* Fix empty packages scenario

And, remove some optionals to reduce the problem space

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Joe Rickerby
2025-03-10 21:45:54 +00:00
committed by GitHub
co-authored by pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
parent f5e502aa88
commit 25f2d3f313
13 changed files with 375 additions and 96 deletions
+42 -8
View File
@@ -1,21 +1,55 @@
from pathlib import Path
import pytest
from cibuildwheel.util.packaging import DependencyConstraints
def test_defaults():
dependency_constraints = DependencyConstraints.with_defaults()
def test_defaults(tmp_path: Path) -> None:
dependency_constraints = DependencyConstraints.pinned()
project_root = Path(__file__).parents[1]
resources_dir = project_root / "cibuildwheel" / "resources"
assert dependency_constraints.base_file_path
assert dependency_constraints.base_file_path.samefile(resources_dir / "constraints.txt")
assert dependency_constraints.get_for_python_version("3.99").samefile(
resources_dir / "constraints.txt"
constraints_file = dependency_constraints.get_for_python_version(
version="3.99", tmp_dir=tmp_path
)
assert dependency_constraints.get_for_python_version("3.9").samefile(
resources_dir / "constraints-python39.txt"
assert constraints_file
assert constraints_file.samefile(resources_dir / "constraints.txt")
constraints_file = dependency_constraints.get_for_python_version(
version="3.9", tmp_dir=tmp_path
)
assert dependency_constraints.get_for_python_version("3.13").samefile(
resources_dir / "constraints-python313.txt"
assert constraints_file
assert constraints_file.samefile(resources_dir / "constraints-python39.txt")
constraints_file = dependency_constraints.get_for_python_version(
version="3.13", tmp_dir=tmp_path
)
assert constraints_file
assert constraints_file.samefile(resources_dir / "constraints-python313.txt")
def test_inline_packages(tmp_path: Path) -> None:
dependency_constraints = DependencyConstraints(
base_file_path=None,
packages=["foo==1.2.3", "bar==4.5.6"],
)
constraint_file = dependency_constraints.get_for_python_version(version="x.x", tmp_dir=tmp_path)
assert constraint_file
constraints_file_contents = constraint_file.read_text()
assert constraints_file_contents == "foo==1.2.3\nbar==4.5.6"
@pytest.mark.parametrize("config_string", ["", "latest", "packages:"])
def test_empty_constraints(config_string: str) -> None:
dependency_constraints = DependencyConstraints.from_config_string(config_string)
assert not dependency_constraints.packages
assert not dependency_constraints.base_file_path
assert dependency_constraints == DependencyConstraints.latest()
+38
View File
@@ -11,6 +11,7 @@ from cibuildwheel.frontend import _split_config_settings
from cibuildwheel.options import BuildOptions, _get_pinned_container_images
from cibuildwheel.selector import BuildSelector, EnableGroup
from cibuildwheel.util import resources
from cibuildwheel.util.packaging import DependencyConstraints
# CIBW_PLATFORM is tested in main_platform_test.py
@@ -342,6 +343,43 @@ def test_before_all(before_all, platform_specific, platform, intercepted_build_a
assert build_options.before_all == (before_all or "")
@pytest.mark.parametrize(
"dependency_versions",
[None, "pinned", "latest", "FILE", "packages: pip==21.0.0"],
)
@pytest.mark.parametrize("platform_specific", [False, True])
def test_dependency_versions(
dependency_versions, platform_specific, platform, intercepted_build_args, monkeypatch, tmp_path
):
option_value = dependency_versions
if dependency_versions == "FILE":
constraints_file = tmp_path / "constraints.txt"
constraints_file.write_text("foo==1.2.3\nbar==4.5.6")
option_value = str(constraints_file)
if option_value is not None:
if platform_specific:
monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS_" + platform.upper(), option_value)
monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS", "overwritten")
else:
monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS", option_value)
main()
build_options: BuildOptions = intercepted_build_args.args[0].build_options(identifier=None)
dependency_constraints = build_options.dependency_constraints
if dependency_versions is None or dependency_versions == "pinned":
assert dependency_constraints == DependencyConstraints.pinned()
elif dependency_versions == "latest":
assert dependency_constraints == DependencyConstraints.latest()
elif dependency_versions == "FILE":
assert dependency_constraints.base_file_path
assert dependency_constraints.base_file_path.samefile(Path(option_value))
elif dependency_versions.startswith("packages:"):
assert dependency_constraints.packages == ["pip==21.0.0"]
@pytest.mark.parametrize("method", ["unset", "command_line", "env_var"])
def test_debug_traceback(monkeypatch, method, capfd):
if method == "command_line":
+61
View File
@@ -14,6 +14,8 @@ from cibuildwheel.options import (
_get_pinned_container_images,
)
from cibuildwheel.selector import EnableGroup
from cibuildwheel.util import resources
from cibuildwheel.util.packaging import DependencyConstraints
PYPROJECT_1 = """
[tool.cibuildwheel]
@@ -468,3 +470,62 @@ def test_free_threaded_support(
assert EnableGroup.CPythonFreeThreading in options.globals.build_selector.enable
else:
assert EnableGroup.CPythonFreeThreading not in options.globals.build_selector.enable
@pytest.mark.parametrize(
("toml_assignment", "base_file_path", "packages"),
[
("", resources.CONSTRAINTS, []),
("dependency-versions = 'pinned'", resources.CONSTRAINTS, []),
("dependency-versions = 'latest'", None, []),
("dependency-versions = 'constraints file.txt'", Path("constraints file.txt"), []),
(
"dependency-versions = \"file:'constraints file.txt'\"",
Path("constraints file.txt"),
[],
),
(
"dependency-versions = {file = 'constraints file.txt'}",
Path("constraints file.txt"),
[],
),
(
"dependency-versions = 'packages: foo==1.2.3 bar==4.5.6'",
None,
["foo==1.2.3", "bar==4.5.6"],
),
],
)
def test_dependency_versions_toml(
tmp_path: Path,
toml_assignment: str,
base_file_path: Path | None,
packages: list[str] | None,
monkeypatch: pytest.MonkeyPatch,
) -> None:
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
(tmp_path / "constraints file.txt").write_text("")
monkeypatch.chdir(tmp_path)
pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel]
{toml_assignment}
"""
)
)
options = Options(platform="linux", command_line_arguments=args, env={})
parsed_dependency_constraints = options.build_options(None).dependency_constraints
if base_file_path is None and packages is None:
assert parsed_dependency_constraints == DependencyConstraints.latest()
else:
if parsed_dependency_constraints.base_file_path and base_file_path:
assert parsed_dependency_constraints.base_file_path.samefile(base_file_path)
else:
assert parsed_dependency_constraints.base_file_path == base_file_path
assert parsed_dependency_constraints.packages == packages