A wheel is a ZIP archive with UTF-8 file names. This
page follows the Python Packaging Authority’s “Binary distribution format” specification
(first PEP 427, February 2013) and its “Entry points specification”. The
METADATA file is described on the core
metadata page.
File name
{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
| Part | Rule |
|---|---|
| distribution | The normalised project name: runs of -, _ and . become _, letters lower-case. Readers must still accept . and capitals. |
| version | The normalised version; it cannot contain -. |
| build tag | Optional; must start with a digit; breaks ties between otherwise equal names. Sorted as an empty tuple when absent, else as (leading digits as an integer, rest as a string). |
| python tag | Compatibility tag for the Python implementation and version, such as py3 or cp312. |
| abi tag | Compatibility tag for the ABI, such as none or cp312. |
| platform tag | Compatibility tag for the platform, such as any or win_amd64. |
No part may contain -. Example from the specification:
distribution-1.0-1-py27-none-any.whl. The three tags are defined by the
platform compatibility tags specification (PEP 425).
The .dist-info folder
{distribution}-{version}.dist-info/, which archivers should place at the
end of the ZIP so that metadata can be changed without rewriting the archive.
| Entry | Status | Contents |
|---|---|---|
METADATA | Required | Core metadata, version 1.1 or later. |
WHEEL | Required | Wheel metadata. |
RECORD | Required | Every file with its hash. |
entry_points.txt | Optional | Entry points. |
licenses/ | Required with metadata 2.4+ and License-File | The licence files (PEP 639). license_files/ and LICENSES/ are reserved names. |
sboms/ | Optional | Software bills of materials describing the contents (PEP 770). |
RECORD.jws, RECORD.p7s | Deprecated (PEP 815) | Signatures of RECORD; build tools must not add them, but installers may still meet them. |
INSTALLER and REQUESTED belong to installed projects and
are not in a wheel.
WHEEL
Key: value lines:
| Key | Meaning |
|---|---|
Wheel-Version | Version of the wheel specification, such as 1.0. Installers warn on a newer minor version and fail on a newer major version. |
Generator | The tool that built the wheel, optionally with its version. |
Root-Is-Purelib | true: the archive root is installed into purelib; otherwise into platlib. |
Tag | The wheel’s expanded compatibility tags; the specification’s example is py2.py3-none-any. |
Build | The build number; left out when there is none. |
RECORD
Lists almost every file in the wheel with its hash; RECORD itself cannot hash itself. Hashes must use SHA-256 or a stronger algorithm; MD5 and SHA-1 are not permitted. Installers check every hash, and installation fails if any file other than RECORD, RECORD.jws or RECORD.p7s is missing from it or has the wrong hash. The CSV layout of each line (path, hash, size) is set by the “Recording installed projects” specification, which was not read for this page.
The .data folder
{distribution}-{version}.data/ holds files that are not installed into
site-packages, in subfolders named after sysconfig installation paths:
| Subfolder | Installed to |
|---|---|
purelib | The sysconfig purelib path. |
platlib | The sysconfig platlib path. |
headers | The headers installation path. |
scripts | Scripts; only regular files. A script must start with exactly #!python (or #!pythonw for a GUI script), which the installer rewrites. |
data | The sysconfig data path. |
Installation
- Unpack: read
WHEEL, checkWheel-Version, and unpack the archive root into purelib or platlib according toRoot-Is-Purelib. - Spread: move each
.datasubfolder to its destination; rewrite#!pythonlines to the right interpreter; make Windows.exewrappers where needed (on Unix, set the executable bit); update RECORD with the installed paths; remove the empty.datafolder; compile.pyfiles to.pyc.
entry_points.txt
UTF-8 INI: each section is an entry point group, each line
name = object reference, where the reference is
module or module:attr.attr, optionally followed by extras in
brackets (foomod:main_bar [bar,baz]). Names are case-sensitive, may not
contain =, start with [ or begin or end with white space;
new names should use letters, digits, _, . and
-. Group names are dot-separated words; a project defining a new group
should prefix it with a PyPI name it owns.
| Group | Meaning |
|---|---|
console_scripts | Each name becomes a command that calls the function with no arguments; its integer return value is the exit code. Runs in a console on Windows. |
gui_scripts | The same, but runs without a console on Windows. |
[console_scripts]
foo = foomod:main
foobar = foomod:main_bar [bar,baz]
History
| Date | Change |
|---|---|
| February 2013 | Approved as PEP 427. |
| February 2021 | File name escaping rules revised to match common tools. |
| December 2024 | scripts/ limited to regular files; .dist-info/licenses/ added (PEP 639). |
| January 2025 | Name and version normalisation clarified for the .dist-info and .data folders. |
| January 2026 | RECORD.jws and RECORD.p7s deprecated (PEP 815). |
Sources
Python Packaging User Guide, specifications: “Binary distribution format” and “Entry points specification”. Example tag values in the file name table are illustrations; the tags themselves are defined in the “Platform compatibility tags” specification, not covered here.