ZipToolView zip files online — 100% private

What is a .whl file? Python wheels, tags, and how to inspect them

A .whl file is a Python wheel — the standard built-package format that pip installs. It was defined by PEP 427 and is now the dominant distribution format on PyPI. A wheel is simply a ZIP archive with a .whl extension and a specially formatted filename that tells pip which Python interpreter, ABI, and platform it was built for. Because the package is already built, pip installs it by unpacking it straight into your site-packages directory — no compiler required at install time.

The filename is not cosmetic; it encodes compatibility. The format is: {name}-{version}(-{build tag})-{python tag}-{abi tag}-{platform tag}.whl. A pure-Python wheel that runs anywhere ends in -py3-none-any.whl (any Python 3, any ABI, any platform). A CPython 3.12 wheel with compiled C extensions for 64-bit Linux might be numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.whl. Those tags are how pip decides whether a given wheel matches your environment before it will install it.

What a .whl file actually is

A Python wheel is a ZIP-format archive with a .whl extension. Rename it to .zip and any unzip tool will open it. PEP 427 defined the format in 2012; PEP 425 (and later the PyPA platform compatibility tags spec) defined the filename tags wheels carry. Wheels replaced the older .egg format and are what pip downloads from PyPI by default.

The key advantage is that a wheel is already built. For a pure-Python package that just means the .py files are collected and zipped. For a package with C/C++/Rust extensions, the wheel contains the pre-compiled platform-specific extension modules for a specific Python version and CPU architecture — .so on Linux and macOS, .pyd on Windows. (Python's import machinery uses the .so suffix for C extensions even on macOS, not .dylib; .dylib is for shared libraries Python links against, not importable modules.) pip unpacks the wheel into your site-packages and uses the manifest inside it to track every installed file for a clean uninstall later.

Decoding the filename tags

The filename {name}-{version}(-{build tag})-{python tag}-{abi tag}-{platform tag}.whl is what pip reads first. Each tag position narrows compatibility. Here is what each one means, with concrete examples.

The name and version are the package name (underscores become dashes) and its PEP 440 version. The optional build tag (a leading integer, like -1-) is rare; it only acts as a tiebreaker when two wheels share the same version. The three tag positions that matter are python, abi, and platform.

  • python tag — the interpreter implementation and version it targets. py3 means any Python 3; py2.py3 means both Python 2 and 3; cp312 means CPython 3.12 specifically; pp37 means PyPy 3.7.
  • abi tag — the application binary interface the compiled extensions require. none means no ABI dependency (pure Python, or extensions built against the limited/stable ABI); cp312 means the CPython 3.12 ABI; abi3 means the stable limited ABI that works across CPython 3.x versions.
  • platform tag — the OS and CPU. any means platform-independent; manylinux_2_17_x86_64 is a 64-bit Intel/AMD Linux wheel; macosx_11_0_arm64 is Apple Silicon macOS; win_amd64 is 64-bit Windows; linux_aarch64 is 64-bit ARM Linux.

Two real filenames side by side

A pure-Python wheel: requests-2.31.0-py3-none-any.whl. The py3-none-any suffix says it imports on any Python 3 interpreter, needs no specific ABI, and runs on any operating system. One file, every platform. That is the normal case for pure-Python libraries.

A compiled wheel: numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.whl. cp312 means CPython 3.12; the second cp312 (abi tag) means it links against the CPython 3.12 ABI; manylinux_2_17_x86_64 means it was built for glibc-based 64-bit Linux. pip will refuse to install it on Python 3.11 or on macOS. A single numpy release ships dozens of these wheels — one per Python version per platform — and pip picks the matching one automatically.

The internal structure of a wheel

Unzip a wheel and you find two kinds of content at the root: the importable package, and a metadata directory named {name}-{version}.dist-info/. The package directory is what actually gets imported — top-level modules and subpackages as .py files, plus compiled extension modules (.so on Linux and macOS, .pyd on Windows) for packages with C extensions. A wheel built for one platform only contains the extension files for that platform.

The {name}-{version}.dist-info/ directory holds the install metadata. Its layout is defined in PEP 376. The important files it ships inside the wheel:

  • METADATA — the package metadata in RFC 822-style headers: name, version, summary, author, license, and most usefully Requires-Dist (the dependencies pip will pull in).
  • RECORD — a CSV manifest of every file the wheel installs, with paths and hashes. It ships in the wheel and pip rewrites it on install with accurate hashes; pip then consults RECORD on uninstall to delete exactly those files.
  • WHEEL — wheel-specific metadata: Wheel-Version, Generator, and the Tag (or Tags) line that restates the python/abi/platform compatibility.
  • entry_points.txt — optional, INI-format file declaring console scripts (the commands the package adds to your PATH, like pytest or flake8) and plugin hooks.
  • top_level.txt — optional, the top-level importable module names. Note this is a setuptools convention, not required by the spec: wheels built with other backends like Poetry or flit may omit it.

How pip uses the wheel

When you run pip install, pip resolves dependencies, then for each package picks the best-matching wheel for your interpreter and platform by reading the filename tags. It downloads the wheel, verifies it, and unpacks its contents straight into site-packages — no build step, no compiler invocation. pip also writes a few extra files into the .dist-info directory at install time, notably INSTALLER (recording that pip did the install) and direct_url.json (where it came from); these are not shipped inside the wheel itself. RECORD is what pip consults on pip uninstall to remove exactly the files it placed.

Wheel vs source distribution (sdist)

A wheel is a binary, pre-built distribution: pip just unpacks it. A source distribution, or sdist, is a .tar.gz (sometimes .zip) of the project source code. Installing an sdist means pip has to build it — for a pure-Python package that is just running a PEP 517 build backend; for a package with C extensions it means invoking a C compiler, which requires a working build toolchain (gcc/clang, Python headers, sometimes cmake or Rust) on the machine.

You see an sdist instead of a wheel in a few cases: the project has not published wheels for your platform; it is a legacy package; or it is pure source by design (some projects only ship source). Modern PyPI projects publish wheels for every supported combination and fall back to sdist only when no matching wheel exists. The rule of thumb: pip prefers wheels; if it cannot find a matching one, it builds from the sdist, and that is the moment missing-compiler errors appear.

Inspecting a wheel before you install it

Because a wheel is a ZIP, you can read everything inside it without running pip — and you should, for any wheel from a source you have not vetted. Reading METADATA tells you the declared dependencies before pip pulls them in. Reading the filename tags tells you exactly which Python version and platform it claims. Listing the package directory shows what gets imported and whether it ships compiled binaries.

The fastest way to do this without installing anything is to open the .whl in an in-browser viewer. ZipTool parses the archive entirely in your browser via zip.js, so the wheel is never uploaded to a server — your file contents stay on your machine. That is a genuine privacy benefit for inspecting a wheel with sensitive or proprietary code, but keep the distinction clear: nothing being uploaded does not mean opening an untrusted file is risk-free. Parsing still runs JavaScript in your browser tab, and a malicious archive is still malicious. For a wheel you do not trust, the safest move is a throwaway VM or container, not your main environment.

To use it: drag the .whl onto the tool, browse the file tree, and read METADATA, RECORD, WHEEL, and the package source directly. Once you are satisfied, run pip install <wheel>. See the archive formats overview for other ZIP-based formats (.apk, .epub, .jar, .docx) you can open the same way.

Frequently asked questions

What is a .whl file?

A .whl file is a Python wheel: a ZIP-format archive that is the standard built-package format pip installs. Defined by PEP 427, it contains the importable package plus a .dist-info metadata directory, and its filename encodes which Python version and platform it was built for. Because it is pre-built, pip installs it by unpacking it into site-packages with no compiler needed.

How do I install a Python wheel?

Run pip install path/to/package-1.0.0-py3-none-any.whl from the directory containing the wheel, or give pip the full path. pip reads the filename tags to confirm the wheel matches your interpreter and platform, unpacks it into your site-packages, and writes the RECORD manifest for later uninstall. If the tags do not match (for example a cp312 wheel on Python 3.11), pip will refuse with an error.

What do the tags in a wheel filename mean?

The filename is {name}-{version}(-{build tag})-{python tag}-{abi tag}-{platform tag}.whl. The python tag (py3, cp312) says which interpreter and version it targets; the abi tag (none, abi3, cp312) says which binary interface compiled extensions need; the platform tag (any, manylinux_2_17_x86_64, macosx_11_0_arm64, win_amd64) says which OS and CPU. A py3-none-any wheel runs anywhere; a cp312-cp312-manylinux_2_17_x86_64 wheel runs only on CPython 3.12 on 64-bit Linux.

What is the difference between a wheel and a source distribution (sdist)?

A wheel (.whl) is a binary, pre-built distribution: pip just unpacks it into site-packages. A source distribution, or sdist (.tar.gz), is the raw project source that pip must build before installing — for packages with C extensions that means invoking a C compiler and requires a build toolchain on your machine. pip prefers wheels and only falls back to building from an sdist when no matching wheel exists, which is when missing-compiler errors typically appear.

Is a Python wheel just a zip?

Yes. A .whl file is a standard ZIP archive — rename it to .zip and any unzip tool will open it. The .whl extension and the structured filename are conventions pip and the packaging tooling rely on, but the bytes inside are an ordinary ZIP containing the package directory, a {name}-{version}.dist-info metadata folder, and a RECORD manifest of every installed file. You can inspect one with any ZIP viewer before installing.