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 structured exception hierarchy with specific error types and return codes for different failure scenarios.
Foundation exception class for all cibuildwheel-specific errors.
class FatalError(BaseException):
return_code: int = 1
def __init__(self, *args: object) -> None:
"""
Initialize a fatal error.
Args:
*args: Error message arguments
"""Errors related to invalid or conflicting configuration.
class ConfigurationError(FatalError):
return_code: int = 2
"""
Raised when configuration is invalid or conflicting.
Examples:
- Invalid platform specified
- Conflicting command-line arguments
- Invalid build selectors
- Missing required configuration
"""Errors when no builds match the selection criteria.
class NothingToDoError(FatalError):
return_code: int = 3
"""
Raised when no builds match the selection criteria.
Examples:
- All builds filtered out by skip patterns
- No builds available for specified platform/architecture
- Empty build matrix after filtering
"""Errors for deprecated configuration or usage patterns.
class DeprecationError(FatalError):
return_code: int = 4
"""
Raised when deprecated features are used.
Examples:
- Deprecated Python versions (< 3.8)
- Deprecated configuration options
- Deprecated command syntax
"""Errors related to wheel types and platform compatibility.
class NonPlatformWheelError(FatalError):
return_code: int = 5
"""
Raised when a non-platform wheel is detected where platform wheel expected.
Examples:
- Pure Python wheel when native wheel expected
- Universal wheel when platform-specific wheel required
"""
class AlreadyBuiltWheelError(FatalError):
return_code: int = 6
"""
Raised when a wheel already exists and overwriting is disabled.
Examples:
- Wheel exists in output directory
- Incremental build with existing artifacts
"""Errors related to Docker/Podman container operations.
class OCIEngineTooOldError(FatalError):
return_code: int = 7
"""
Raised when the container engine version is too old.
Examples:
- Docker version lacks required features
- Podman version incompatible with cibuildwheel
- Missing container runtime capabilities
"""Errors during the wheel repair process.
class RepairStepProducedNoWheelError(FatalError):
return_code: int = 8
"""
Raised when wheel repair produces no output wheel.
Examples:
- auditwheel repair fails to create wheel
- delocate repair removes wheel entirely
- Custom repair command produces no output
"""
class RepairStepProducedMultipleWheelsError(FatalError):
return_code: int = 8
"""
Raised when wheel repair produces multiple wheels unexpectedly.
Examples:
- Repair process splits wheel into multiple files
- Custom repair command creates additional wheels
- Unexpected wheel multiplication during repair
"""Cibuildwheel uses specific exit codes to indicate different error types:
| Code | Error Type | Description |
|---|---|---|
| 0 | Success | Build completed successfully |
| 1 | FatalError | General fatal error |
| 2 | ConfigurationError | Invalid configuration |
| 3 | NothingToDoError | No builds to execute |
| 4 | DeprecationError | Deprecated features used |
| 5 | NonPlatformWheelError | Unexpected wheel type |
| 6 | AlreadyBuiltWheelError | Wheel already exists |
| 7 | OCIEngineTooOldError | Container engine too old |
| 8 | RepairStepError | Wheel repair failed |
# Invalid platform specification
cibuildwheel --platform invalid_platform
# ConfigurationError: Unsupported platform: invalid_platform
# Conflicting options
cibuildwheel --only cp311-linux_x86_64 --platform windows
# ConfigurationError: --platform cannot be specified with --only
# Invalid architecture
cibuildwheel --archs invalid_arch
# ConfigurationError: Invalid architecture specification# No builds match criteria
CIBW_BUILD = "cp37-*" # Python 3.7 not supported in v3.x
# NothingToDoError: No build identifiers selected
# All builds skipped
CIBW_SKIP = "*"
# NothingToDoError: No build identifiers selected
# Architecture not available
cibuildwheel --platform linux --archs nonexistent_arch
# NothingToDoError: No builds available for specified architecture# Deprecated Python version
CIBW_BUILD = "cp36-*"
# DeprecationError: Python 3.6 no longer supported
# Deprecated configuration format
CIBW_TEST_COMMAND = "python {python} -m pytest"
# DeprecationError: {python} placeholder no longer supported# Docker too old
# OCIEngineTooOldError: Docker version 19.03+ required
# Podman configuration issue
# OCIEngineTooOldError: Podman not properly configuredfrom cibuildwheel import main
from cibuildwheel.errors import (
ConfigurationError,
NothingToDoError,
FatalError
)
try:
main()
except ConfigurationError as e:
print(f"Configuration error: {e}")
sys.exit(2)
except NothingToDoError as e:
print(f"Nothing to build: {e}")
sys.exit(3)
except FatalError as e:
print(f"Fatal error: {e}")
sys.exit(e.return_code)# GitHub Actions
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
continue-on-error: false
- name: Handle no builds
if: failure() && contains(github.event.head_commit.message, '[allow-empty]')
run: |
echo "No builds to execute, but allowed by commit message"
exit 0import os
from cibuildwheel.errors import NothingToDoError
try:
# Run cibuildwheel
main()
except NothingToDoError as e:
# Check if empty builds are allowed
if os.environ.get("CIBW_ALLOW_EMPTY"):
print(f"No builds to execute: {e}")
sys.exit(0) # Success despite no builds
else:
raise # Re-raise as error# Validate configuration before running
from cibuildwheel.options import compute_options
from cibuildwheel.errors import ConfigurationError
try:
options = compute_options(platform, args, env)
print("Configuration is valid")
except ConfigurationError as e:
print(f"Configuration validation failed: {e}")# Check if any builds will be selected
from cibuildwheel.selector import BuildSelector
build_selector = BuildSelector(
build_config="cp311-*",
skip_config="*-win32"
)
# Test against known identifiers
test_ids = ["cp311-linux_x86_64", "cp311-win_amd64", "cp311-win32"]
matching_builds = [id for id in test_ids if build_selector(id)]
if not matching_builds:
print("Warning: No builds will be selected")# Validate environment before building
import shutil
import sys
# Check required tools
if not shutil.which("python"):
print("Error: Python not found in PATH")
sys.exit(1)
# Check platform-specific requirements
if sys.platform == "linux":
if not shutil.which("docker") and not shutil.which("podman"):
print("Error: Docker or Podman required for Linux builds")
sys.exit(1)# Full tracebacks for all errors
cibuildwheel --debug-traceback
# Environment variable equivalent
export CIBW_DEBUG_TRACEBACK=1# Print build identifiers without building
cibuildwheel --print-build-identifiers
# Shows what would be built with current configuration# Most cibuildwheel errors include helpful context
try:
main()
except ConfigurationError as e:
# Error message includes:
# - What configuration was invalid
# - Expected values or format
# - Suggestions for fixing
print(f"Detailed error: {e}")def safe_cibuildwheel(platform=None, **kwargs):
"""Wrapper around cibuildwheel with custom error handling."""
try:
return main()
except NothingToDoError:
print("No wheels to build for current configuration")
return 0 # Treat as success
except ConfigurationError as e:
print(f"Please check your cibuildwheel configuration: {e}")
return 2
except FatalError as e:
print(f"Build failed: {e}")
return e.return_codeimport logging
from cibuildwheel.errors import FatalError
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
try:
main()
except FatalError as e:
logger.error(f"Cibuildwheel failed with code {e.return_code}: {e}")
raiseInstall with Tessl CLI
npx tessl i tessl/pypi-cibuildwheel