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 comprehensive configuration system supporting TOML files, environment variables, and command-line options with validation and schema support.
Main container for all build configuration options.
@dataclasses.dataclass(frozen=True, kw_only=True)
class Options:
globals: GlobalOptions
def build_options(self, identifier: str) -> BuildOptions:
"""
Get build-specific options for a given build identifier.
Args:
identifier: Build identifier (e.g., 'cp311-linux_x86_64')
Returns:
BuildOptions instance with configuration for this build
"""
def check_for_invalid_configuration(self, identifiers: Sequence[str]) -> None:
"""
Validate configuration against actual build identifiers.
Args:
identifiers: List of build identifiers that will be built
Raises:
ValueError: If configuration contains invalid settings
"""
def summary(self, identifiers: Sequence[str]) -> str:
"""
Generate a human-readable summary of the configuration.
Args:
identifiers: List of build identifiers
Returns:
Formatted string summarizing the build configuration
"""Configuration that applies to all builds.
@dataclasses.dataclass(frozen=True, kw_only=True)
class GlobalOptions:
package_dir: Path
output_dir: Path
build_selector: BuildSelector
test_selector: TestSelector
architectures: set[Architecture]
allow_empty: boolConfiguration specific to individual builds.
@dataclasses.dataclass(frozen=True, kw_only=True)
class BuildOptions:
globals: GlobalOptions
environment: ParsedEnvironment
build_frontend: BuildFrontendConfig
dependency_constraints: DependencyConstraints | None
before_all: str
before_build: str
repair_wheel_command: str | None
test_command: str
test_requires: list[str]
test_extras: str
config_settings: dict[str, str | list[str]]
# ... many more build-specific optionsParsed command-line arguments structure.
@dataclasses.dataclass(kw_only=True)
class CommandLineArguments:
platform: Literal["auto", "linux", "macos", "windows"] | None
archs: str | None
output_dir: Path
only: str | None
config_file: str
package_dir: Path
print_build_identifiers: bool
allow_empty: bool
debug_traceback: bool
enable: list[str]
clean_cache: boolMain function for computing final options from all sources.
def compute_options(
platform: PlatformName,
command_line_arguments: CommandLineArguments,
env: Mapping[str, str]
) -> Options:
"""
Compute final build options from all configuration sources.
Args:
platform: Target platform name
command_line_arguments: Parsed CLI arguments
env: Environment variables
Returns:
Complete Options object with resolved configuration
Raises:
ConfigurationError: If configuration is invalid or conflicting
"""Configuration for the tool used to build wheels.
BuildFrontendName = Literal["pip", "build", "build[uv]"]
@dataclasses.dataclass(frozen=True)
class BuildFrontendConfig:
name: BuildFrontendName
args: Sequence[str] = ()Primary configuration method using pyproject.toml or dedicated config files:
[tool.cibuildwheel]
# Build selection
build = "cp39-* cp310-* cp311-*"
skip = "*-win32 *-linux_i686"
# Build customization
build-frontend = "build"
config-settings = {cmake.define.BUILD_TESTING = "OFF"}
# Environment variables
environment = {CFLAGS = "-O2", LDFLAGS = "-s"}
# Testing
test-command = "pytest {project}/tests"
test-requires = ["pytest", "numpy"]
# Platform-specific overrides
[tool.cibuildwheel.linux]
before-all = "yum install -y cmake"
manylinux-x86_64-image = "manylinux2014"
[tool.cibuildwheel.windows]
before-build = "pip install delvewheel"
[tool.cibuildwheel.macos]
archs = ["universal2"]Configuration via environment variables with CIBW_ prefix:
# Build selection
export CIBW_BUILD="cp39-* cp310-* cp311-*"
export CIBW_SKIP="*-win32 *-linux_i686"
# Build customization
export CIBW_BUILD_FRONTEND="build"
export CIBW_ENVIRONMENT="CFLAGS=-O2 LDFLAGS=-s"
# Testing
export CIBW_TEST_COMMAND="pytest {project}/tests"
export CIBW_TEST_REQUIRES="pytest numpy"
# Platform-specific (Linux example)
export CIBW_BEFORE_ALL_LINUX="yum install -y cmake"
export CIBW_MANYLINUX_X86_64_IMAGE="manylinux2014"Override configuration via command-line arguments:
cibuildwheel \
--platform linux \
--archs x86_64,aarch64 \
--output-dir wheelhouse \
--config-file custom-build.tomlConfiguration sources are merged in this priority order (highest to lowest):
Platform-specific settings override global settings:
[tool.cibuildwheel]
# Global setting
build = "cp39-* cp310-* cp311-*"
[tool.cibuildwheel.linux]
# Linux override - adds cp312
build = "cp39-* cp310-* cp311-* cp312-*"
[tool.cibuildwheel.windows]
# Windows override - removes cp39
build = "cp310-* cp311-*"[tool.cibuildwheel]
# Python versions to build
build = "cp39-* cp310-* cp311-*"
# Python versions/platforms to skip
skip = ["*-win32", "*-linux_i686", "pp*"]
# Enable additional build categories
enable = ["pypy", "graalpy"]
# Allow empty build selection
allow-empty = true
# Project's Python compatibility
project-requires-python = ">=3.9"[tool.cibuildwheel]
# Build tool selection
build-frontend = {name = "build", args = ["--installer", "uv"]}
# Build backend config settings
config-settings = "cmake.define.BUILD_TESTING=OFF"
# Environment variables for builds
environment = {CFLAGS = "-O2", LDFLAGS = "-s"}
# Environment variables to pass from host
environment-pass = ["SECRET_TOKEN", "API_KEY"]
# Commands to run before builds
before-all = "yum install -y cmake"
before-build = "pip install cython"
# Custom wheel repair command
repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}"[tool.cibuildwheel]
# Test command template
test-command = "pytest {project}/tests"
# Test dependencies
test-requires = ["pytest", "numpy", "scipy"]
# Test extras (optional dependencies)
test-extras = ["test", "docs"]
# Skip tests for certain builds
test-skip = ["*-linux_ppc64le", "*-linux_s390x"][tool.cibuildwheel.linux]
# Custom container images
manylinux-x86_64-image = "quay.io/pypa/manylinux2014_x86_64"
musllinux-x86_64-image = "quay.io/pypa/musllinux_1_1_x86_64"
# Container engine selection
container-engine = "docker" # or "podman"Access the JSON schema for configuration validation.
def get_schema(tool_name: str = "cibuildwheel") -> dict[str, Any]:
"""
Get the JSON schema for cibuildwheel configuration.
Args:
tool_name: Name of the tool section in pyproject.toml
Returns:
JSON schema dictionary for validation
"""Cibuildwheel provides a schema entry point for validate-pyproject:
# Entry point: validate_pyproject.tool_schema
"cibuildwheel" = "cibuildwheel.schema:get_schema"Many IDEs can use the schema for autocomplete and validation:
{
"$schema": "https://json.schemastore.org/pyproject.json",
"tool": {
"cibuildwheel": {
"build": "cp39-* cp310-* cp311-*"
}
}
}# Use environment variables for dynamic config
import os
import sys
# Conditional builds based on CI environment
if os.environ.get("CI_QUICK_BUILD"):
CIBW_BUILD = f"cp{sys.version_info.major}{sys.version_info.minor}-*"
else:
CIBW_BUILD = "cp39-* cp310-* cp311-*"[tool.cibuildwheel]
# Base configuration
build = "cp39-* cp310-* cp311-*"
test-command = "pytest"
# Development profile
[tool.cibuildwheel.dev]
build = "cp311-*" # Only current version
test-skip = "*" # Skip tests
archs = "native" # Native arch only
# Release profile
[tool.cibuildwheel.release]
build = "*" # All versions
enable = ["pypy"] # Include PyPy
test-command = "pytest --cov"from cibuildwheel.options import compute_options, CommandLineArguments
from cibuildwheel.errors import ConfigurationError
try:
options = compute_options(
platform="linux",
command_line_arguments=args,
env=os.environ
)
# Configuration is valid
except ConfigurationError as e:
print(f"Configuration error: {e}")[tool.cibuildwheel]
build = "cp311-*"
test-command = "python -m pytest"[tool.cibuildwheel]
# Build all recent Python versions
build = "cp39-* cp310-* cp311-* cp312-*"
skip = ["*-win32", "*-linux_i686"] # Skip 32-bit
enable = ["pypy"]
# Build customization
build-frontend = "build"
environment = {CFLAGS = "-O2"}
before-build = "pip install cython numpy"
# Testing
test-command = "pytest {project}/tests -x -v"
test-requires = ["pytest", "pytest-xdist"]
test-extras = ["test"]
# Platform overrides
[tool.cibuildwheel.linux]
before-all = "yum install -y cmake gcc-c++"
manylinux-x86_64-image = "manylinux2014"
[tool.cibuildwheel.macos]
archs = ["universal2"]
environment = {MACOSX_DEPLOYMENT_TARGET = "10.14"}
[tool.cibuildwheel.windows]
before-build = "pip install delvewheel"Install with Tessl CLI
npx tessl i tessl/pypi-cibuildwheel