Skip to content

Commit 3992d57

Browse files
mayeuthenryiii
authored andcommitted
feat: add option to opt-in free-threaded builds
1 parent 345467c commit 3992d57

File tree

8 files changed

+75
-3
lines changed

8 files changed

+75
-3
lines changed

bin/generate_schema.py

+5
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@
104104
description: Set environment variables on the host to pass-through to the container
105105
during the build.
106106
type: string_array
107+
free-threaded-support:
108+
type: boolean
109+
default: false
110+
description: The project supports free-threaded builds of Python (PEP703)
107111
manylinux-aarch64-image:
108112
type: string
109113
description: Specify alternative manylinux / musllinux container images
@@ -248,6 +252,7 @@
248252
del non_global_options["build"]
249253
del non_global_options["skip"]
250254
del non_global_options["test-skip"]
255+
del non_global_options["free-threaded-support"]
251256

252257
overrides["items"]["properties"]["select"]["oneOf"] = string_array
253258
overrides["items"]["properties"] |= non_global_options.copy()

cibuildwheel/options.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def architectures(self) -> set[Architecture]:
117117
return self.globals.architectures
118118

119119

120-
Setting = Union[Mapping[str, str], Sequence[str], str, int]
120+
Setting = Union[Mapping[str, str], Sequence[str], str, int, bool]
121121

122122

123123
@dataclasses.dataclass(frozen=True)
@@ -196,7 +196,7 @@ def _resolve_cascade(
196196
if value is None:
197197
continue
198198

199-
if ignore_empty and not value:
199+
if ignore_empty and not value and value is not False:
200200
continue
201201

202202
value_string = _stringify_setting(value, list_sep, table_format)
@@ -258,7 +258,7 @@ def _stringify_setting(
258258
raise ConfigOptionError(msg)
259259
return list_sep.join(setting)
260260

261-
if isinstance(setting, int):
261+
if isinstance(setting, (bool, int)):
262262
return str(setting)
263263

264264
return setting
@@ -516,6 +516,10 @@ def globals(self) -> GlobalOptions:
516516
skip_config = self.reader.get("skip", env_plat=False, list_sep=" ")
517517
test_skip = self.reader.get("test-skip", env_plat=False, list_sep=" ")
518518

519+
free_threaded_support = strtobool(
520+
self.reader.get("free-threaded-support", env_plat=False, ignore_empty=True)
521+
)
522+
519523
prerelease_pythons = args.prerelease_pythons or strtobool(
520524
self.env.get("CIBW_PRERELEASE_PYTHONS", "0")
521525
)
@@ -536,12 +540,14 @@ def globals(self) -> GlobalOptions:
536540
skip_config = ""
537541
architectures = Architecture.all_archs(self.platform)
538542
prerelease_pythons = True
543+
free_threaded_support = True
539544

540545
build_selector = BuildSelector(
541546
build_config=build_config,
542547
skip_config=skip_config,
543548
requires_python=requires_python,
544549
prerelease_pythons=prerelease_pythons,
550+
free_threaded_support=free_threaded_support,
545551
)
546552
test_selector = TestSelector(skip_config=test_skip)
547553

cibuildwheel/resources/cibuildwheel.schema.json

+6
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,12 @@
255255
],
256256
"title": "CIBW_ENVIRONMENT_PASS"
257257
},
258+
"free-threaded-support": {
259+
"type": "boolean",
260+
"default": false,
261+
"description": "The project supports free-threaded builds of Python (PEP703)",
262+
"title": "CIBW_FREE_THREADED_SUPPORT"
263+
},
258264
"manylinux-aarch64-image": {
259265
"type": "string",
260266
"description": "Specify alternative manylinux / musllinux container images",

cibuildwheel/resources/defaults.toml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
build = "*"
33
skip = ""
44
test-skip = ""
5+
free-threaded-support = false
56

67
archs = ["auto"]
78
build-frontend = "default"

cibuildwheel/util.py

+7
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ class BuildSelector:
241241
PRERELEASE_SKIP: ClassVar[str] = "cp313-* cp313t-*"
242242
prerelease_pythons: bool = False
243243

244+
free_threaded_support: bool = False
245+
244246
def __call__(self, build_id: str) -> bool:
245247
# Filter build selectors by python_requires if set
246248
if self.requires_python is not None:
@@ -257,6 +259,10 @@ def __call__(self, build_id: str) -> bool:
257259
if not self.prerelease_pythons and selector_matches(self.PRERELEASE_SKIP, build_id):
258260
return False
259261

262+
# filter out free threaded pythons if self.free_threaded_support is False
263+
if not self.free_threaded_support and selector_matches("*t-*", build_id):
264+
return False
265+
260266
should_build = selector_matches(self.build_config, build_id)
261267
should_skip = selector_matches(self.skip_config, build_id)
262268

@@ -268,6 +274,7 @@ def options_summary(self) -> Any:
268274
"skip_config": self.skip_config,
269275
"requires_python": str(self.requires_python),
270276
"prerelease_pythons": self.prerelease_pythons,
277+
"free_threaded_support": self.free_threaded_support,
271278
}
272279

273280

test/utils.py

+5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ def cibuildwheel_get_build_identifiers(project_path, env=None, *, prerelease_pyt
3737
cmd = [sys.executable, "-m", "cibuildwheel", "--print-build-identifiers", str(project_path)]
3838
if prerelease_pythons:
3939
cmd.append("--prerelease-pythons")
40+
if env is None:
41+
env = os.environ.copy()
42+
env.setdefault("CIBW_FREE_THREADED_SUPPORT", "1")
4043

4144
cmd_output = subprocess.run(
4245
cmd,
@@ -94,6 +97,8 @@ def cibuildwheel_run(
9497

9598
_update_pip_cache_dir(env)
9699

100+
env.setdefault("CIBW_FREE_THREADED_SUPPORT", "1")
101+
97102
with TemporaryDirectory() as tmp_output_dir:
98103
subprocess.run(
99104
[

unit_test/build_selector_test.py

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def test_build_filter_pre():
5050
assert build_selector("cp313-manylinux_x86_64")
5151
assert build_selector("cp37-win_amd64")
5252
assert build_selector("cp313-win_amd64")
53+
assert not build_selector("cp313t-manylinux_x86_64")
5354

5455

5556
def test_skip():
@@ -144,6 +145,14 @@ def test_build_limited_python_patch():
144145
assert build_selector("cp37-manylinux_x86_64")
145146

146147

148+
def test_build_free_threaded_python():
149+
build_selector = BuildSelector(
150+
build_config="*", skip_config="", prerelease_pythons=True, free_threaded_support=True
151+
)
152+
153+
assert build_selector("cp313t-manylinux_x86_64")
154+
155+
147156
def test_testing_selector():
148157
# local import to avoid pytest trying to collect this as a test class!
149158
from cibuildwheel.util import TestSelector

unit_test/options_test.py

+33
Original file line numberDiff line numberDiff line change
@@ -415,3 +415,36 @@ def test_override_inherit_environment_with_references(tmp_path: Path):
415415
) == {
416416
"PATH": "/opt/local/bin:/opt/bin:/usr/bin:/bin",
417417
}
418+
419+
420+
@pytest.mark.parametrize(
421+
("toml_assignment", "env", "expected_result"),
422+
[
423+
("", {}, False),
424+
("free-threaded-support = true", {}, True),
425+
("free-threaded-support = false", {}, False),
426+
("", {"CIBW_FREE_THREADED_SUPPORT": "0"}, False),
427+
("", {"CIBW_FREE_THREADED_SUPPORT": "1"}, True),
428+
("free-threaded-support = false", {"CIBW_FREE_THREADED_SUPPORT": "1"}, True),
429+
("free-threaded-support = true", {"CIBW_FREE_THREADED_SUPPORT": "0"}, False),
430+
("free-threaded-support = true", {"CIBW_FREE_THREADED_SUPPORT": ""}, True),
431+
("free-threaded-support = false", {"CIBW_FREE_THREADED_SUPPORT": ""}, False),
432+
],
433+
)
434+
def test_free_threaded_support(
435+
tmp_path: Path, toml_assignment: str, env: dict[str, str], expected_result: bool
436+
):
437+
args = CommandLineArguments.defaults()
438+
args.package_dir = tmp_path
439+
440+
pyproject_toml: Path = tmp_path / "pyproject.toml"
441+
pyproject_toml.write_text(
442+
textwrap.dedent(
443+
f"""\
444+
[tool.cibuildwheel]
445+
{toml_assignment}
446+
"""
447+
)
448+
)
449+
options = Options(platform="linux", command_line_arguments=args, env=env)
450+
assert options.globals.build_selector.free_threaded_support is expected_result

0 commit comments

Comments
 (0)