CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wheel

Command line tool for manipulating wheel files

Overview
Eval results
Files

tag-management.mddocs/

Wheel Tag Management

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.

Capabilities

Tag Modification Function

Main function for modifying wheel tags and creating new wheel files with updated compatibility information.

def tags(
    wheel: str,
    python_tags: str | None = None,
    abi_tags: str | None = None,
    platform_tags: str | None = None,
    build_tag: str | None = None,
    remove: bool = False,
) -> str:
    """
    Modify wheel tags and create new wheel file.
    
    Parameters:
    - wheel: Path to input wheel file
    - python_tags: Python version tags (e.g., "py38.py39")
    - abi_tags: ABI tags (e.g., "cp38.cp39") 
    - platform_tags: Platform tags (e.g., "linux_x86_64.win_amd64")
    - build_tag: Build tag (must start with digit, no dashes)
    - remove: Delete original wheel file after creating new one
    
    Returns:
    Filename of newly created wheel
    
    Tag modification syntax:
    - Replace: "py38.py39" replaces all Python tags
    - Append: "+py310" adds py310 to existing tags
    - Remove: "-py37" removes py37 from existing tags
    - Multiple: Dot-separated values for multiple tags
    
    Raises:
    - AssertionError: If internal WHEEL tags don't match filename
    - WheelError: For invalid wheel files or format errors
    """

Tag Computation Utilities

Helper functions for computing and manipulating tag sets.

def _compute_tags(original_tags: Iterable[str], new_tags: str | None) -> set[str]:
    """
    Compute final tag set based on modification rules.
    
    Parameters:
    - original_tags: Current tags from wheel
    - new_tags: Tag modification string (None for no change)
    
    Returns:
    Final set of tags after applying modifications
    
    Modification rules:
    - None or empty: No change (returns original_tags)
    - Starting with '+': Append tags (union operation)
    - Starting with '-': Remove tags (difference operation)  
    - Other: Replace tags completely
    - Dot-separated: Multiple tags in single string
    """

Tag Validation

The tags function performs comprehensive validation to ensure wheel integrity:

  1. Filename Parsing: Validates wheel filename format using WHEEL_INFO_RE
  2. Internal Consistency: Verifies WHEEL file tags match filename tags
  3. Build Tag Consistency: Ensures build tags match between filename and WHEEL file
  4. Tag Format: Validates tag syntax and relationships

Usage Examples

Basic Tag Modifications

from wheel._commands.tags import tags

# Replace Python version tags
new_wheel = tags(
    'package-1.0-py3-none-any.whl',
    python_tags='py38.py39.py310'
)
# Result: package-1.0-py38.py39.py310-none-any.whl

# Add platform support
new_wheel = tags(
    'package-1.0-py3-none-any.whl',
    platform_tags='linux_x86_64.macosx_10_9_x86_64'
)
# Result: package-1.0-py3-none-linux_x86_64.macosx_10_9_x86_64.whl

# Set ABI tags for compiled extensions
new_wheel = tags(
    'package-1.0-py38-abi3-linux_x86_64.whl',
    abi_tags='cp38.cp39.cp310'
)
# Result: package-1.0-py38-cp38.cp39.cp310-linux_x86_64.whl

Append and Remove Operations

# Append Python version
new_wheel = tags(
    'package-1.0-py38.py39-none-any.whl',
    python_tags='+py310'
)
# Result: package-1.0-py38.py39.py310-none-any.whl

# Remove specific version
new_wheel = tags(
    'package-1.0-py37.py38.py39-none-any.whl', 
    python_tags='-py37'
)
# Result: package-1.0-py38.py39-none-any.whl

# Multiple operations (applied left to right)
new_wheel = tags(
    'package-1.0-py37.py38-none-any.whl',
    python_tags='-py37.+py39.+py310'
)
# Result: package-1.0-py38.py39.py310-none-any.whl

Build Tags

# Add build tag
new_wheel = tags(
    'package-1.0-py3-none-any.whl',
    build_tag='20231201'
)
# Result: package-1.0-20231201-py3-none-any.whl

# Remove build tag  
new_wheel = tags(
    'package-1.0-20231201-py3-none-any.whl',
    build_tag=''
)
# Result: package-1.0-py3-none-any.whl

File Management

# Keep original file (default)
new_wheel = tags('package-1.0-py3-none-any.whl', python_tags='py39')
# Creates: package-1.0-py39-none-any.whl
# Keeps: package-1.0-py3-none-any.whl

# Remove original file
new_wheel = tags(
    'package-1.0-py3-none-any.whl', 
    python_tags='py39',
    remove=True
)
# Creates: package-1.0-py39-none-any.whl  
# Deletes: package-1.0-py3-none-any.whl

Complex Tag Combinations

# Multiple tag types simultaneously
new_wheel = tags(
    'package-1.0-py3-none-any.whl',
    python_tags='py38.py39.py310',
    abi_tags='cp38.cp39.cp310', 
    platform_tags='linux_x86_64.win_amd64',
    build_tag='20231201'
)
# Result: package-1.0-20231201-py38.py39.py310-cp38.cp39.cp310-linux_x86_64.win_amd64.whl

Programmatic Tag Management

from wheel._commands.tags import tags, _compute_tags
from wheel.wheelfile import WheelFile

# Analyze current tags
with WheelFile('package-1.0-py3-none-any.whl', 'r') as wf:
    parsed = wf.parsed_filename
    current_python = parsed.group('pyver').split('.')
    current_abi = parsed.group('abi').split('.')
    current_platform = parsed.group('plat').split('.')
    
    print(f"Current Python tags: {current_python}")
    print(f"Current ABI tags: {current_abi}")
    print(f"Current platform tags: {current_platform}")

# Compute new tag set
new_python_tags = _compute_tags(current_python, '+py311')
print(f"New Python tags: {sorted(new_python_tags)}")

# Apply changes
new_wheel = tags(
    'package-1.0-py3-none-any.whl',
    python_tags='.'.join(sorted(new_python_tags))
)

Error Handling

from wheel._commands.tags import tags
from wheel.wheelfile import WheelError

try:
    # This will raise AssertionError if tags are inconsistent
    new_wheel = tags('corrupted-wheel.whl', python_tags='py39')
except AssertionError as e:
    print(f"Wheel integrity error: {e}")
except WheelError as e:
    print(f"Wheel format error: {e}")

Common Use Cases

Cross-Platform Distribution

# Create platform-specific variants from universal wheel
platforms = ['linux_x86_64', 'macosx_10_9_x86_64', 'win_amd64']

for platform in platforms:
    platform_wheel = tags(
        'package-1.0-py3-none-any.whl',
        platform_tags=platform
    )
    print(f"Created: {platform_wheel}")

Python Version Compatibility

# Update wheel for newer Python versions
python_versions = ['py39', 'py310', 'py311']

updated_wheel = tags(
    'package-1.0-py38-none-any.whl',
    python_tags='.'.join(python_versions)
)
print(f"Updated wheel: {updated_wheel}")

ABI Tag Updates

# Convert from specific ABI to abi3 (stable ABI)
stable_wheel = tags(
    'package-1.0-py38-cp38-linux_x86_64.whl',
    abi_tags='abi3'
)
print(f"Stable ABI wheel: {stable_wheel}")

Types

from collections.abc import Iterable
from typing import Literal

# No additional types beyond function signatures
# Uses standard library types and wheel.wheelfile.WheelFile

Install with Tessl CLI

npx tessl i tessl/pypi-wheel

docs

cli-commands.md

index.md

package-conversion.md

tag-management.md

wheelfile-ops.md

tile.json