Skip to main content

wheelgap

CI PyPI License: MIT Python 3.11+

Find the Python dependencies that have no prebuilt wheel for your target platform, before your Docker build does.

Point it at your lockfile and the platforms you ship to (ARM servers, Alpine images, Windows on ARM, free-threaded Python 3.14t). It tells you which packages pip or uv will have to compile from source, and which can't be installed at all.

wheelgap demo

Install and run (under 30 seconds)

With uv:

uvx wheelgap

Or with pipx:

pipx run wheelgap

Or install it: pip install wheelgap.

Run either one in a project directory. It finds uv.lock, pylock.toml, poetry.lock or requirements.txt on its own.

Usage

# Will my Alpine ARM image compile anything?
wheelgap -p 3.13 -t alpine-arm64

# Is my stack ready for free-threaded Python 3.14?
wheelgap -p 3.14t -t all

# Several Pythons at once, Linux on both architectures
wheelgap uv.lock -p 3.12,3.13,3.14 -t linux-x86_64,linux-aarch64

# No lockfile? Check packages directly (latest version, or pin one)
wheelgap psycopg2 uvloop numpy==2.3.4 -t windows,win-arm64

# Machine-readable, for scripts and CI
wheelgap --json > wheelgap.json

# Why is it a gap? List each rejected wheel and the reason
wheelgap -t alpine-arm64 --explain

Each cell in the table is one of:

cell meaning
✓ a compatible wheel exists, so the install is fast
● source no wheel, only an sdist, so it compiles at install time (needs a compiler, headers, time)
✗ missing no wheel and no sdist, so the install fails
– not installed on this target (environment markers exclude it)

Packages that are fine everywhere are hidden; --all shows them.

--explain lists, for each gap, the wheels that were rejected and why, closest misses first:

armlib 1.4.0 on musl-aarch64: ● source
  armlib-1.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl  manylinux_2_17_aarch64 is a glibc wheel; musl (Alpine) needs musllinux

If you think wheelgap got a package wrong, that output (or --json --explain) is all a bug report needs, with no need to share your lockfile.

Exit codes: 0 no gaps, 1 gaps found, 2 bad input. --exit-zero always exits 0.

Targets

name aliases wheel tags it accepts
linux-x86_64 linux, linux-amd64 manylinux_2_*_x86_64 up to --glibc (default 2.36), legacy manylinux2014/2010/1
linux-aarch64 linux-arm64 manylinux_2_*_aarch64
musl-x86_64 alpine, musl musllinux_1_*_x86_64 up to --musl (default 1.2)
musl-aarch64 alpine-arm64 musllinux_1_*_aarch64
macos-arm64 macos, mac macosx_*_arm64 and universal2 up to --macos (default 14.0)
macos-x86_64 macos-intel macosx_*_x86_64 and universal2
win-amd64 windows, win win_amd64
win-arm64 windows-arm64 win_arm64

-t all checks all eight. Python versions take a trailing t for free-threaded builds (3.14t), which correctly rejects abi3 wheels. Without -p, wheelgap uses the Python it is running on.

In CI (GitHub Action)

- uses: actions/checkout@v7
- uses: patrickballard/wheelgap@v0.1.1
  with:
    args: uv.lock -p 3.13 -t linux-x86_64,linux-aarch64

The job fails when a dependency would build from source on a target you ship, for example when a dependency bump drops ARM wheels.

How it works

  • Lockfiles are read offline. uv.lock, pylock.toml and poetry.lock already record every file published for each pinned version. wheelgap parses the wheel filenames' tags with packaging and checks them against the exact tag set each target accepts. That is the same logic pip and uv use to pick a wheel.
  • requirements.txt and bare package names are looked up on the PyPI JSON API. Feed it a fully pinned file (from uv pip compile, pip-compile or uv export): wheelgap checks the lines you give it and doesn't resolve dependencies.
  • Environment markers are respected. A pywin32 ; sys_platform == 'win32' dependency won't show up as a Linux gap. For uv.lock the dependency graph is walked per target, so transitive platform-specific packages are handled too.
  • --online re-checks lockfile packages against PyPI. Use it when a project has uploaded new wheels for an existing version since you locked, or if your uv.lock was generated with environments restrictions.

Why this exists

Missing wheels show up late, in the most expensive place:

  • Your python:3.13-alpine image on an ARM runner takes 25 minutes because two transitive dependencies don't publish musllinux_aarch64 wheels, and QEMU is compiling them.
  • A build fails with error: Microsoft Visual C++ 14.0 or greater is required on a teammate's Snapdragon laptop.
  • You try free-threaded Python 3.14t and half your stack quietly falls back to source builds, because abi3 wheels don't apply there.

Sites like pyreadiness.org and free-threaded wheels track the top few hundred packages. wheelgap checks your packages, at your pinned versions, for your targets, in about a second, and it can run in CI.

Roadmap

  • Pyodide/WebAssembly, iOS and Android targets
  • Read targets from [tool.wheelgap] in pyproject.toml
  • Private indexes (--index-url, the simple API)
  • Infer the target from a Dockerfile (FROM python:3.13-alpine, --platform=linux/arm64)
  • "Closest version with a wheel" hints (e.g. upgrade foo 1.2 → 1.4 to get musllinux wheels)

Contributions welcome. Issues labelled good first issue are a good start, and CONTRIBUTING.md has the 3-command dev setup.

License

MIT

Metadata

Release files for wheelgap 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wheelgap 0.1.1
File Size Uploaded Provenance Actions
wheelgap-0.1.1.tar.gz 262.3 kB With provenance Details for wheelgap-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for wheelgap 0.1.1
File Interpreter ABI Platform Actions
wheelgap-0.1.1-py3-none-any.whl Python 3 none any Details for wheelgap-0.1.1-py3-none-any.whl

Total release size: 281.5 kB

Release files / wheelgap-0.1.1.tar.gz

Download URL wheelgap-0.1.1.tar.gz
Size 262.3 kB
Tags Source
SHA-256 checksum
How to use checksums
3a272f37df31427c5a2c04ecfc7e5eab0c803acecb043719179df202974e7a85
BLAKE2b-256 checksum
How to use checksums
1ae85aab2f90b9d1593022f74a58f19fbd5b6c022c6109f00504cfc11b71c6e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Release files / wheelgap-0.1.1-py3-none-any.whl

Download URL wheelgap-0.1.1-py3-none-any.whl
Size 19.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ccc8a40333ea1f0509faf29d94596458d84b0ae07d0d00bccef41134d5be74b
BLAKE2b-256 checksum
How to use checksums
375657cd9f4eb9cc32110ddc420083734a5feefc6534882ee9956bc1167edce1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Security for wheelgap 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for wheelgap-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page