Tool for validating Python wheel contents to detect common packaging errors and ensure proper distribution structure
npx @tessl/cli install tessl/pypi-check-wheel-contents@0.6.0A Python library and command-line tool for validating the contents of Python wheel distribution packages. It performs comprehensive checks to detect common packaging errors and ensures wheels contain appropriate files, helping developers avoid publishing wheels with issues like compiled bytecode files, duplicate files, modules in wrong locations, or missing package components.
pip install check-wheel-contentsimport check_wheel_contents
from check_wheel_contents import WheelChecker, WheelContents, Configuration
from check_wheel_contents.checks import Check, FailedCheck
from check_wheel_contents.errors import UserInputError, WheelValidationError# Check a single wheel
check-wheel-contents path/to/your-package-1.0.0-py3-none-any.whl
# Check all wheels in a directory
check-wheel-contents ./dist/
# Check with custom configuration
check-wheel-contents --config pyproject.toml path/to/wheel.whl
# Ignore specific checks
check-wheel-contents --ignore W001,W002 path/to/wheel.whl
# Select only specific checks
check-wheel-contents --select W1 path/to/wheel.whlfrom pathlib import Path
from check_wheel_contents import WheelChecker, WheelContents
from check_wheel_contents.checks import Check
# Create a checker instance
checker = WheelChecker()
# Load and analyze a wheel
wheel_path = Path("dist/my-package-1.0.0-py3-none-any.whl")
contents = WheelContents.from_wheel(wheel_path)
# Run all checks
failures = checker.check_contents(contents)
# Process results
if failures:
for failure in failures:
print(failure.show(str(wheel_path)))
else:
print(f"{wheel_path}: OK")The package follows a modular architecture with clear separation of concerns:
Core functionality for parsing wheel files and extracting their internal structure. Provides detailed representation of files, directories, metadata, and distribution information.
class WheelContents:
dist_info_dir: str
data_dir: str
root_is_purelib: bool
by_signature: dict[tuple[int | None, str | None], list[File]]
filetree: Directory
@classmethod
def from_wheel(cls, wheel_path: Path) -> 'WheelContents': ...
@property
def purelib_tree(self) -> Directory: ...
@property
def platlib_tree(self) -> Directory: ...Comprehensive validation system with configurable checks for common wheel packaging issues. Supports built-in checks (W001-W202) and custom validation rules.
class WheelChecker:
selected: set[Check]
toplevel: list[str] | None
pkgtree: Directory | None
def configure_options(self, configpath=None, select: set[Check] | None = None,
ignore: set[Check] | None = None,
toplevel: list[str] | None = None,
package: tuple[str, ...] = (),
src_dir: tuple[str, ...] = (),
package_omit: list[str] | None = None) -> None: ...
def check_contents(self, contents: WheelContents) -> list[FailedCheck]: ...Flexible configuration system supporting multiple file formats (TOML, INI) and command-line overrides. Handles check selection, package expectations, and directory traversal patterns.
class Configuration:
select: set[Check] | None
ignore: set[Check] | None
toplevel: list[str] | None
package_paths: list[Path] | None
src_dirs: list[Path] | None
package_omit: list[str] | None
@classmethod
def from_config_file(cls, path: str | None = None) -> 'Configuration': ...class Check(Enum):
W001 = "Wheel contains .pyc/.pyo files"
W002 = "Wheel contains duplicate files"
W003 = "Wheel contains non-module at library toplevel"
W004 = "Module is not located at importable path"
W005 = "Wheel contains common toplevel name in library"
W006 = "__init__.py at top level of library"
W007 = "Wheel library is empty"
W008 = "Wheel is empty"
W009 = "Wheel contains multiple toplevel library entries"
W010 = "Toplevel library directory contains no Python modules"
W101 = "Wheel library is missing files in package tree"
W102 = "Wheel library contains files not in package tree"
W201 = "Wheel library is missing specified toplevel entry"
W202 = "Wheel library has undeclared toplevel entry"
class FailedCheck:
check: Check
args: list[str]
def show(self, filename: str | None = None) -> str: ...
class File:
parts: tuple[str, ...]
size: int | None
hashsum: str | None
@classmethod
def from_record_row(cls, row: list[str]) -> 'File': ...
@property
def path(self) -> str: ...
@property
def signature(self) -> tuple[int | None, str | None]: ...
class Directory:
path: str | None
entries: dict[str, File | 'Directory']
@classmethod
def from_record_rows(cls, rows: list[list[str]]) -> 'Directory': ...
def iterdir(self) -> Iterator[File | 'Directory']: ...
def lookup(self, path: str) -> File | 'Directory' | None: ...
class UserInputError(ValueError):
"""Exception for invalid user input values"""
class WheelValidationError(Exception):
"""Exception for invalid or malformed wheels"""