Skip to content

Hosted rollback, remove and vendored takeover always refuse on a uv pip compile pylock.toml because its packages carry no index key #407

Description

[agent] Found by the scheduled uv bug-hunt routine (ledger #310).

Summary

A standalone PEP 751 lock made by uv pip compile --format pylock.toml is a supported hosted lane (docs/testing/uv-compatibility.md, "standalone PEP 751 compilation"). scan --mode hosted wires it, and uv pip sync pylock.toml installs the patch. But on main the hosted pin can't be unwound by anything:

  • rollback → partial_failure: cannot restore pkg:pypi/six@1.16.0 to its upstream registry entry: pylock.toml: no sibling registry package shows the registry and artifact fields this uv release records; restore it from version control instead (git checkout -- pylock.toml)
  • remove pkg:pypi/six@1.16.0 → hosted_revert_failed (same message)
  • scan --mode vendored (hosted → vendored takeover) → failed redirect_revert_failed

The cause: uv pip compile never writes an index key on [[packages]] (only uv export --format pylock.toml does). The v5 upstream restore takes a pylock entry's registry only from a sibling's index (crates/socket-patch-core/src/patch/redirect/upstream/uv.rs:314-321). With no sibling carrying one, registries is empty and the restore refuses, however many plain PyPI siblings the lock has. Every sibling here is a files.pythonhosted.org artifact with the full url / upload-time / size / hashes shape.

Impact

Any project that uses uv pip compile --format pylock.toml and adopts hosted mode can't roll back, remove or switch to vendored mode afterwards. The only remedy is the git checkout the error suggests, which also throws away any unrelated lock changes made since. A rollback meant to unpatch everything leaves the patch wired and installable.

Repro (Linux)

Mock patch API as in #379 / #381 (now also returning integrity.sha512), --patch-server-url for the mock origin, PyPI JSON API reachable.

SP="socket-patch --api-url http://127.0.0.1:18080 --api-token t --org test-org --patch-server-url http://127.0.0.1:18080"
mkdir p && cd p
printf 'six==1.16.0\nidna==3.7\ncertifi==2024.2.2\n' > req.in
uv pip compile --format pylock.toml req.in -o pylock.toml
grep -c '^index' pylock.toml                 # 0: uv pip compile records no index
$SP scan --mode hosted --json --yes          # redirected 1, rewrittenFiles [pylock.toml]
uv venv v && VIRTUAL_ENV=v uv pip sync pylock.toml   # installs the patched six.py
$SP rollback --json --yes                    # exit 1, partial_failure, hosted.failed[0] = "...no sibling registry package shows the registry..."
$SP remove pkg:pypi/six@1.16.0 --json --yes  # exit 1, hosted_revert_failed
$SP scan --mode vendored --json --yes        # exit 1, failed redirect_revert_failed
grep -c 127.0.0.1:18080 pylock.toml          # 1: still wired

Control: the same packages exported with uv lock && uv export --format pylock.toml (which writes index = "https://pypi.org/simple") roll back successfully.

Expected vs actual

  • Expected: CLI_CONTRACT.md, "Hosted unwind coverage", pypi: rollback / remove restore PEP 751 pylock*.toml pins, with hashes re-derived from PyPI's JSON API. The listed refusals for pylock are only "a release with a wheel that is not pure Python 3". The "other registry packages name no registry" refusal is stated for uv locks, where every registry package records its source. For a uv pip compile pylock the missing index is normal, so the restore should follow the siblings' shape (no index key) when their artifacts are PyPI files. At minimum, the refusal should be documented, and hosted mode should warn before it wires a lock it can never unwind.
  • Actual: rollback, remove and the vendored takeover all refuse on every uv pip compile pylock, and the hosted pin stays.

OS × uv matrix (main 2463257)

OS uv 0.8.17 uv 0.12.21
Linux fail (rollback / remove / takeover) fail (rollback / remove / takeover; reproduced 2×)

macOS and Windows weren't probed. The refusal is in platform-independent TOML inspection.

First bad

2463257 (#277, the v5 upstream restore; v4's ledger-based revert didn't depend on sibling entries).

Suspect code

  • crates/socket-patch-core/src/patch/redirect/upstream/uv.rs:314-321: for pep751, the registry comes only from package.get("index"), and siblings without it are skipped.
  • crates/socket-patch-core/src/patch/redirect/upstream/uv.rs:345-351: the "no sibling registry package" refusal.

Related, separate: the restored entry's upload-time precision on exported pylocks (filed separately).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions