0
# Wheel Tag Management
1
2
Modify wheel filename tags for Python version, ABI, and platform compatibility without rebuilding the package. Enables retargeting wheels for different Python versions or platforms when the code is compatible.
3
4
## Capabilities
5
6
### Tag Modification Function
7
8
Main function for modifying wheel tags and creating new wheel files with updated compatibility information.
9
10
```python { .api }
11
def tags(
12
wheel: str,
13
python_tags: str | None = None,
14
abi_tags: str | None = None,
15
platform_tags: str | None = None,
16
build_tag: str | None = None,
17
remove: bool = False,
18
) -> str:
19
"""
20
Modify wheel tags and create new wheel file.
21
22
Parameters:
23
- wheel: Path to input wheel file
24
- python_tags: Python version tags (e.g., "py38.py39")
25
- abi_tags: ABI tags (e.g., "cp38.cp39")
26
- platform_tags: Platform tags (e.g., "linux_x86_64.win_amd64")
27
- build_tag: Build tag (must start with digit, no dashes)
28
- remove: Delete original wheel file after creating new one
29
30
Returns:
31
Filename of newly created wheel
32
33
Tag modification syntax:
34
- Replace: "py38.py39" replaces all Python tags
35
- Append: "+py310" adds py310 to existing tags
36
- Remove: "-py37" removes py37 from existing tags
37
- Multiple: Dot-separated values for multiple tags
38
39
Raises:
40
- AssertionError: If internal WHEEL tags don't match filename
41
- WheelError: For invalid wheel files or format errors
42
"""
43
```
44
45
### Tag Computation Utilities
46
47
Helper functions for computing and manipulating tag sets.
48
49
```python { .api }
50
def _compute_tags(original_tags: Iterable[str], new_tags: str | None) -> set[str]:
51
"""
52
Compute final tag set based on modification rules.
53
54
Parameters:
55
- original_tags: Current tags from wheel
56
- new_tags: Tag modification string (None for no change)
57
58
Returns:
59
Final set of tags after applying modifications
60
61
Modification rules:
62
- None or empty: No change (returns original_tags)
63
- Starting with '+': Append tags (union operation)
64
- Starting with '-': Remove tags (difference operation)
65
- Other: Replace tags completely
66
- Dot-separated: Multiple tags in single string
67
"""
68
```
69
70
### Tag Validation
71
72
The tags function performs comprehensive validation to ensure wheel integrity:
73
74
1. **Filename Parsing**: Validates wheel filename format using WHEEL_INFO_RE
75
2. **Internal Consistency**: Verifies WHEEL file tags match filename tags
76
3. **Build Tag Consistency**: Ensures build tags match between filename and WHEEL file
77
4. **Tag Format**: Validates tag syntax and relationships
78
79
### Usage Examples
80
81
#### Basic Tag Modifications
82
83
```python
84
from wheel._commands.tags import tags
85
86
# Replace Python version tags
87
new_wheel = tags(
88
'package-1.0-py3-none-any.whl',
89
python_tags='py38.py39.py310'
90
)
91
# Result: package-1.0-py38.py39.py310-none-any.whl
92
93
# Add platform support
94
new_wheel = tags(
95
'package-1.0-py3-none-any.whl',
96
platform_tags='linux_x86_64.macosx_10_9_x86_64'
97
)
98
# Result: package-1.0-py3-none-linux_x86_64.macosx_10_9_x86_64.whl
99
100
# Set ABI tags for compiled extensions
101
new_wheel = tags(
102
'package-1.0-py38-abi3-linux_x86_64.whl',
103
abi_tags='cp38.cp39.cp310'
104
)
105
# Result: package-1.0-py38-cp38.cp39.cp310-linux_x86_64.whl
106
```
107
108
#### Append and Remove Operations
109
110
```python
111
# Append Python version
112
new_wheel = tags(
113
'package-1.0-py38.py39-none-any.whl',
114
python_tags='+py310'
115
)
116
# Result: package-1.0-py38.py39.py310-none-any.whl
117
118
# Remove specific version
119
new_wheel = tags(
120
'package-1.0-py37.py38.py39-none-any.whl',
121
python_tags='-py37'
122
)
123
# Result: package-1.0-py38.py39-none-any.whl
124
125
# Multiple operations (applied left to right)
126
new_wheel = tags(
127
'package-1.0-py37.py38-none-any.whl',
128
python_tags='-py37.+py39.+py310'
129
)
130
# Result: package-1.0-py38.py39.py310-none-any.whl
131
```
132
133
#### Build Tags
134
135
```python
136
# Add build tag
137
new_wheel = tags(
138
'package-1.0-py3-none-any.whl',
139
build_tag='20231201'
140
)
141
# Result: package-1.0-20231201-py3-none-any.whl
142
143
# Remove build tag
144
new_wheel = tags(
145
'package-1.0-20231201-py3-none-any.whl',
146
build_tag=''
147
)
148
# Result: package-1.0-py3-none-any.whl
149
```
150
151
#### File Management
152
153
```python
154
# Keep original file (default)
155
new_wheel = tags('package-1.0-py3-none-any.whl', python_tags='py39')
156
# Creates: package-1.0-py39-none-any.whl
157
# Keeps: package-1.0-py3-none-any.whl
158
159
# Remove original file
160
new_wheel = tags(
161
'package-1.0-py3-none-any.whl',
162
python_tags='py39',
163
remove=True
164
)
165
# Creates: package-1.0-py39-none-any.whl
166
# Deletes: package-1.0-py3-none-any.whl
167
```
168
169
#### Complex Tag Combinations
170
171
```python
172
# Multiple tag types simultaneously
173
new_wheel = tags(
174
'package-1.0-py3-none-any.whl',
175
python_tags='py38.py39.py310',
176
abi_tags='cp38.cp39.cp310',
177
platform_tags='linux_x86_64.win_amd64',
178
build_tag='20231201'
179
)
180
# Result: package-1.0-20231201-py38.py39.py310-cp38.cp39.cp310-linux_x86_64.win_amd64.whl
181
```
182
183
#### Programmatic Tag Management
184
185
```python
186
from wheel._commands.tags import tags, _compute_tags
187
from wheel.wheelfile import WheelFile
188
189
# Analyze current tags
190
with WheelFile('package-1.0-py3-none-any.whl', 'r') as wf:
191
parsed = wf.parsed_filename
192
current_python = parsed.group('pyver').split('.')
193
current_abi = parsed.group('abi').split('.')
194
current_platform = parsed.group('plat').split('.')
195
196
print(f"Current Python tags: {current_python}")
197
print(f"Current ABI tags: {current_abi}")
198
print(f"Current platform tags: {current_platform}")
199
200
# Compute new tag set
201
new_python_tags = _compute_tags(current_python, '+py311')
202
print(f"New Python tags: {sorted(new_python_tags)}")
203
204
# Apply changes
205
new_wheel = tags(
206
'package-1.0-py3-none-any.whl',
207
python_tags='.'.join(sorted(new_python_tags))
208
)
209
```
210
211
### Error Handling
212
213
```python
214
from wheel._commands.tags import tags
215
from wheel.wheelfile import WheelError
216
217
try:
218
# This will raise AssertionError if tags are inconsistent
219
new_wheel = tags('corrupted-wheel.whl', python_tags='py39')
220
except AssertionError as e:
221
print(f"Wheel integrity error: {e}")
222
except WheelError as e:
223
print(f"Wheel format error: {e}")
224
```
225
226
### Common Use Cases
227
228
#### Cross-Platform Distribution
229
230
```python
231
# Create platform-specific variants from universal wheel
232
platforms = ['linux_x86_64', 'macosx_10_9_x86_64', 'win_amd64']
233
234
for platform in platforms:
235
platform_wheel = tags(
236
'package-1.0-py3-none-any.whl',
237
platform_tags=platform
238
)
239
print(f"Created: {platform_wheel}")
240
```
241
242
#### Python Version Compatibility
243
244
```python
245
# Update wheel for newer Python versions
246
python_versions = ['py39', 'py310', 'py311']
247
248
updated_wheel = tags(
249
'package-1.0-py38-none-any.whl',
250
python_tags='.'.join(python_versions)
251
)
252
print(f"Updated wheel: {updated_wheel}")
253
```
254
255
#### ABI Tag Updates
256
257
```python
258
# Convert from specific ABI to abi3 (stable ABI)
259
stable_wheel = tags(
260
'package-1.0-py38-cp38-linux_x86_64.whl',
261
abi_tags='abi3'
262
)
263
print(f"Stable ABI wheel: {stable_wheel}")
264
```
265
266
## Types
267
268
```python { .api }
269
from collections.abc import Iterable
270
from typing import Literal
271
272
# No additional types beyond function signatures
273
# Uses standard library types and wheel.wheelfile.WheelFile
274
```