Skip to content

Pick pure pypi lock wheels through one shared portability rule (#1150) - #1151

Merged
Mikola Lysenko (mikolalysenko) merged 2 commits into
mainfrom
arch-refactor/1150-shared-wheel-pick
Oct 8, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 2 commits into
mainfrom
arch-refactor/1150-shared-wheel-pick

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1150

Summary

Lock inventory decided a pypi package's "pure" wheel with its own -none-any.whl suffix check (twice: uv/PEP 751 and poetry). Since #1053, the shared classifier (pypi_distribution::wheel_platform_from_filename, #1048) calls cp311-, pp310- and py2-none-any wheels platform-locked, and vendored mode, the hosted redirect and (since #1121) ledger recovery follow it. Inventory didn't, so a lock whose only pinned wheel had one of those tags was resolved as fetchable. Now every lock reader goes through one rule and one pick.

Why (leverage)

Register row E89 (living document §6, doc/06-discovery-vex.md), the slice #1121 left open. B 1 (#1150), U 0, D 2 (two suffix checks; the inventory/recovery wheel pick written twice), S 0, R L. Every file is free of open arch-refactor/* and agent/fix-* PRs (#1058, an arch-fix/* PR, touches a different hunk of lock_inventory/pypi.rs).

What changed

  • lock_inventory/pypi.rs: new portable_wheel_artifact(package, keys): package_artifacts → is_portable_wheel_url → http_url → that artifact's sha256. The uv/PEP 751 inventory calls it with archive/wheels/wheel. Poetry's files / [metadata.files] names are classified through is_portable_wheel_url.
  • lock_inventory/recover.rs: pure_wheel_from_uv_unit keeps its unit parsing and calls the same pick with archive/wheels/wheel/files (keys unchanged).

Deleted

  • python_package_archive (inventory's copy of the pick) and recovery's inline copy of it.
  • Both ends_with("-none-any.whl") checks; no copy remains in production lock_inventory.

git diff --stat: production pypi.rs +25 / −14 (4 of the + lines declare the test module), recover.rs +5 / −18, so net −2 production lines; tests +101 (lock_inventory/pypi_wheel_tests.rs, a sibling module, because lock_inventory/tests.rs is changed by an open PR).

Behavior

uv / PEP 751 / poetry lock inventory no longer resolves or hash-pins a cp*-none-any, pp*-none-any or py2-none-any wheel (the entry stays discovery-only, integrity: None), which matches vendored, hosted and recovery. A wheel name the classifier can't parse (fewer than three tags) is no longer called pure. Nothing else changes: portable wheels (py3, py2.py3, pyXY) and ?query/#fragment stripping work as before, and recovery's keys and fail-closed behavior are unchanged.

Test evidence

  • New table tests in pypi_wheel_tests.rs run uv inventory, poetry inventory and ledger recovery over 8 wheel shapes (including the ones where the suffix rule and the classifier differed) and pin them to wheel_platform_from_filename, plus a query/fragment case.
    • On main (red): uv_inventory_and_ledger_recovery_pick_the_same_wheels: inventory, six-1.16.0-cp311-none-any.whl: left Some(".../six-1.16.0-cp311-none-any.whl") right None; poetry_inventory_pins_only_a_portable_wheel: left Sha256Hex(..) right None.
    • On the branch: green.
  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --lib: 5784 passed, 4 failed. Those 4 are the known root-only sandbox failures (copy_tree::relax_loop_must_not_traverse_symlinked_root, vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, pypi_poetry::wire_write_failure_maps_error_and_leaves_lock_untouched, pypi_requirements::wire_failure_rolls_back_already_written_files), and they fail on main too.
  • CLI: in_process_vendor_pypi_takeover 6/6, in_process_redirect_poetry 7/7, hosted_superseding_pypi 4/4, mode_migration_pypi 40/41. The one failure, pipenv_hosted_to_vendored_names_the_unpatched_requirements, fails identically on main in this sandbox because it needs a GET to pypi.org, which the sandbox doesn't allow.

Risk

Low. The change is confined to wheel selection in lock inventory and recovery, and the only behavior change is the documented convergence with the #1048 classifier.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LERMTm6HLt9J1WNEqVJUui


Note

Low Risk
Scope is wheel selection in lock inventory and recovery only; behavior aligns with the existing #1048 classifier already used in vendored and hosted paths.

Overview
PyPI lock inventory and ledger recovery no longer treat wheels as “pure” via a -none-any.whl filename suffix. They share portable_wheel_artifact, which uses is_portable_wheel_url (same rule as vendored/hosted mode).

python_lock_inventory (uv / PEP 751) and poetry files / metadata hashing now only resolve or pin wheels the classifier considers portable (e.g. py3-none-any, py2.py3-none-any). Interpreter-tagged cp*/pp*-none-any, py2-none-any, and unparseable tag triples stay discovery-only with no integrity pin. pure_wheel_from_uv_unit in recovery delegates to the same helper instead of duplicating the pick.

New pypi_wheel_tests.rs locks uv inventory, poetry inventory, recovery, and the classifier to the same wheel table, including URLs with ? / # suffixes.

Reviewed by Cursor Bugbot for commit 964f429. Configure here.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) added refactor Structural change: duplicated code or logic, missing abstraction, layering, dead code arch-refactor PR opened by the scheduled architecture refactor routine labels Oct 8, 2026
Lock inventory picked a pypi package's "pure" wheel with its own
`-none-any.whl` suffix check, so a uv.lock or poetry.lock whose only
pinned wheel is cp311-, pp310- or py2-none-any was resolved as
fetchable while vendored mode, the hosted redirect and ledger
recovery call the same wheel platform-locked (#1048).

The uv/PEP 751 inventory and ledger recovery now share one
`portable_wheel_artifact` pick, and the poetry reader classifies
its file names through `is_portable_wheel_url`. Both suffix checks
and recovery's copy of the pick are gone. A table test runs every
former caller over the wheel shapes where the rules differed.

Fixes #1150

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 8, 2026 17:16
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 964f429. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 8, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review at head 964f429f90.


Generated by Claude Code

@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit dac7189 Oct 8, 2026
455 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the arch-refactor/1150-shared-wheel-pick branch October 8, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-refactor PR opened by the scheduled architecture refactor routine Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review refactor Structural change: duplicated code or logic, missing abstraction, layering, dead code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lock inventory pins cp311/pp310/py2 "-none-any" wheels as pure, while vendored, hosted and recovery refuse them

3 participants