feat: adding enable (#2048)

* feat: adding enable

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* refactor: use recommended term

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: use `cached_property` for `Options` properties

* fix: warn on PyPy default

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
This commit is contained in:
Henry Schreiner
2024-11-14 11:39:52 -05:00
committed by GitHub
co-authored by mayeut
parent 9459533408
commit 393e0352e8
9 changed files with 207 additions and 99 deletions
+15
View File
@@ -26,6 +26,12 @@ $defs:
- append
default: none
description: How to inherit the parent's value.
enable:
enum:
- cpython-freethreading
- cpython-prerelease
- pypy
description: A Python version or flavor to enable.
additionalProperties: false
description: cibuildwheel's settings.
type: object
@@ -99,6 +105,13 @@ properties:
default: pinned
description: Specify how cibuildwheel controls the versions of the tools it uses
type: string
enable:
description: Enable or disable certain builds.
oneOf:
- $ref: "#/$defs/enable"
- type: array
items:
$ref: "#/$defs/enable"
environment:
description: Set environment variables needed during the build.
type: string_table
@@ -110,6 +123,7 @@ properties:
type: boolean
default: false
description: The project supports free-threaded builds of Python (PEP703)
deprecated: Use the `enable` option instead.
manylinux-aarch64-image:
type: string
description: Specify alternative manylinux / musllinux container images
@@ -261,6 +275,7 @@ del non_global_options["build"]
del non_global_options["skip"]
del non_global_options["test-skip"]
del non_global_options["free-threaded-support"]
del non_global_options["enable"]
overrides["items"]["properties"]["select"]["oneOf"] = string_array
overrides["items"]["properties"] |= non_global_options.copy()
+47 -13
View File
@@ -30,8 +30,10 @@ from .util import (
BuildFrontendConfig,
BuildSelector,
DependencyConstraints,
EnableGroups,
TestSelector,
format_safe,
read_python_configs,
resources_dir,
selector_matches,
strtobool,
@@ -512,6 +514,7 @@ class OptionsReader:
env_plat: bool = True,
option_format: OptionFormat | None = None,
ignore_empty: bool = False,
env_rule: InheritRule = InheritRule.NONE,
) -> str:
"""
Get and return the value for the named option from environment,
@@ -543,8 +546,8 @@ class OptionsReader:
(o.options.get(name), o.inherit.get(name, InheritRule.NONE))
for o in self.active_config_overrides
],
(self.env.get(envvar), InheritRule.NONE),
(self.env.get(plat_envvar) if env_plat else None, InheritRule.NONE),
(self.env.get(envvar), env_rule),
(self.env.get(plat_envvar) if env_plat else None, env_rule),
ignore_empty=ignore_empty,
option_format=option_format,
)
@@ -558,14 +561,15 @@ class Options:
platform: PlatformName,
command_line_arguments: CommandLineArguments,
env: Mapping[str, str],
read_config_file: bool = True,
defaults: bool = False,
):
self.platform = platform
self.command_line_arguments = command_line_arguments
self.env = env
self._defaults = defaults
self.reader = OptionsReader(
self.config_file_path if read_config_file else None,
None if defaults else self.config_file_path,
platform=platform,
env=env,
disallow=DISALLOWED_OPTIONS,
@@ -578,7 +582,7 @@ class Options:
except FileNotFoundError:
self.pyproject_toml = None
@property
@functools.cached_property
def config_file_path(self) -> Path | None:
args = self.command_line_arguments
@@ -596,7 +600,7 @@ class Options:
def package_requires_python_str(self) -> str | None:
return get_requires_python_str(self.package_dir, self.pyproject_toml)
@property
@functools.cached_property
def globals(self) -> GlobalOptions:
args = self.command_line_arguments
package_dir = args.package_dir
@@ -608,16 +612,34 @@ class Options:
skip_config = self.reader.get("skip", env_plat=False, option_format=ListFormat(sep=" "))
test_skip = self.reader.get("test-skip", env_plat=False, option_format=ListFormat(sep=" "))
allow_empty = args.allow_empty or strtobool(self.env.get("CIBW_ALLOW_EMPTY", "0"))
enable_groups = self.reader.get(
"enable", env_plat=False, option_format=ListFormat(sep=" "), env_rule=InheritRule.APPEND
)
enable = {EnableGroups(group) for group in enable_groups.split()}
free_threaded_support = strtobool(
self.reader.get("free-threaded-support", env_plat=False, ignore_empty=True)
)
allow_empty = args.allow_empty or strtobool(self.env.get("CIBW_ALLOW_EMPTY", "0"))
prerelease_pythons = args.prerelease_pythons or strtobool(
self.env.get("CIBW_PRERELEASE_PYTHONS", "0")
)
if free_threaded_support or prerelease_pythons:
msg = (
"free-threaded-support and prerelease-pythons should be specified by enable instead"
)
if enable:
raise OptionsReaderError(msg)
log.warning(msg)
if free_threaded_support:
enable.add(EnableGroups.CPythonFreeThreading)
if prerelease_pythons:
enable.add(EnableGroups.CPythonPrerelease)
# This is not supported in tool.cibuildwheel, as it comes from a standard location.
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
requires_python_str: str | None = (
@@ -633,18 +655,30 @@ class Options:
build_config = args.only
skip_config = ""
architectures = Architecture.all_archs(self.platform)
prerelease_pythons = True
free_threaded_support = True
enable = set(EnableGroups)
build_selector = BuildSelector(
build_config=build_config,
skip_config=skip_config,
requires_python=requires_python,
prerelease_pythons=prerelease_pythons,
free_threaded_support=free_threaded_support,
enable=frozenset(
enable | {EnableGroups.PyPy}
), # For backwards compatibility, we are adding PyPy for now
)
test_selector = TestSelector(skip_config=test_skip)
all_configs = read_python_configs(self.platform)
all_pypy_ids = {
config["identifier"] for config in all_configs if config["identifier"].startswith("pp")
}
if (
not self._defaults
and EnableGroups.PyPy not in enable
and any(build_selector(build_id) for build_id in all_pypy_ids)
):
msg = "PyPy builds will be disabled by default in version 3. Enabling PyPy builds should be specified by enable"
log.warning(msg)
return GlobalOptions(
package_dir=package_dir,
output_dir=output_dir,
@@ -831,7 +865,7 @@ class Options:
platform=self.platform,
command_line_arguments=CommandLineArguments.defaults(),
env={},
read_config_file=False,
defaults=True,
)
def summary(self, identifiers: Iterable[str]) -> str:
@@ -10,7 +10,16 @@
],
"default": "none",
"description": "How to inherit the parent's value."
}
},
"enable": {
"enum": [
"cpython-eol",
"cpython-freethreading",
"cpython-prerelease",
"pypy-eol"
]
},
"description": "A Python version or flavor to enable."
},
"additionalProperties": false,
"description": "cibuildwheel's settings.",
@@ -228,6 +237,21 @@
"type": "string",
"title": "CIBW_DEPENDENCY_VERSIONS"
},
"enable": {
"description": "Enable or disable certain builds.",
"oneOf": [
{
"$ref": "#/$defs/enable"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/enable"
}
}
],
"title": "CIBW_ENABLE"
},
"environment": {
"description": "Set environment variables needed during the build.",
"oneOf": [
+1
View File
@@ -3,6 +3,7 @@ build = "*"
skip = ""
test-skip = ""
free-threaded-support = false
enable = []
archs = ["auto"]
build-frontend = "default"
+24 -14
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import contextlib
import enum
import fnmatch
import itertools
import os
@@ -23,7 +24,7 @@ from functools import lru_cache, total_ordering
from pathlib import Path, PurePath
from tempfile import TemporaryDirectory
from time import sleep
from typing import Any, ClassVar, Final, Literal, TextIO, TypeVar
from typing import Any, Final, Literal, TextIO, TypeVar
from zipfile import ZipFile
import bracex
@@ -41,6 +42,7 @@ from .typing import PathOrStr, PlatformName
__all__ = [
"MANYLINUX_ARCHS",
"EnableGroups",
"call",
"chdir",
"combine_constraints",
@@ -66,6 +68,16 @@ free_thread_enable_313: Final[Path] = resources_dir / "free-threaded-enable-313.
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
class EnableGroups(enum.Enum):
"""
Groups of build selectors that are not enabled by default.
"""
CPythonFreeThreading = "cpython-freethreading"
CPythonPrerelease = "cpython-prerelease"
PyPy = "pypy"
MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64",
"i686",
@@ -247,12 +259,7 @@ class BuildSelector:
build_config: str
skip_config: str
requires_python: SpecifierSet | None = None
# a pattern that skips prerelease versions, when include_prereleases is False.
PRERELEASE_SKIP: ClassVar[str] = ""
prerelease_pythons: bool = False
free_threaded_support: bool = False
enable: frozenset[EnableGroups] = frozenset()
def __call__(self, build_id: str) -> bool:
# Filter build selectors by python_requires if set
@@ -266,12 +273,16 @@ class BuildSelector:
if not self.requires_python.contains(version):
return False
# filter out the prerelease pythons if self.prerelease_pythons is False
if not self.prerelease_pythons and selector_matches(self.PRERELEASE_SKIP, build_id):
# filter out groups that are not enabled
if EnableGroups.CPythonFreeThreading not in self.enable and selector_matches(
"cp3??t-*", build_id
):
return False
# filter out free threaded pythons if self.free_threaded_support is False
if not self.free_threaded_support and selector_matches("*t-*", build_id):
if EnableGroups.CPythonPrerelease not in self.enable and selector_matches(
"cp314*", build_id
):
return False
if EnableGroups.PyPy not in self.enable and selector_matches("pp*", build_id):
return False
should_build = selector_matches(self.build_config, build_id)
@@ -284,8 +295,7 @@ class BuildSelector:
"build_config": self.build_config,
"skip_config": self.skip_config,
"requires_python": str(self.requires_python),
"prerelease_pythons": self.prerelease_pythons,
"free_threaded_support": self.free_threaded_support,
"enable": sorted(group.value for group in self.enable),
}
+72 -60
View File
@@ -422,52 +422,6 @@ See the [cibuildwheel 1 documentation](https://cibuildwheel.pypa.io/en/1.x/) for
}
</style>
### `CIBW_FREE_THREADED_SUPPORT` {: #free-threaded-support}
> Choose whether free-threaded variants should be built
[PEP 703](https://www.python.org/dev/peps/pep-0703) introduced variants of CPython that can be built without the Global Interpreter Lock (GIL).
Those variants are also known as free-threaded / no-gil.
Building for free-threaded variants is disabled by default.
Building can be enabled by setting this option to `true`. The free-threaded compatible wheels will be built in addition to the standard wheels.
This option doesn't support overrides.
If you need to enable/disable it per platform or python version, set this option to `true` and use [`CIBW_BUILD`](#build-skip)/[`CIBW_SKIP`](#build-skip) options to filter the builds.
The build identifiers for those variants have a `t` suffix in their `python_tag` (e.g. `cp313t-manylinux_x86_64`)
!!! note
This feature is experimental: [Whats New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
#### Examples
!!! tab examples "Environment variables"
```yaml
# Enable free-threaded support
CIBW_FREE_THREADED_SUPPORT: 1
# Skip building free-threaded compatible wheels on Windows
CIBW_FREE_THREADED_SUPPORT: 1
CIBW_SKIP: *t-win*
```
It is generally recommended to use `free-threaded-support` in a config file as you can statically declare that you
support free-threaded builds.
!!! tab examples "pyproject.toml"
```toml
[tool.cibuildwheel]
# Enable free-threaded support
free-threaded-support = true
# Skip building free-threaded compatible wheels on Windows
free-threaded-support = true
skip = "*t-win*"
```
### `CIBW_ARCHS` {: #archs}
> Change the architectures built on your machine by default.
@@ -603,24 +557,53 @@ the package is compatible with all versions of Python that it can build.
CIBW_PROJECT_REQUIRES_PYTHON: ">=3.6"
```
### `CIBW_PRERELEASE_PYTHONS` {: #prerelease-pythons}
> Enable building with pre-release versions of Python if available
### `CIBW_ENABLE` {: #enable}
> Enable building with extra categories of selectors present.
This option lets you opt-in to non-default builds, like pre-releases and
free-threaded Python. These are not included by default to give a nice default
for new users, but can be added to the selectors available here. The allowed
values are:
- `cypython-prerelease`: Enables beta versions of Pythons if any are available
(May-July, approximately). For backward compatibility, `CIBW_PRERELEASE_PYTHONS`
is also supported until cibuildwheel 3.
- `cpython-freethreading`: [PEP 703](https://www.python.org/dev/peps/pep-0703)
introduced variants of CPython that can be built without the Global
Interpreter Lock (GIL). Those variants are also known as free-threaded /
no-gil. This will enable building these wheels while they are experimental.
The build identifiers for those variants have a `t` suffix in their
`python_tag` (e.g. `cp313t-manylinux_x86_64`). For backward compatibility,
`CIBW_FREE_THREADED_SUPPORT` is also supported until cibuildwheel 3.
- `pypy`: Enable PyPy. For backward compatibility, this is always enabled until
cibuildwheel 3 is released.
During the beta period, when new versions of Python are being tested,
cibuildwheel will often gain early support for beta releases. If you would
like to test wheel building with these versions, you can enable this flag.
!!! caution
This option is provided for testing purposes only. It is not
recommended to distribute wheels built when `CIBW_PRERELEASE_PYTHONS` is
set, such as uploading to PyPI. Please _do not_ upload these wheels to
PyPI, as they are not guaranteed to work with the final Python release.
Once Python is ABI stable and enters the release candidate phase, that
version of Python will become available without this flag.
`cpython-prerelease` is provided for testing purposes only. It is not
recommended to distribute wheels built with beta releases, such as
uploading to PyPI. Please _do not_ upload these wheels to PyPI, as they are
not guaranteed to work with the final Python release. Once Python is ABI
stable and enters the release candidate phase, that version of Python will
become available without this flag.
Default: Off (0) if Python is available in beta phase. No effect otherwise.
!!! note
Free threading is experimental: [Whats New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
Default: empty (`pypy` is always injected).
This option doesn't support overrides or platform specific variants; it is
intended as a way to acknowledge that a project is aware that these extra
selectors exist. If you need to enable/disable it per platform or python
version, set this option to `true` and use
[`CIBW_BUILD`](#build-skip)/[`CIBW_SKIP`](#build-skip) options to filter the
builds.
Unlike all other cibuildwheel options, the environment variable setting will
only add to the TOML config; you can't remove an enable by setting an empty or
partial list in environment variables; use `CIBW_SKIP` instead.
This option can also be set using the [command-line option](#command-line) `--prerelease-pythons`. This option is not available in the `pyproject.toml` config.
#### Examples
@@ -628,9 +611,38 @@ This option can also be set using the [command-line option](#command-line) `--pr
```yaml
# Include latest Python beta
CIBW_PRERELEASE_PYTHONS: True
CIBW_ENABLE: cpython-prerelease
# Include free-threaded support
CIBW_ENABLE: cpython-freethreading
# Include both
CIBW_ENABLE: cpython-prerelease cpython-freethreading
# Skip building free-threaded compatible wheels on Windows
CIBW_ENABLE: cpython-freethreading
CIBW_SKIP: *t-win*
```
It is generally recommended to use `cpython-freethreading` in a config
file as you can statically declare that you support free-threaded builds.
!!! tab examples "pyproject.toml"
```toml
[tool.cibuildwheel]
# Enable free-threaded support
enable = ["cpython-freethreading"]
# Skip building free-threaded compatible wheels on Windows
enable = ["cpython-freethreading"]
skip = "*t-win*"
```
It is generally not recommended to use `cpython-prerelease` in a config file,
as it's intended for testing pre-releases for a 2-3 month period only.
### `CIBW_ALLOW_EMPTY` {: #allow-empty}
> Suppress the error code if no wheels match the specified build identifiers
+16 -9
View File
@@ -2,11 +2,13 @@ from __future__ import annotations
from packaging.specifiers import SpecifierSet
from cibuildwheel.util import BuildSelector
from cibuildwheel.util import BuildSelector, EnableGroups
def test_build():
build_selector = BuildSelector(build_config="cp3*-* *-manylinux*", skip_config="")
build_selector = BuildSelector(
build_config="cp3*-* *-manylinux*", skip_config="", enable=frozenset([EnableGroups.PyPy])
)
assert build_selector("cp36-manylinux_x86_64")
assert build_selector("cp37-manylinux_x86_64")
@@ -43,7 +45,7 @@ def test_build_filter_pre():
build_selector = BuildSelector(
build_config="cp3*-* *-manylinux*",
skip_config="",
prerelease_pythons=True,
enable=frozenset([EnableGroups.CPythonPrerelease, EnableGroups.PyPy]),
)
assert build_selector("cp37-manylinux_x86_64")
@@ -55,7 +57,9 @@ def test_build_filter_pre():
def test_skip():
build_selector = BuildSelector(
build_config="*", skip_config="pp36-* cp3?-manylinux_i686 cp36-win* *-win32"
build_config="*",
skip_config="pp36-* cp3?-manylinux_i686 cp36-win* *-win32",
enable=frozenset([EnableGroups.PyPy]),
)
assert not build_selector("pp36-manylinux_x86_64")
@@ -79,7 +83,9 @@ def test_skip():
def test_build_and_skip():
build_selector = BuildSelector(
build_config="cp36-* cp37-macosx* *-manylinux*", skip_config="pp37-* cp37-manylinux_i686"
build_config="cp36-* cp37-macosx* *-manylinux*",
skip_config="pp37-* cp37-manylinux_i686",
enable=frozenset([EnableGroups.PyPy]),
)
assert not build_selector("pp37-manylinux_x86_64")
@@ -110,7 +116,10 @@ def test_build_braces():
def test_build_limited_python():
build_selector = BuildSelector(
build_config="*", skip_config="", requires_python=SpecifierSet(">=3.7")
build_config="*",
skip_config="",
requires_python=SpecifierSet(">=3.7"),
enable=frozenset([EnableGroups.PyPy]),
)
assert not build_selector("cp36-manylinux_x86_64")
@@ -146,9 +155,7 @@ def test_build_limited_python_patch():
def test_build_free_threaded_python():
build_selector = BuildSelector(
build_config="*", skip_config="", prerelease_pythons=True, free_threaded_support=True
)
build_selector = BuildSelector(build_config="*", skip_config="", enable=frozenset(EnableGroups))
assert build_selector("cp313t-manylinux_x86_64")
+2 -1
View File
@@ -6,6 +6,7 @@ import pytest
from cibuildwheel.__main__ import main
from cibuildwheel.architecture import Architecture
from cibuildwheel.util import EnableGroups
from ..conftest import MOCK_PACKAGE_DIR
@@ -216,7 +217,7 @@ def test_only_argument(intercepted_build_args, monkeypatch, only, plat):
assert options.globals.build_selector.skip_config == ""
assert options.platform == plat
assert options.globals.architectures == Architecture.all_archs(plat)
assert options.globals.build_selector.prerelease_pythons is True
assert EnableGroups.PyPy in options.globals.build_selector.enable
@pytest.mark.parametrize("only", ("cp311-manylxinux_x86_64", "some_linux_thing"))
+5 -1
View File
@@ -15,6 +15,7 @@ from cibuildwheel.options import (
Options,
_get_pinned_container_images,
)
from cibuildwheel.util import EnableGroups
PYPROJECT_1 = """
[tool.cibuildwheel]
@@ -454,4 +455,7 @@ def test_free_threaded_support(
)
)
options = Options(platform="linux", command_line_arguments=args, env=env)
assert options.globals.build_selector.free_threaded_support is expected_result
if expected_result:
assert EnableGroups.CPythonFreeThreading in options.globals.build_selector.enable
else:
assert EnableGroups.CPythonFreeThreading not in options.globals.build_selector.enable