Verifying wheel tags with auditwheel show
This page answers one question: how do you read the output of auditwheel show to confirm a spatial wheel carries a versioned manylinux tag and leaks no host libraries — before PyPI rejects it or a user hits a missing-SONAME error? It sits inside the Testing and Validating Spatial Wheels cluster of the Modern Python Build Tooling & Wheel Configuration reference, and gives you a line-by-line reading of the report, the tag that PyPI requires, and the external-reference check that catches unbundled dependencies.
Context & Root Cause
auditwheel show is a static inspector: it reads a wheel’s extension module, resolves what it links, and reports two things that decide whether the wheel is portable. The first is the platform tag it computes from the glibc symbol versions the binary references — this is the tag auditwheel repair would stamp, and the value PyPI checks. A wheel whose extension still references only base-system symbols and whose external libraries are all bundled earns a versioned tag like manylinux_2_28_x86_64; one that never went through repair keeps the generic linux_x86_64, which PyPI refuses because it makes no portability promise.
The second is the external references list: every shared library the extension needs that is not inside the wheel. For a correctly repaired geospatial wheel this list contains only the permitted base libc set (libc, libm, libpthread, libdl). If libgdal.so.34 or libproj.so.25 appears there, the repair did not bundle it, and the wheel will raise cannot open shared object file on any host lacking that exact SONAME. Reading these two sections is the static gate that precedes the runtime smoke test in Testing and Validating Spatial Wheels.
Solution / Fix
This targets auditwheel 6.x and a repaired Linux wheel. On macOS the analogue is delocate-listdeps --all.
1. Run the report
auditwheel show dist/_geospatial_ext-1.0-cp39-abi3-manylinux_2_28_x86_64.whl
A healthy report reads roughly:
_geospatial_ext-1.0-cp39-abi3-manylinux_2_28_x86_64.whl is consistent with
the following platform tag: "manylinux_2_28_x86_64".
The wheel references external versioned symbols in these system-provided
shared libraries: libc.so.6 with versions {GLIBC_2.17, ...}
This constrains the platform tag to "manylinux_2_28_x86_64".
2. Read the tag line
The tag must be versioned (manylinux_2_28, manylinux2014, or musllinux_1_2). If it says linux_x86_64, the wheel is unrepaired — run auditwheel repair and re-check.
3. Read the external references
# Any geospatial library in the external list is a bundling failure
auditwheel show dist/*.whl | grep -iE 'libgdal|libproj|libgeos'
# expected: empty (they should be INSIDE the wheel, not external)
If a geospatial SONAME appears, re-run repair with LD_LIBRARY_PATH pointing at the build-time libraries, the fix in fixing “libgdal.so: cannot open shared object file”.
Verification
# 1. Tag is versioned manylinux/musllinux, never plain linux
auditwheel show dist/*.whl | grep -oE '(manylinux|musllinux)[_0-9a-z]*' | head -1
# expected: manylinux_2_28_x86_64 (a non-empty versioned tag)
# 2. No geospatial library is external
test -z "$(auditwheel show dist/*.whl | grep -iE 'libgdal|libproj|libgeos')" \
&& echo "all geospatial libs bundled"
# expected: all geospatial libs bundled
# 3. The tag in the report matches the filename tag
python - <<'PY'
import glob, re
f = glob.glob("dist/*.whl")[0]
print("filename tag:", re.search(r'-(manylinux[_0-9a-z]+|linux_\w+)\.whl', f).group(1))
PY
# expected: a manylinux tag matching the report, not linux_x86_64
A versioned tag, an empty geospatial-external check, and a filename that agrees with the report confirm the wheel is portable. A mismatch between filename and report tag means the file was renamed without a real repair.
Pitfalls & Alternatives
Trusting the filename over the report. A wheel can be renamed to claim manylinux_2_28 without being repaired; auditwheel show computes the tag from the binary, so it is the source of truth. Always read the report, never just the filename.
Ignoring the external-symbol constraint. auditwheel may report that a newer glibc symbol constrains the tag to, say, manylinux_2_34, higher than you intended. That means GDAL was built against too-new a glibc — rebuild in the older manylinux_2_28 base image to lower the floor.
Treating show as sufficient. A perfect tag report does not prove the wheel runs — data files and driver registration are invisible to static inspection. Always follow with smoke-testing GDAL wheels in a clean container.
Related
- Testing and Validating Spatial Wheels — the parent guide placing this static gate before the runtime tests.
- Smoke-testing GDAL wheels in a clean container — the runtime gate that catches what static inspection cannot.
- Manylinux and manyARM Docker base images — how the base image sets the glibc floor the tag reflects.