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.

Reading an auditwheel show report: the tag line and the external references The auditwheel show report has two parts that matter. The platform tag line must read a versioned manylinux tag such as manylinux_2_28_x86_64; a plain linux_x86_64 means unrepaired and PyPI rejects it. The external references section must list only permitted base libc libraries; any GDAL or PROJ entry there means that library was not bundled. tag line — must be versioned manylinux_2_28_x86_64 ✓ linux_x86_64 ✗ (unrepaired) PyPI rejects the plain tag external references — base only libc.so.6 · libm.so.6 ✓ libgdal.so.34 here ✗ GDAL listed = not bundled

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.