0
# Utilities
1
2
Cibuildwheel provides various utility functions for string formatting, command preparation, version parsing, and other common operations.
3
4
## Capabilities
5
6
### String Formatting
7
8
Safe string formatting with path support.
9
10
```python { .api }
11
def format_safe(template: str, **kwargs: str | os.PathLike[str]) -> str:
12
"""
13
Safely format a template string with keyword arguments.
14
15
Args:
16
template: Template string with {key} placeholders
17
**kwargs: Keyword arguments for substitution
18
19
Returns:
20
Formatted string with substitutions applied
21
22
Raises:
23
KeyError: If template contains undefined placeholders
24
"""
25
```
26
27
### Command Preparation
28
29
Prepare shell commands with safe argument substitution.
30
31
```python { .api }
32
def prepare_command(command: str, **kwargs: PathOrStr) -> str:
33
"""
34
Prepare a shell command with safe argument substitution.
35
36
Args:
37
command: Command template with {key} placeholders
38
**kwargs: Arguments for substitution (strings or paths)
39
40
Returns:
41
Command string with arguments safely substituted
42
"""
43
```
44
45
### Boolean Conversion
46
47
Convert string values to boolean with flexible input support.
48
49
```python { .api }
50
def strtobool(val: str) -> bool:
51
"""
52
Convert string representation of truth to boolean.
53
54
Args:
55
val: String value to convert
56
57
Returns:
58
Boolean representation of the input
59
60
Raises:
61
ValueError: If string is not a recognized boolean value
62
63
Accepts: 'y', 'yes', 't', 'true', 'on', '1' (case-insensitive) for True
64
'n', 'no', 'f', 'false', 'off', '0' (case-insensitive) for False
65
"""
66
```
67
68
### Text Unwrapping
69
70
Utilities for unwrapping and reformatting text.
71
72
```python { .api }
73
def unwrap(text: str) -> str:
74
"""
75
Unwrap text by removing line breaks within paragraphs.
76
77
Args:
78
text: Input text with possible line breaks
79
80
Returns:
81
Text with line breaks removed within paragraphs
82
"""
83
84
def unwrap_preserving_paragraphs(text: str) -> str:
85
"""
86
Unwrap text while preserving paragraph breaks.
87
88
Args:
89
text: Input text with paragraphs separated by blank lines
90
91
Returns:
92
Text with line breaks removed within paragraphs but paragraph breaks preserved
93
"""
94
```
95
96
### Key-Value Parsing
97
98
Parse configuration strings containing key-value pairs.
99
100
```python { .api }
101
def parse_key_value_string(
102
key_value_string: str,
103
positional_arg_names: Sequence[str] | None = None,
104
kw_arg_names: Sequence[str] | None = None,
105
) -> dict[str, list[str]]:
106
"""
107
Parse a string containing key-value pairs and positional arguments.
108
109
Args:
110
key_value_string: String to parse (e.g., "pos1 pos2 key1=val1 key2=val2")
111
positional_arg_names: Names for positional arguments
112
kw_arg_names: Valid keyword argument names
113
114
Returns:
115
Dictionary mapping argument names to lists of values
116
117
Raises:
118
ValueError: If parsing fails or invalid arguments provided
119
"""
120
```
121
122
### Version Handling
123
124
Flexible version comparison and parsing.
125
126
```python { .api }
127
@dataclasses.dataclass(order=True)
128
class FlexibleVersion:
129
"""
130
Version class that handles various version formats flexibly.
131
132
Supports:
133
- Semantic versioning (1.2.3)
134
- Python version format (3.11.0)
135
- Pre-release versions (1.0.0a1, 1.0.0rc1)
136
- Development versions (1.0.0.dev0)
137
- Epoch versions (1!1.0.0)
138
"""
139
140
def __init__(self, version: str) -> None:
141
"""
142
Initialize from version string.
143
144
Args:
145
version: Version string to parse
146
"""
147
148
def __str__(self) -> str:
149
"""Return string representation of version."""
150
151
def __eq__(self, other: object) -> bool:
152
"""Check version equality."""
153
154
def __lt__(self, other: FlexibleVersion) -> bool:
155
"""Compare versions for ordering."""
156
```
157
158
## Usage Examples
159
160
### String Formatting
161
162
```python
163
from cibuildwheel.util.helpers import format_safe
164
165
# Basic formatting
166
template = "Building {package} version {version}"
167
result = format_safe(template, package="my-package", version="1.0.0")
168
# Result: "Building my-package version 1.0.0"
169
170
# Path formatting
171
template = "Output to {output_dir}/wheels"
172
result = format_safe(template, output_dir=Path("/tmp/build"))
173
# Result: "Output to /tmp/build/wheels"
174
```
175
176
### Command Preparation
177
178
```python
179
from cibuildwheel.util.helpers import prepare_command
180
from pathlib import Path
181
182
# Prepare test command
183
command = "pytest {project}/tests --output={output}"
184
prepared = prepare_command(
185
command,
186
project=Path("/src/mypackage"),
187
output=Path("/tmp/test-results.xml")
188
)
189
# Result: "pytest /src/mypackage/tests --output=/tmp/test-results.xml"
190
191
# Command with shell escaping
192
command = "echo {message}"
193
prepared = prepare_command(command, message="Hello World with spaces")
194
# Result: "echo 'Hello World with spaces'"
195
```
196
197
### Boolean Conversion
198
199
```python
200
from cibuildwheel.util.helpers import strtobool
201
202
# Convert various string representations
203
strtobool("yes") # True
204
strtobool("true") # True
205
strtobool("1") # True
206
strtobool("on") # True
207
208
strtobool("no") # False
209
strtobool("false") # False
210
strtobool("0") # False
211
strtobool("off") # False
212
213
# Case insensitive
214
strtobool("TRUE") # True
215
strtobool("False") # False
216
217
# Error on invalid input
218
try:
219
strtobool("maybe") # ValueError
220
except ValueError:
221
pass
222
```
223
224
### Text Unwrapping
225
226
```python
227
from cibuildwheel.util.helpers import unwrap, unwrap_preserving_paragraphs
228
229
# Simple unwrapping
230
text = """This is a long line
231
that was wrapped
232
for display."""
233
234
unwrapped = unwrap(text)
235
# Result: "This is a long line that was wrapped for display."
236
237
# Preserving paragraphs
238
text = """First paragraph
239
with wrapped lines.
240
241
Second paragraph
242
also wrapped."""
243
244
unwrapped = unwrap_preserving_paragraphs(text)
245
# Result: "First paragraph with wrapped lines.\n\nSecond paragraph also wrapped."
246
```
247
248
### Key-Value Parsing
249
250
```python
251
from cibuildwheel.util.helpers import parse_key_value_string
252
253
# Parse mixed positional and keyword arguments
254
config = "arg1 arg2 key1=value1 key2=value2"
255
parsed = parse_key_value_string(
256
config,
257
positional_arg_names=["first", "second"],
258
kw_arg_names=["key1", "key2", "key3"]
259
)
260
# Result: {
261
# "first": ["arg1"],
262
# "second": ["arg2"],
263
# "key1": ["value1"],
264
# "key2": ["value2"]
265
# }
266
267
# Multiple values for same key
268
config = "key1=val1 key1=val2 key2=single"
269
parsed = parse_key_value_string(config)
270
# Result: {
271
# "key1": ["val1", "val2"],
272
# "key2": ["single"]
273
# }
274
```
275
276
### Version Comparison
277
278
```python
279
from cibuildwheel.util.helpers import FlexibleVersion
280
281
# Create version objects
282
v1 = FlexibleVersion("1.2.3")
283
v2 = FlexibleVersion("1.2.4")
284
v3 = FlexibleVersion("1.2.3a1")
285
v4 = FlexibleVersion("2.0.0")
286
287
# Comparison operations
288
print(v1 < v2) # True
289
print(v1 > v3) # True (release > prerelease)
290
print(v2 < v4) # True
291
292
# Sorting versions
293
versions = [v4, v1, v3, v2]
294
sorted_versions = sorted(versions)
295
# Result: [v3, v1, v2, v4] (1.2.3a1, 1.2.3, 1.2.4, 2.0.0)
296
297
# String representation
298
print(str(v1)) # "1.2.3"
299
print(str(v3)) # "1.2.3a1"
300
```
301
302
## Advanced Usage
303
304
### Template Systems
305
306
```python
307
from cibuildwheel.util.helpers import format_safe
308
309
# Build complex command templates
310
template = """
311
cd {project_dir} && \
312
python -m pip install {build_deps} && \
313
python -m build --outdir {wheel_dir}
314
"""
315
316
command = format_safe(
317
template,
318
project_dir="/src/mypackage",
319
build_deps="wheel setuptools",
320
wheel_dir="/tmp/wheels"
321
).strip()
322
```
323
324
### Configuration Parsing
325
326
```python
327
from cibuildwheel.util.helpers import parse_key_value_string
328
329
# Parse build frontend configuration
330
frontend_config = "build --installer uv --outdir {wheel_dir}"
331
parsed = parse_key_value_string(
332
frontend_config,
333
positional_arg_names=["tool"],
334
kw_arg_names=["installer", "outdir"]
335
)
336
337
tool = parsed["tool"][0] # "build"
338
installer = parsed.get("installer", ["pip"])[0] # "uv"
339
outdir = parsed.get("outdir", ["{wheel_dir}"])[0] # "{wheel_dir}"
340
```
341
342
### Version Range Checking
343
344
```python
345
from cibuildwheel.util.helpers import FlexibleVersion
346
347
def check_python_compatibility(python_version: str, min_version: str = "3.8"):
348
"""Check if Python version meets minimum requirement."""
349
current = FlexibleVersion(python_version)
350
minimum = FlexibleVersion(min_version)
351
return current >= minimum
352
353
# Usage
354
check_python_compatibility("3.11.0", "3.8") # True
355
check_python_compatibility("3.7.0", "3.8") # False
356
```
357
358
### Environment Variable Processing
359
360
```python
361
from cibuildwheel.util.helpers import strtobool
362
import os
363
364
def get_bool_env(var_name: str, default: bool = False) -> bool:
365
"""Get boolean environment variable with default."""
366
value = os.environ.get(var_name)
367
if value is None:
368
return default
369
return strtobool(value)
370
371
# Usage
372
debug_enabled = get_bool_env("CIBW_DEBUG", False)
373
skip_tests = get_bool_env("CIBW_SKIP_TESTS", False)
374
```
375
376
### Text Processing Pipelines
377
378
```python
379
from cibuildwheel.util.helpers import unwrap_preserving_paragraphs
380
381
def process_multiline_config(config_text: str) -> str:
382
"""Process multiline configuration text."""
383
# Remove extra whitespace and unwrap lines
384
processed = unwrap_preserving_paragraphs(config_text.strip())
385
386
# Additional processing
387
lines = processed.split('\n')
388
cleaned_lines = [line.strip() for line in lines if line.strip()]
389
390
return '\n'.join(cleaned_lines)
391
392
# Usage with configuration files
393
config = """
394
build = cp39-* cp310-*
395
cp311-*
396
397
skip = *-win32
398
*-linux_i686
399
"""
400
401
processed = process_multiline_config(config)
402
# Result: "build = cp39-* cp310-* cp311-*\n\nskip = *-win32 *-linux_i686"
403
```
404
405
## Integration with Cibuildwheel
406
407
These utilities are used throughout cibuildwheel for:
408
409
- **Configuration processing**: Parsing TOML and environment variables
410
- **Command generation**: Creating shell commands for builds and tests
411
- **Version handling**: Comparing Python versions and package versions
412
- **Text formatting**: Processing help text and error messages
413
- **Path handling**: Working with file paths across platforms
414
415
The utilities provide a consistent interface for common operations while handling edge cases and platform differences internally.