Skip to content

fix(downloads): accept wheels whose dist-info omits the local version…#1240

Closed
pmattsso wants to merge 1 commit into
python-wheel-build:mainfrom
pmattsso:fix/download-wheel-local-version-distinfo
Closed

fix(downloads): accept wheels whose dist-info omits the local version…#1240
pmattsso wants to merge 1 commit into
python-wheel-build:mainfrom
pmattsso:fix/download-wheel-local-version-distinfo

Conversation

@pmattsso

@pmattsso pmattsso commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

… segment

Some vendor-built wheels (e.g. AMD's tensorflow-rocm) name their dist-info directory using only the base version while the wheel filename includes a PEP 440 local segment. wheelfile.WheelFile rejects these with "Missing RECORD file" because it expects the directory name to match the full filename version. Add a fallback that catches WheelError and checks for RECORD and METADATA under the base-version dist-info directory when the filename carries a local version. The wheel is accepted with a warning if both files exist.

Closes: #1239

Pull Request Description

What

Add a fallback in download_wheel() for wheels whose dist-info directory uses the base version instead of the full version including the local segment. When wheelfile.WheelFile raises WheelError, the new _validate_wheel_with_local_version() helper checks whether RECORD and METADATA exist under {name}-{public_version}.dist-info/. The wheel is accepted with a warning if valid; the original error is re-raised otherwise. Four tests cover the new code path.

Why

What

Add a fallback in download_wheel() for wheels whose dist-info directory uses the base version instead of the full version including the local segment. When wheelfile.WheelFile raises WheelError, the new _validate_wheel_with_local_version() helper checks whether RECORD and METADATA exist under {name}-{public_version}.dist-info/. The wheel is accepted with a warning if valid; the original error is re-raised otherwise. Four tests cover the new code path.

Why

Some vendor-built wheels (e.g. AMD's tensorflow_rocm-2.19.1+rocm7.14.0a20260624) name their dist-info directory with only the base version (tensorflow_rocm-2.19.1.dist-info) while the filename carries a PEP 440 local segment. WheelFile rejects these with "Missing RECORD file" because it expects the directory name to match the full filename version. This blocks fromager bootstrap for any pre-built wheel that follows this pattern.

… segment

Some vendor-built wheels (e.g. AMD's tensorflow-rocm) name their
dist-info directory using only the base version while the wheel
filename includes a PEP 440 local segment. wheelfile.WheelFile
rejects these with "Missing RECORD file" because it expects the
directory name to match the full filename version.
Add a fallback that catches WheelError and checks for RECORD and
METADATA under the base-version dist-info directory when the
filename carries a local version. The wheel is accepted with a
warning if both files exist.

Closes: python-wheel-build#1239

Signed-off-by: pmattsso <pmattsso@redhat.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@pmattsso pmattsso requested a review from a team as a code owner July 8, 2026 13:19
@mergify mergify Bot added the ci label Jul 8, 2026
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds a fallback validation function in downloads.py to handle wheels whose filenames contain a local version segment (e.g., "+local") not reflected in the dist-info directory name. When the standard WheelFile-based validation raises WheelError, download_wheel now attempts this fallback, which checks the ZIP for RECORD and METADATA under the base-version dist-info path, re-raising the original error if the fallback also fails. Tests were added covering both the helper function and download_wheel's updated behavior.

Estimated code review effort: 2 (Simple) | ~15 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning [#1239] The fallback only checks RECORD and METADATA; it does not verify WHEEL as the issue requires for valid base-version dist-info. Extend the fallback to require WHEEL too, or keep WheelFile validation for that file before accepting the wheel.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes stay within wheel validation and matching tests, with no unrelated scope introduced.
Title check ✅ Passed The title clearly summarizes the wheel validation fix for local-version wheels.
Description check ✅ Passed The description matches the change and explains the fallback validation for vendor-built wheels.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_downloads.py`:
- Around line 224-250: Add a test in tests/test_downloads.py covering
_validate_wheel_with_local_version when the wheel has a local-version filename
but the base-version dist-info is missing METADATA; mirror the existing
test_validate_wheel_with_local_version_no_record setup, omit METADATA, and
assert the helper returns False so both required base-version files are
exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d38e6cc7-eecc-42f3-9b79-fa314d671b0a

📥 Commits

Reviewing files that changed from the base of the PR and between 2e7c9cd and 58ada5c.

📒 Files selected for processing (2)
  • src/fromager/downloads.py
  • tests/test_downloads.py

Comment thread tests/test_downloads.py
Comment on lines +224 to +250
def test_validate_wheel_with_local_version_valid(tmp_path: pathlib.Path) -> None:
"""Fallback validation passes for a well-formed wheel with base-only dist-info."""
filename = "pkg-1.0+local-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
zf.writestr("pkg-1.0.dist-info/RECORD", "")
assert _validate_wheel_with_local_version(f, filename) is True


def test_validate_wheel_with_local_version_no_local(tmp_path: pathlib.Path) -> None:
"""Fallback returns False when the filename has no local version."""
filename = "pkg-1.0-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
zf.writestr("pkg-1.0.dist-info/RECORD", "")
assert _validate_wheel_with_local_version(f, filename) is False


def test_validate_wheel_with_local_version_no_record(tmp_path: pathlib.Path) -> None:
"""Fallback returns False when RECORD is missing from base-version dist-info."""
filename = "pkg-1.0+local-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
assert _validate_wheel_with_local_version(f, filename) is False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing test for absent METADATA in base-version dist-info.

There's a test for missing RECORD but not for missing METADATA. The implementation checks for both files, so this edge case should be covered.

🧪 Proposed test addition
 def test_validate_wheel_with_local_version_no_record(tmp_path: pathlib.Path) -> None:
     """Fallback returns False when RECORD is missing from base-version dist-info."""
     filename = "pkg-1.0+local-py3-none-any.whl"
     f = tmp_path / filename
     with zipfile.ZipFile(f, "w") as zf:
         zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
     assert _validate_wheel_with_local_version(f, filename) is False
+
+
+def test_validate_wheel_with_local_version_no_metadata(tmp_path: pathlib.Path) -> None:
+    """Fallback returns False when METADATA is missing from base-version dist-info."""
+    filename = "pkg-1.0+local-py3-none-any.whl"
+    f = tmp_path / filename
+    with zipfile.ZipFile(f, "w") as zf:
+        zf.writestr("pkg-1.0.dist-info/RECORD", "")
+    assert _validate_wheel_with_local_version(f, filename) is False
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_validate_wheel_with_local_version_valid(tmp_path: pathlib.Path) -> None:
"""Fallback validation passes for a well-formed wheel with base-only dist-info."""
filename = "pkg-1.0+local-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
zf.writestr("pkg-1.0.dist-info/RECORD", "")
assert _validate_wheel_with_local_version(f, filename) is True
def test_validate_wheel_with_local_version_no_local(tmp_path: pathlib.Path) -> None:
"""Fallback returns False when the filename has no local version."""
filename = "pkg-1.0-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
zf.writestr("pkg-1.0.dist-info/RECORD", "")
assert _validate_wheel_with_local_version(f, filename) is False
def test_validate_wheel_with_local_version_no_record(tmp_path: pathlib.Path) -> None:
"""Fallback returns False when RECORD is missing from base-version dist-info."""
filename = "pkg-1.0+local-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
assert _validate_wheel_with_local_version(f, filename) is False
def test_validate_wheel_with_local_version_valid(tmp_path: pathlib.Path) -> None:
"""Fallback validation passes for a well-formed wheel with base-only dist-info."""
filename = "pkg-1.0+local-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
zf.writestr("pkg-1.0.dist-info/RECORD", "")
assert _validate_wheel_with_local_version(f, filename) is True
def test_validate_wheel_with_local_version_no_local(tmp_path: pathlib.Path) -> None:
"""Fallback returns False when the filename has no local version."""
filename = "pkg-1.0-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
zf.writestr("pkg-1.0.dist-info/RECORD", "")
assert _validate_wheel_with_local_version(f, filename) is False
def test_validate_wheel_with_local_version_no_record(tmp_path: pathlib.Path) -> None:
"""Fallback returns False when RECORD is missing from base-version dist-info."""
filename = "pkg-1.0+local-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/METADATA", "Metadata-Version: 1.0\n")
assert _validate_wheel_with_local_version(f, filename) is False
def test_validate_wheel_with_local_version_no_metadata(tmp_path: pathlib.Path) -> None:
"""Fallback returns False when METADATA is missing from base-version dist-info."""
filename = "pkg-1.0+local-py3-none-any.whl"
f = tmp_path / filename
with zipfile.ZipFile(f, "w") as zf:
zf.writestr("pkg-1.0.dist-info/RECORD", "")
assert _validate_wheel_with_local_version(f, filename) is False
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_downloads.py` around lines 224 - 250, Add a test in
tests/test_downloads.py covering _validate_wheel_with_local_version when the
wheel has a local-version filename but the base-version dist-info is missing
METADATA; mirror the existing test_validate_wheel_with_local_version_no_record
setup, omit METADATA, and assert the helper returns False so both required
base-version files are exercised.

Source: Path instructions

@tiran

tiran commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

These wheels are in violation of https://packaging.python.org/en/latest/specifications/binary-distribution-format/ and therefore invalid . The wheel file name, dist-info directory, and content of METADATA file must match.

@pmattsso

pmattsso commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @tiran I will close this PR.

@pmattsso pmattsso closed this Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: download_wheel rejects valid wheels whose dist-info directory omits the local version segment

2 participants