Command line tool for manipulating wheel files
Core functionality for reading, writing, and manipulating wheel archives. The WheelFile class extends Python's ZipFile with wheel-specific features including hash verification, reproducible builds, and wheel format compliance.
A ZipFile derivative that reads SHA-256 hashes from .dist-info/RECORD and verifies file integrity during read operations.
class WheelFile(ZipFile):
"""
A ZipFile derivative class that also reads SHA-256 hashes from
.dist-info/RECORD and checks any read files against those.
"""
def __init__(
self,
file: str | os.PathLike[str],
mode: Literal["r", "w", "x", "a"] = "r",
compression: int = ZIP_DEFLATED,
):
"""
Initialize wheel file.
Parameters:
- file: Path to wheel file
- mode: File mode ('r', 'w', 'x', 'a')
- compression: Compression method (ZIP_DEFLATED default)
Raises:
- WheelError: If filename doesn't match wheel naming convention
"""
parsed_filename: re.Match[str]
"""Regex match object containing parsed wheel filename components."""
dist_info_path: str
"""Path to .dist-info directory within wheel."""
record_path: str
"""Path to RECORD file within wheel."""Read and write files within wheel archives with automatic hash verification and generation.
def open(
self,
name_or_info: str | ZipInfo,
mode: Literal["r", "w"] = "r",
pwd: bytes | None = None,
) -> IO[bytes]:
"""
Open file in wheel with hash verification.
Parameters:
- name_or_info: Filename or ZipInfo object
- mode: File mode ('r' or 'w')
- pwd: Password for encrypted files
Returns:
File-like object
Raises:
- WheelError: If file hash doesn't match RECORD or file not in RECORD
"""
def write(
self,
filename: str,
arcname: str | None = None,
compress_type: int | None = None,
) -> None:
"""
Write file to wheel archive.
Parameters:
- filename: Path to source file
- arcname: Archive name (uses filename if None)
- compress_type: Compression method override
"""
def writestr(
self,
zinfo_or_arcname: str | ZipInfo,
data: bytes | str,
compress_type: int | None = None,
) -> None:
"""
Write string or bytes data to wheel archive.
Parameters:
- zinfo_or_arcname: Archive filename or ZipInfo object
- data: Data to write (str will be UTF-8 encoded)
- compress_type: Compression method override
"""
def write_files(self, base_dir: str) -> None:
"""
Write entire directory tree to wheel.
Parameters:
- base_dir: Base directory to add to wheel
Note: Defers .dist-info files to write last, skips RECORD file
"""
def close(self) -> None:
"""
Close wheel file, automatically generating RECORD file for write mode.
"""URL-safe base64 encoding/decoding functions used for hash storage in RECORD files.
def urlsafe_b64encode(data: bytes) -> bytes:
"""
URL-safe base64 encoding without padding.
Parameters:
- data: Bytes to encode
Returns:
Encoded bytes without '=' padding
"""
def urlsafe_b64decode(data: bytes) -> bytes:
"""
URL-safe base64 decoding without padding.
Parameters:
- data: Encoded bytes (padding added automatically)
Returns:
Decoded bytes
"""Support for reproducible wheel builds using SOURCE_DATE_EPOCH environment variable.
def get_zipinfo_datetime(
timestamp: float | None = None,
) -> tuple[int, int, int, int, int]:
"""
Get datetime tuple for ZipInfo, supporting reproducible builds.
Parameters:
- timestamp: Unix timestamp (uses SOURCE_DATE_EPOCH env var or current time)
Returns:
Datetime tuple (year, month, day, hour, minute) for ZipInfo
Note: Minimum timestamp is 1980-01-01 00:00:00 UTC (315532800)
"""from wheel.wheelfile import WheelFile
with WheelFile('package-1.0-py3-none-any.whl', 'r') as wf:
# Access parsed filename components
name = wf.parsed_filename.group('name')
version = wf.parsed_filename.group('ver')
# List all files
for info in wf.filelist:
print(f"{info.filename} ({info.file_size} bytes)")
# Read specific file with hash verification
with wf.open('package/__init__.py') as f:
content = f.read().decode('utf-8')
print(content)from wheel.wheelfile import WheelFile
import os
with WheelFile('new_package-1.0-py3-none-any.whl', 'w') as wf:
# Write files from directory (automatically generates hashes)
wf.write_files('build/lib/')
# Write individual files
wf.write('README.txt', 'new_package-1.0.dist-info/README.txt')
# Write string data
metadata = """Name: new-package
Version: 1.0
"""
wf.writestr('new_package-1.0.dist-info/METADATA', metadata)
# RECORD file is automatically generated on close()import os
from wheel.wheelfile import WheelFile
# Set reproducible timestamp
os.environ['SOURCE_DATE_EPOCH'] = '1640995200' # 2022-01-01
with WheelFile('reproducible-1.0-py3-none-any.whl', 'w') as wf:
wf.write_files('src/')
# All files will have the same timestamp for reproducible buildsfrom typing import IO, Literal
from zipfile import ZipInfo
import re
class WheelError(Exception):
"""Exception raised for wheel-related errors."""
pass
WHEEL_INFO_RE: re.Pattern[str]
"""
Regex pattern for parsing wheel filenames.
Matches: name-version(-build)?-python-abi-platform.whl
Groups: namever, name, ver, build, pyver, abi, plat
"""
MINIMUM_TIMESTAMP: int
"""Minimum timestamp for wheel files (315532800 = 1980-01-01 00:00:00 UTC)"""Install with Tessl CLI
npx tessl i tessl/pypi-wheel