Build Python wheels on CI with minimal configuration.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Cibuildwheel provides a powerful pattern-based build selection system for controlling which Python versions, platforms, and architectures to build for.
Main interface for controlling which builds are executed.
@dataclasses.dataclass(frozen=True, kw_only=True)
class BuildSelector:
build_config: str
skip_config: str
requires_python: SpecifierSet | None = None
enable: frozenset[EnableGroup] = frozenset()
def __call__(self, build_id: str) -> bool:
"""
Test if a build identifier should be built.
Args:
build_id: Build identifier string (e.g., 'cp311-linux_x86_64')
Returns:
True if the build should be executed, False otherwise
"""Controls which builds should have tests executed.
@dataclasses.dataclass(frozen=True, kw_only=True)
class TestSelector:
skip_config: str
def __call__(self, build_id: str) -> bool:
"""
Test if a build should be tested.
Args:
build_id: Build identifier string
Returns:
True if tests should be run for this build, False otherwise
"""Low-level pattern matching function for build selectors.
def selector_matches(patterns: str, string: str) -> bool:
"""
Test if any pattern in a space-separated list matches a string.
Args:
patterns: Space-separated list of glob patterns
string: String to test against patterns
Returns:
True if any pattern matches, False otherwise
"""Categories of builds that can be optionally enabled.
class EnableGroup(StrEnum):
CPythonExperimentalRiscV64 = "cpython-experimental-riscv64"
CPythonFreeThreading = "cpython-freethreading"
CPythonPrerelease = "cpython-prerelease"
GraalPy = "graalpy"
PyPy = "pypy"
PyPyEoL = "pypy-eol"
PyodidePrerelease = "pyodide-prerelease"Build identifiers follow the pattern: {python_tag}-{platform_tag}
cp38, cp39, cp310, cp311, cp312, cp313, cp314pp38, pp39, pp310, pp311graalpy311linux_x86_64, linux_i686, linux_aarch64, linux_ppc64le, linux_s390x, linux_armv7l, linux_riscv64macosx_x86_64, macosx_arm64, macosx_universal2win_amd64, win32, win_arm64pyodide_wasm32android_arm64_v8aios_arm64_iphoneos, ios_arm64_iphonesimulator, ios_x86_64_iphonesimulator# Build specific Python versions
CIBW_BUILD = "cp39-* cp310-* cp311-*"
# Build for specific platforms
CIBW_BUILD = "*-linux_x86_64 *-macosx_x86_64"
# Build specific combinations
CIBW_BUILD = "cp311-linux_x86_64 cp311-macosx_arm64"# Skip 32-bit builds
CIBW_SKIP = "*-win32 *-linux_i686"
# Skip PyPy builds
CIBW_SKIP = "pp*"
# Skip specific Python versions
CIBW_SKIP = "cp38-* cp39-*"
# Skip experimental architectures
CIBW_SKIP = "*-linux_riscv64 *-linux_armv7l"# Skip tests for slow architectures
CIBW_TEST_SKIP = "*-linux_ppc64le *-linux_s390x"
# Skip tests for emulated builds
CIBW_TEST_SKIP = "*-linux_aarch64"
# Skip tests for specific Python versions
CIBW_TEST_SKIP = "cp38-*"Enable building with prerelease CPython versions:
CIBW_ENABLE = "cpython-prerelease"
# Or in pyproject.toml:
# enable = ["cpython-prerelease"]Enable PyPy builds (disabled by default):
CIBW_ENABLE = "pypy"
# Enables: pp38, pp39, pp310, pp311Enable GraalPy builds:
CIBW_ENABLE = "graalpy"
# Enables: graalpy311Enable CPython builds with free-threading (Python 3.13+):
CIBW_ENABLE = "cpython-freethreading"
# Enables: cp313t (free-threaded variant)Enable experimental RISC-V 64-bit builds:
CIBW_ENABLE = "cpython-experimental-riscv64"
# Note: This enable group is deprecatedCIBW_ENABLE = "pypy graalpy cpython-prerelease"[tool.cibuildwheel]
# Global build configuration
build = "cp39-* cp310-* cp311-*"
[tool.cibuildwheel.linux]
# Linux-specific overrides
build = "cp39-* cp310-* cp311-* cp312-*"
skip = "*-linux_i686" # Skip 32-bit Linux
[tool.cibuildwheel.windows]
# Windows-specific overrides
skip = "*-win32" # Skip 32-bit Windows
[tool.cibuildwheel.macos]
# macOS-specific overrides
build = "cp310-* cp311-*" # Only newer Python versions# Build matrix excluding slow combinations
CIBW_BUILD = "cp311-linux_x86_64 cp311-linux_aarch64 cp311-macosx_* cp311-win_amd64"
# Skip tests on emulated architectures to save time
CIBW_TEST_SKIP = "*-linux_aarch64 *-linux_ppc64le *-linux_s390x"# Use environment variables for conditional selection
import os
if os.environ.get("CIBW_QUICK_BUILD"):
# Quick build: only test current Python version
CIBW_BUILD = f"cp{sys.version_info.major}{sys.version_info.minor}-*"
else:
# Full build: all supported versions
CIBW_BUILD = "cp39-* cp310-* cp311-* cp312-*"# Select builds based on project's requires-python
from packaging.specifiers import SpecifierSet
# Automatically skip unsupported Python versions
requires_python = SpecifierSet(">=3.10")Cibuildwheel validates build selectors and provides warnings for common issues:
# This would generate a warning
CIBW_BUILD = "cp37-*" # Python 3.7 not supported in cibuildwheel 3.x
# This would generate an error
CIBW_SKIP = "invalid-selector" # Invalid pattern# If no builds match, cibuildwheel will error unless:
CIBW_ALLOW_EMPTY = "1" # Allow empty build selection# Using enable groups without enabling them generates warnings
CIBW_BUILD = "pp39-*" # Warning: PyPy not enabled
CIBW_ENABLE = "pypy" # Required to actually build PyPy# Build for Python 3.11+ only, skip 32-bit
CIBW_BUILD = "cp311-* cp312-*"
CIBW_SKIP = "*-win32 *-linux_i686"# Build everything possible
CIBW_ENABLE = "pypy graalpy cpython-prerelease"
CIBW_BUILD = "*"
CIBW_SKIP = "" # Don't skip anything# Build only for specific deployment targets
CIBW_BUILD = "cp311-linux_x86_64 cp311-macosx_universal2 cp311-win_amd64"
CIBW_TEST_COMMAND = "pytest"# Quick build for development/testing
CIBW_BUILD = "cp311-*" # Current Python version only
CIBW_ARCHS = "native" # Native architecture only
CIBW_TEST_SKIP = "*" # Skip all testsInstall with Tessl CLI
npx tessl i tessl/pypi-cibuildwheel