Anatomy of a Python Wheel

A wheel is a ZIP archive of built package files. Its filename and metadata tell an installer how to use it. The examples come from the published packaging 25.0 release.

Filename fields

packaging-25.0-py3-none-any.whl
FieldValue
Distributionpackaging
Version25.0
Python tagpy3
ABI tagnone
Platform tagany

This filename comes from the published packaging 25.0 release. All file excerpts in this guide come from that wheel.

A wheel filename contains the distribution name and version, followed by Python, application binary interface (ABI), and platform tags. It can also contain a build tag before the Python tag. The build tag must start with a digit. See the filename specification.

Installers compare the compatibility tags with the target environment. They must also account for metadata such as Requires-Python. This wheel uses py3-none-any, but its metadata requires Python 3.8 or later. See the tag specification.

Try it: check Python compatibility before downloading

Use an empty working directory and a current Python with pip. In these commands, python is that interpreter.

Ask pip to select a wheel for Python 3.7. You do not need Python 3.7 installed to run this check.

python -m pip download --only-binary :all: --no-deps --python-version 37 --index-url https://pypi.org/simple --dest artifacts packaging==25.0

This command rejects version 25.0 because it requires Python 3.8 or later. The py3 tag does not override that requirement.

Change only the target Python version:

python -m pip download --only-binary :all: --no-deps --python-version 38 --index-url https://pypi.org/simple --dest artifacts packaging==25.0

This saves artifacts/packaging-25.0-py3-none-any.whl. It downloads the wheel; it does not install it. Keep this file for the next examples.

Both commands were checked with pip 25.3. See pip download for the target-Python option.

Compatibility tags

FieldThis wheelMeaningOther tag forms
Python py3 Python 3, without an interpreter-specific tag. cp identifies CPython. pp identifies PyPy. These prefixes form part of a tag with a version.
ABI none No Python ABI requirement. abi3 names the CPython stable ABI. Interpreter-specific tags identify other ABI requirements.
Platform any No platform restriction in this tag. manylinux tags specify a glibc baseline and architecture. musllinux tags use a musl baseline.

The tag specification defines each field. A tag such as macosx_11_0_arm64 means macOS 11 or later on arm64. Tags describe compatibility; they do not prove that the package is safe.

Checkpoint 1 · reading the filename and its tags

Q1In packaging-25.0-py3-none-any.whl, what does py3 identify?

B. py3 is the Python tag. In the same filename, none is the ABI tag and any is the platform tag.

Q2The published wheel is tagged py3-none-any, but its METADATA lists Requires-Python: >=3.8. What does this mean for an installer?

D. Compatibility tags and Requires-Python impose separate requirements. This wheel requires Python 3.8 or later even though its Python tag is py3.

Q3The guide notes a tag such as macosx_11_0_arm64. What can a compatibility tag actually tell an installer?

A. Tags describe environment compatibility. They do not establish whether the code is safe or whether its license permits a particular use.

Archive contents

These selected paths occur in the published wheel. The archive also contains other packaging modules. The list does not claim to show every member.

packaging/__init__.py
packaging/tags.py
packaging/py.typed
packaging-25.0.dist-info/METADATA
packaging-25.0.dist-info/WHEEL
packaging-25.0.dist-info/RECORD
packaging-25.0.dist-info/licenses/LICENSE
packaging-25.0.dist-info/licenses/LICENSE.APACHE
packaging-25.0.dist-info/licenses/LICENSE.BSD

The .dist-info directory must contain METADATA, WHEEL, and RECORD. A wheel can also contain a .data directory for files that use other install paths. Installers place those files according to the wheel specification.

For metadata version 2.4 or later, license files named in License-File belong under .dist-info/licenses/. The corresponding sdist stores those files relative to its source root.

Try it: list files without installing the wheel

From the same working directory, use Python's ZIP reader:

python -m zipfile --list artifacts/packaging-25.0-py3-none-any.whl

The list includes packaging/tags.py and the three required .dist-info files shown above. It also shows each file's size.

You have inspected the archive, not imported packaging or installed its files. See Python's ZIP command-line interface.

Key files

METADATA

Metadata-Version: 2.4
Name: packaging
Version: 25.0
Requires-Python: >=3.8

These selected headers identify the release and its Python requirement. This wheel has no Requires-Dist headers. Other wheels use that field for dependencies. See Core Metadata.

An index can provide Core Metadata as a separate .metadata file. Clients can then read it without downloading the whole wheel. The Simple Repository API reports whether that file exists.

Try it: read the wheel's requirements

Save this code as read_wheel.py beside the artifacts/ directory. Run python read_wheel.py.

from email.parser import Parser
from zipfile import ZipFile

with ZipFile("artifacts/packaging-25.0-py3-none-any.whl") as wheel:
    metadata = Parser().parsestr(wheel.read("packaging-25.0.dist-info/METADATA").decode())
print("Requires-Python:", metadata["Requires-Python"])
print("Requires-Dist:", metadata.get_all("Requires-Dist", []))

Output from the downloaded wheel:

Requires-Python: >=3.8
Requires-Dist: []

The empty list means this wheel declares no runtime dependencies. It still has a Python-version requirement, which explains the failed Python 3.7 selection.

WHEEL

Wheel-Version: 1.0
Generator: flit 3.12.0
Root-Is-Purelib: true
Tag: py3-none-any

This is the complete WHEEL file. It records the wheel format version, build tool, install location flag, and compatibility tag. Root-Is-Purelib: true directs the installer to place root files in the purelib install location. See the wheel specification.

RECORD

packaging/__init__.py,sha256=_0cDiPVf2S-bNfVmZguxxzmrIYWlyASxpqph4qsJWUc,494
packaging-25.0.dist-info/RECORD,,

These are the first and last rows, copied without changes. Each row holds a path, a hash, and a byte count. The hash uses URL-safe base64 without trailing padding. RECORD itself has empty hash and size fields.

The wheel rules require SHA-256 or a stronger hash for file contents. They exclude RECORD itself and the deprecated signature files from some record requirements. Installers verify recorded hashes during extraction. These hashes check file integrity, not the safety of the code.

Checkpoint 2 · archive contents and file integrity

Q4Where must a license file named in License-File live, for metadata version 2.4 or later?

C. Named license files belong under .dist-info/licenses/ in wheels using metadata 2.4 or later. The real archive includes its license files there.

Q5RECORD lists a sha256 hash for packaging/__init__.py. If the extracted file's hash matches, what has actually been confirmed?

B. The bytes match the hash recorded in RECORD. This does not prove the code is safe: someone could replace both the file and its recorded hash.

Constraints

ConstraintValueNote
PyPI file limit 100.0 MB The default limit for one uploaded file. A project can request an increase. This is PyPI policy, not a wheel-format limit.
PyPI project limit 10.0 GB The default total for all files in a project. See PyPI storage limits for both limits and the request process.
Metadata scope Per file Do not assume that all wheels for a release have identical metadata. The Core Metadata specification explains the limits of that assumption.
Format version Check WHEEL An installer must reject an unsupported major wheel-format version. The wheel specification recommends a warning for a higher minor version.

Checkpoint 3 · limits, policy, and format versions

Q6PyPI enforces a default 100.0 MB limit on one uploaded file. What kind of limit is this?

A. PyPI applies this limit to uploads, and a project can request an increase. It is not a wheel-format requirement.

Q7This wheel's WHEEL file records Wheel-Version: 1.0. Suppose a future wheel's WHEEL file records Wheel-Version: 2.0, and an installer only supports major version 1. What must that installer do?

D. An installer must reject an unsupported major wheel-format version. The specification recommends a warning for a higher minor version.

Proposed wheel variants

PEP 817, Wheel Variants: Beyond Platform Tags, has Draft status as checked on 13 September 2026. Its text is a proposal, not a final wheel standard.

The proposal addresses requirements that current tags cannot express, such as graphics processor features and builds against different library ABIs. It proposes variant metadata and provider plugins that help installers select a build for a machine.

Use the current wheel specification for standard wheel files. Consult PEP 817 for the proposed format and its status. The proposal can change during review.

Cross-reference

PEP 817's motivation, and the two accompanying proposals PEP 825 (Wheel Variants: Package Format) and PEP 777 (How to Re-invent the Wheel), are treated in depth in Wheels & What's (Wheel)Next.

Checkpoint 4 · the proposed variant format

Q8As checked 13 September 2026, what is PEP 817's status, and what does that mean for the variant-wheel description above?

C. PEP 817 was a Draft when checked on 13 September 2026. A draft can change and is not a final standard.