Command line tool for manipulating wheel files
Convert legacy Python package formats (.egg files and Windows installers) to modern wheel format. Supports both zipped .egg files and unpacked .egg directories, as well as Windows executable installers created with bdist_wininst.
Convert multiple package files to wheel format with metadata transformation and platform detection.
def convert(files: list[str], dest_dir: str, verbose: bool) -> None:
"""
Convert .egg files and Windows installers to wheels.
Parameters:
- files: List of file patterns (supports glob patterns)
- dest_dir: Output directory for converted wheels
- verbose: Print conversion progress messages
Supported formats:
- .egg files (zipped archives)
- .egg directories (unpacked)
- .exe files (Windows bdist_wininst installers)
"""Abstract base class and implementations for different package formats.
class ConvertSource(metaclass=ABCMeta):
"""
Abstract base class for conversion sources.
"""
name: str
"""Package name (normalized)."""
version: str
"""Package version."""
pyver: str
"""Python version tags (default: "py2.py3")."""
abi: str
"""ABI tags (default: "none")."""
platform: str
"""Platform tags (default: "any")."""
metadata: Message
"""Email message object containing package metadata."""
@property
def dist_info_dir(self) -> str:
"""Get .dist-info directory name."""
return f"{self.name}-{self.version}.dist-info"
@abstractmethod
def generate_contents(self) -> Iterator[tuple[str, bytes]]:
"""Generate (filename, content) pairs for wheel contents."""
passConvert zipped .egg files to wheel format with metadata transformation.
class EggFileSource(ConvertSource):
"""
Convert .egg files to wheels.
Handles:
- Binary wheels detection (assumes CPython)
- PKG-INFO metadata conversion
- requires.txt dependency conversion
- entry_points.txt preservation
"""
def __init__(self, path: Path):
"""
Initialize from .egg file path.
Parameters:
- path: Path to .egg file
Raises:
- ValueError: If filename doesn't match .egg pattern
"""
def generate_contents(self) -> Iterator[tuple[str, bytes]]:
"""
Generate wheel contents from .egg file.
Yields:
Tuples of (archive_path, file_content)
Processing:
- Converts EGG-INFO/ to .dist-info/
- Transforms PKG-INFO to METADATA
- Converts requires.txt to Requires-Dist headers
- Preserves entry_points.txt
"""Convert unpacked .egg directories to wheel format.
class EggDirectorySource(EggFileSource):
"""
Convert .egg directories to wheels.
Similar to EggFileSource but reads from filesystem directory
instead of zip archive.
"""
def generate_contents(self) -> Iterator[tuple[str, bytes]]:
"""
Generate wheel contents from .egg directory.
Yields:
Tuples of (archive_path, file_content)
Updates name and version from PKG-INFO if available.
"""Convert Windows executable installers to wheel format with platform detection.
class WininstFileSource(ConvertSource):
"""
Convert Windows bdist_wininst installers to wheels.
Handles:
- Platform detection from installer filename
- .pyd file ABI detection
- .egg-info metadata extraction
- SCRIPTS directory mapping to .data/scripts/
"""
def __init__(self, path: Path):
"""
Initialize from Windows installer path.
Parameters:
- path: Path to .exe installer file
Processing:
- Extracts platform from filename pattern
- Scans for .egg-info and .pyd files for metadata
- Determines Python version and ABI from content
"""
def generate_contents(self) -> Iterator[tuple[str, bytes]]:
"""
Generate wheel contents from Windows installer.
Yields:
Tuples of (archive_path, file_content)
Mapping:
- SCRIPTS/ -> {name}-{version}.data/scripts/
- .egg-info/ -> .dist-info/ (with metadata conversion)
"""Utility functions for converting between metadata formats.
def convert_requires(requires: str, metadata: Message) -> None:
"""
Convert requires.txt format to Requires-Dist headers.
Parameters:
- requires: Content of requires.txt file
- metadata: Message object to add headers to
Format handling:
- [extra] sections become conditional requirements
- Requirement lines converted to Requires-Dist format
- Generates Provides-Extra headers for extras
"""
def convert_pkg_info(pkginfo: str, metadata: Message) -> None:
"""
Convert PKG-INFO format to Metadata 2.4 format.
Parameters:
- pkginfo: PKG-INFO file content
- metadata: Message object to populate
Transformations:
- Updates Metadata-Version to 2.4
- Converts Description to message body
- Maps Home-page to Project-URL: Homepage
- Maps Download-URL to Project-URL: Download
- Filters out "UNKNOWN" values
"""
def normalize(name: str) -> str:
"""
Normalize package name for wheel filename.
Parameters:
- name: Package name to normalize
Returns:
Normalized name ([-_.] replaced with underscores, lowercased)
"""from wheel._commands.convert import convert
import os
# Convert all .egg files in current directory
egg_files = ['package-1.0-py2.7.egg', 'another-2.0.egg']
convert(egg_files, dest_dir='wheels/', verbose=True)
# Using glob patterns
convert(['*.egg'], dest_dir='dist/', verbose=False)from wheel._commands.convert import EggFileSource
from wheel.wheelfile import WheelFile
from pathlib import Path
# Create custom conversion
egg_path = Path('package-1.0.egg')
source = EggFileSource(egg_path)
# Generate wheel
wheel_path = f"{source.name}-{source.version}-{source.pyver}-{source.abi}-{source.platform}.whl"
with WheelFile(wheel_path, 'w') as wf:
for name, content in source.generate_contents():
wf.writestr(name, content)
# Add generated metadata
wf.writestr(
f"{source.dist_info_dir}/METADATA",
source.metadata.as_string(policy=serialization_policy).encode('utf-8')
)from wheel._commands.convert import convert
# Convert Windows installers
convert(['package-1.0.win32-py3.8.exe'], dest_dir='wheels/', verbose=True)
# Results in: package-1.0-py38-cp38-win32.whlimport re
from email.message import Message
from email.policy import EmailPolicy
from abc import ABCMeta, abstractmethod
from collections.abc import Iterator
from pathlib import Path
# Filename parsing patterns
egg_filename_re: re.Pattern[str]
"""Pattern for parsing .egg filenames: name-version(-pyver)(-arch).egg"""
egg_info_re: re.Pattern[str]
"""Pattern for parsing .egg-info directory names"""
wininst_re: re.Pattern[str]
"""Pattern for parsing Windows installer filenames"""
pyd_re: re.Pattern[str]
"""Pattern for parsing .pyd file extensions for ABI detection"""
# Email serialization policy
serialization_policy: EmailPolicy
"""Email policy for metadata serialization (UTF-8, no line wrapping)"""
GENERATOR: str
"""Generator string for WHEEL files (f"wheel {__version__}")"""Install with Tessl CLI
npx tessl i tessl/pypi-wheel