Skip to content

Commit 393e035

Browse files
henryiiimayeut
andauthored
feat: adding enable (#2048)
* feat: adding enable Signed-off-by: Henry Schreiner <[email protected]> * refactor: use recommended term Signed-off-by: Henry Schreiner <[email protected]> * fix: use `cached_property` for `Options` properties * fix: warn on PyPy default --------- Signed-off-by: Henry Schreiner <[email protected]> Co-authored-by: mayeut <[email protected]>
1 parent 9459533 commit 393e035

File tree

9 files changed

+207
-99
lines changed

9 files changed

+207
-99
lines changed

bin/generate_schema.py

+15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
- append
2727
default: none
2828
description: How to inherit the parent's value.
29+
enable:
30+
enum:
31+
- cpython-freethreading
32+
- cpython-prerelease
33+
- pypy
34+
description: A Python version or flavor to enable.
2935
additionalProperties: false
3036
description: cibuildwheel's settings.
3137
type: object
@@ -99,6 +105,13 @@
99105
default: pinned
100106
description: Specify how cibuildwheel controls the versions of the tools it uses
101107
type: string
108+
enable:
109+
description: Enable or disable certain builds.
110+
oneOf:
111+
- $ref: "#/$defs/enable"
112+
- type: array
113+
items:
114+
$ref: "#/$defs/enable"
102115
environment:
103116
description: Set environment variables needed during the build.
104117
type: string_table
@@ -110,6 +123,7 @@
110123
type: boolean
111124
default: false
112125
description: The project supports free-threaded builds of Python (PEP703)
126+
deprecated: Use the `enable` option instead.
113127
manylinux-aarch64-image:
114128
type: string
115129
description: Specify alternative manylinux / musllinux container images
@@ -261,6 +275,7 @@
261275
del non_global_options["skip"]
262276
del non_global_options["test-skip"]
263277
del non_global_options["free-threaded-support"]
278+
del non_global_options["enable"]
264279

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

cibuildwheel/options.py

+47-13
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
BuildFrontendConfig,
3131
BuildSelector,
3232
DependencyConstraints,
33+
EnableGroups,
3334
TestSelector,
3435
format_safe,
36+
read_python_configs,
3537
resources_dir,
3638
selector_matches,
3739
strtobool,
@@ -512,6 +514,7 @@ def get(
512514
env_plat: bool = True,
513515
option_format: OptionFormat | None = None,
514516
ignore_empty: bool = False,
517+
env_rule: InheritRule = InheritRule.NONE,
515518
) -> str:
516519
"""
517520
Get and return the value for the named option from environment,
@@ -543,8 +546,8 @@ def get(
543546
(o.options.get(name), o.inherit.get(name, InheritRule.NONE))
544547
for o in self.active_config_overrides
545548
],
546-
(self.env.get(envvar), InheritRule.NONE),
547-
(self.env.get(plat_envvar) if env_plat else None, InheritRule.NONE),
549+
(self.env.get(envvar), env_rule),
550+
(self.env.get(plat_envvar) if env_plat else None, env_rule),
548551
ignore_empty=ignore_empty,
549552
option_format=option_format,
550553
)
@@ -558,14 +561,15 @@ def __init__(
558561
platform: PlatformName,
559562
command_line_arguments: CommandLineArguments,
560563
env: Mapping[str, str],
561-
read_config_file: bool = True,
564+
defaults: bool = False,
562565
):
563566
self.platform = platform
564567
self.command_line_arguments = command_line_arguments
565568
self.env = env
569+
self._defaults = defaults
566570

567571
self.reader = OptionsReader(
568-
self.config_file_path if read_config_file else None,
572+
None if defaults else self.config_file_path,
569573
platform=platform,
570574
env=env,
571575
disallow=DISALLOWED_OPTIONS,
@@ -578,7 +582,7 @@ def __init__(
578582
except FileNotFoundError:
579583
self.pyproject_toml = None
580584

581-
@property
585+
@functools.cached_property
582586
def config_file_path(self) -> Path | None:
583587
args = self.command_line_arguments
584588

@@ -596,7 +600,7 @@ def config_file_path(self) -> Path | None:
596600
def package_requires_python_str(self) -> str | None:
597601
return get_requires_python_str(self.package_dir, self.pyproject_toml)
598602

599-
@property
603+
@functools.cached_property
600604
def globals(self) -> GlobalOptions:
601605
args = self.command_line_arguments
602606
package_dir = args.package_dir
@@ -608,16 +612,34 @@ def globals(self) -> GlobalOptions:
608612
skip_config = self.reader.get("skip", env_plat=False, option_format=ListFormat(sep=" "))
609613
test_skip = self.reader.get("test-skip", env_plat=False, option_format=ListFormat(sep=" "))
610614

615+
allow_empty = args.allow_empty or strtobool(self.env.get("CIBW_ALLOW_EMPTY", "0"))
616+
617+
enable_groups = self.reader.get(
618+
"enable", env_plat=False, option_format=ListFormat(sep=" "), env_rule=InheritRule.APPEND
619+
)
620+
enable = {EnableGroups(group) for group in enable_groups.split()}
621+
611622
free_threaded_support = strtobool(
612623
self.reader.get("free-threaded-support", env_plat=False, ignore_empty=True)
613624
)
614625

615-
allow_empty = args.allow_empty or strtobool(self.env.get("CIBW_ALLOW_EMPTY", "0"))
616-
617626
prerelease_pythons = args.prerelease_pythons or strtobool(
618627
self.env.get("CIBW_PRERELEASE_PYTHONS", "0")
619628
)
620629

630+
if free_threaded_support or prerelease_pythons:
631+
msg = (
632+
"free-threaded-support and prerelease-pythons should be specified by enable instead"
633+
)
634+
if enable:
635+
raise OptionsReaderError(msg)
636+
log.warning(msg)
637+
638+
if free_threaded_support:
639+
enable.add(EnableGroups.CPythonFreeThreading)
640+
if prerelease_pythons:
641+
enable.add(EnableGroups.CPythonPrerelease)
642+
621643
# This is not supported in tool.cibuildwheel, as it comes from a standard location.
622644
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
623645
requires_python_str: str | None = (
@@ -633,18 +655,30 @@ def globals(self) -> GlobalOptions:
633655
build_config = args.only
634656
skip_config = ""
635657
architectures = Architecture.all_archs(self.platform)
636-
prerelease_pythons = True
637-
free_threaded_support = True
658+
enable = set(EnableGroups)
638659

639660
build_selector = BuildSelector(
640661
build_config=build_config,
641662
skip_config=skip_config,
642663
requires_python=requires_python,
643-
prerelease_pythons=prerelease_pythons,
644-
free_threaded_support=free_threaded_support,
664+
enable=frozenset(
665+
enable | {EnableGroups.PyPy}
666+
), # For backwards compatibility, we are adding PyPy for now
645667
)
646668
test_selector = TestSelector(skip_config=test_skip)
647669

670+
all_configs = read_python_configs(self.platform)
671+
all_pypy_ids = {
672+
config["identifier"] for config in all_configs if config["identifier"].startswith("pp")
673+
}
674+
if (
675+
not self._defaults
676+
and EnableGroups.PyPy not in enable
677+
and any(build_selector(build_id) for build_id in all_pypy_ids)
678+
):
679+
msg = "PyPy builds will be disabled by default in version 3. Enabling PyPy builds should be specified by enable"
680+
log.warning(msg)
681+
648682
return GlobalOptions(
649683
package_dir=package_dir,
650684
output_dir=output_dir,
@@ -831,7 +865,7 @@ def defaults(self) -> Options:
831865
platform=self.platform,
832866
command_line_arguments=CommandLineArguments.defaults(),
833867
env={},
834-
read_config_file=False,
868+
defaults=True,
835869
)
836870

837871
def summary(self, identifiers: Iterable[str]) -> str:

cibuildwheel/resources/cibuildwheel.schema.json

+25-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,16 @@
1010
],
1111
"default": "none",
1212
"description": "How to inherit the parent's value."
13-
}
13+
},
14+
"enable": {
15+
"enum": [
16+
"cpython-eol",
17+
"cpython-freethreading",
18+
"cpython-prerelease",
19+
"pypy-eol"
20+
]
21+
},
22+
"description": "A Python version or flavor to enable."
1423
},
1524
"additionalProperties": false,
1625
"description": "cibuildwheel's settings.",
@@ -228,6 +237,21 @@
228237
"type": "string",
229238
"title": "CIBW_DEPENDENCY_VERSIONS"
230239
},
240+
"enable": {
241+
"description": "Enable or disable certain builds.",
242+
"oneOf": [
243+
{
244+
"$ref": "#/$defs/enable"
245+
},
246+
{
247+
"type": "array",
248+
"items": {
249+
"$ref": "#/$defs/enable"
250+
}
251+
}
252+
],
253+
"title": "CIBW_ENABLE"
254+
},
231255
"environment": {
232256
"description": "Set environment variables needed during the build.",
233257
"oneOf": [

cibuildwheel/resources/defaults.toml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ build = "*"
33
skip = ""
44
test-skip = ""
55
free-threaded-support = false
6+
enable = []
67

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

cibuildwheel/util.py

+24-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import contextlib
4+
import enum
45
import fnmatch
56
import itertools
67
import os
@@ -23,7 +24,7 @@
2324
from pathlib import Path, PurePath
2425
from tempfile import TemporaryDirectory
2526
from time import sleep
26-
from typing import Any, ClassVar, Final, Literal, TextIO, TypeVar
27+
from typing import Any, Final, Literal, TextIO, TypeVar
2728
from zipfile import ZipFile
2829

2930
import bracex
@@ -41,6 +42,7 @@
4142

4243
__all__ = [
4344
"MANYLINUX_ARCHS",
45+
"EnableGroups",
4446
"call",
4547
"chdir",
4648
"combine_constraints",
@@ -66,6 +68,16 @@
6668
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
6769

6870

71+
class EnableGroups(enum.Enum):
72+
"""
73+
Groups of build selectors that are not enabled by default.
74+
"""
75+
76+
CPythonFreeThreading = "cpython-freethreading"
77+
CPythonPrerelease = "cpython-prerelease"
78+
PyPy = "pypy"
79+
80+
6981
MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
7082
"x86_64",
7183
"i686",
@@ -247,12 +259,7 @@ class BuildSelector:
247259
build_config: str
248260
skip_config: str
249261
requires_python: SpecifierSet | None = None
250-
251-
# a pattern that skips prerelease versions, when include_prereleases is False.
252-
PRERELEASE_SKIP: ClassVar[str] = ""
253-
prerelease_pythons: bool = False
254-
255-
free_threaded_support: bool = False
262+
enable: frozenset[EnableGroups] = frozenset()
256263

257264
def __call__(self, build_id: str) -> bool:
258265
# Filter build selectors by python_requires if set
@@ -266,12 +273,16 @@ def __call__(self, build_id: str) -> bool:
266273
if not self.requires_python.contains(version):
267274
return False
268275

269-
# filter out the prerelease pythons if self.prerelease_pythons is False
270-
if not self.prerelease_pythons and selector_matches(self.PRERELEASE_SKIP, build_id):
276+
# filter out groups that are not enabled
277+
if EnableGroups.CPythonFreeThreading not in self.enable and selector_matches(
278+
"cp3??t-*", build_id
279+
):
271280
return False
272-
273-
# filter out free threaded pythons if self.free_threaded_support is False
274-
if not self.free_threaded_support and selector_matches("*t-*", build_id):
281+
if EnableGroups.CPythonPrerelease not in self.enable and selector_matches(
282+
"cp314*", build_id
283+
):
284+
return False
285+
if EnableGroups.PyPy not in self.enable and selector_matches("pp*", build_id):
275286
return False
276287

277288
should_build = selector_matches(self.build_config, build_id)
@@ -284,8 +295,7 @@ def options_summary(self) -> Any:
284295
"build_config": self.build_config,
285296
"skip_config": self.skip_config,
286297
"requires_python": str(self.requires_python),
287-
"prerelease_pythons": self.prerelease_pythons,
288-
"free_threaded_support": self.free_threaded_support,
298+
"enable": sorted(group.value for group in self.enable),
289299
}
290300

291301

0 commit comments

Comments
 (0)