Repository navigation
Fix stale specifier on uv script-lock revert (#869) - #1458
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Reverting a vendored PEP 723 script lock (vendor --revert, remove, rollback) wrote back the specifier recorded at vendor time. If the user had changed the package's requirement in the script while it was vendored, uv left the lock untouched, so the revert put the stale specifier back, reported success, and every later `uv run --locked --script` failed. The revert now re-derives the [manifest] specifier from the live script block, using the same rule the uv.lock lane uses for pyproject.toml. A spelling uv's output can't be predicted for (a multi-clause range) is kept as drift with both files untouched. Fixes #869 Assisted-by: Claude Code:claude-opus-5-5
End-to-end check with real uv: vendor a PEP 723 script, change the vendored package's requirement with `uv add --script`, revert, and require `uv lock --script --check` to accept the restored pair. Refs #869 Assisted-by: Claude Code:claude-opus-5-5
A script whose declarations are the same as right after vendoring now keeps the recorded lock without re-deriving anything, so an ambiguous declaration match can't turn an untouched script into drift. Warnings for an underivable spelling now name the script instead of pyproject.toml and uv.lock. Refs #869 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
1 similar comment
|
BugBot review Generated by Claude Code |
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
✅ 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 8865851. Configure here.
|
Ready for review at
Generated by Claude Code |
Final review briefWhat it does: Reverting a vendored package in a PEP 723 script lock ( Risk: low. When the script's declarations are unchanged, the recorded lock is returned byte-for-byte. Every new failure path falls back to the existing Look here:
Verified:
Changes I made: none. Open questions (non-blocking):
Auto-merge (squash) is armed, so approving sends it straight to the merge queue. Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #869
Summary
Reverting a vendored PEP 723 script lock (
vendor --revert,remove,rollback) now restores the[manifest]specifier the script declaresat revert time, not the one recorded when vendoring. Before, a user who
changed the vendored package's requirement while it was vendored
(
uv add --script s.py "six>=1.15") got the stale==1.16.0written backwith
status: success, and every lateruv run --locked --scriptfailed.Root cause
A path source has no specifier, so uv leaves
s.py.lockbyte-identical whenthe script's requirement changes.
revert_python_locks(
vendor/pypi_lock.rs) then hitrestore_document'slive == newpath andreturned the recorded original verbatim. #841 fixed the same shape for
project
uv.lockfiles (respell_originalinvendor/pypi_uv.rs), butscript locks go through this separate backend.
Fix
respell_script_lock: before the document restore, each[manifest]entry (requirements / constraints / build-constraints / overrides) for the
vendored package gets its specifier re-derived from the live PEP 723 block
through the same
respell_lock_specifierrule the uv.lock lane uses,so both lanes agree on spelling, markers and ambiguity.
vendor_lock_entry_drifted, both files kept, as the project lane does.lock is kept untouched (byte-exact), so an ambiguous declaration match
can't turn an untouched script into drift (found by
/code-review).respell_lock_specifiernow takes the metadata file's name, so itsmessages name the script instead of pyproject.toml / uv.lock.
docs/testing/uv-compatibility.mdnotes that script locks follow thesame rule.
No wrapper (npm/pypi/gem) changes: this is core-only behaviour.
Test evidence
Per-issue checklist:
vendor --revert/remove/rollbackwrite the stale==1.16.0back into<script>.py.lock, souv run --lockedfails (exit 0) #869:script_revert_respells_a_requirement_changed_while_vendored(
one>=0.9,one==1.*, bareone),script_revert_keeps_drift_for_an_underivable_new_requirement(multi-clause), plus e2e
uv_vendor_script_revert_after_declaration_editin
e2e_vendor_pypi_buildwith real uv (uv add --script, revert, thenuv lock --script --check).Red → green:
origin/main: both new revert tests fail (stale==1restored, no warning); pass with the fix.
{ name = "six", specifier = "==1.16.0" }in the restored lock; passeswith the fix.
respell_script_lockagainst locks real uv wrote forsix>=1.15,six==1.16.*and baresix. Its output was byte-identicalto uv's in all three, which caught two padding bugs.
Commands run locally:
cargo fmt --all -- --check: okcargo clippy --workspace --all-features -- -D warnings: okcargo test -p socket-patch-cli --all-features --test e2e_vendor_pypi_build: 47 passed, 7 ignoredcargo test --workspace --all-features --no-fail-fast: everything passes except13 tests in untouched modules that fail only in this sandbox. They inject
write failures with
chmod, which root ignores (covgap_commands_vendor,in_process_redirect, repair, copy_tree, vlt_heal, pypi_poetry,
pypi_requirements). One more,
e2e_bun_lockb, needs patches-api.socket.dev,which the sandbox can't reach. CI runs them unprivileged and online.
/code-review high findings not fixed
script dependency (
uv add --script s.py six==1.16.0) leaves an unrecordedpath element. This is the script twin of Vendored uv transitive package later added as a direct dependency (
uv add six==1.16.0):vendor --revert/remove/rollbackhalf-revert the pair, souv sync --lockedfails andvendor --checkclaims nothing references the wheel #1374. It is a differentscenario. It already fails closed (
vendor_lock_entry_drifted), so nowrong file is ever written. It is left as a follow-up.
script already warns
vendor_lockfile_missing, and an unreadable one failsthe revert at the up-front read.
with_specifierand re-parsing per entry: kept. The uv.lock laneedits text while this lane edits a
toml_editdocument, and the per-entryparse is negligible.
🤖 Generated with Claude Code
https://claude.ai/code/session_01XPNBoBdAu7JJXCUQipjXY8
Note
Medium Risk
Changes vendored revert behavior for PEP 723 script locks and shared lock-specifier logic; incorrect respelling could break
uv lock --script --check, but drift paths fail closed and tests cover the scenario.Overview
Fixes #869: when you change a vendored package's requirement in a PEP 723 script (
uv add --script s.py "six>=1.15") while it stays vendored, uv leavess.py.lockunchanged because path sources record no specifier.vendor --revertused to write the old recorded specifier back with success, breakinguv run --locked --script.The fix mirrors #840 for project
uv.lock: before restoring a script lock,respell_script_lockre-derives each[manifest]entry (requirements, constraints, build-constraints, overrides) for the unwound package from the live# /// scriptblock via sharedrespell_lock_specifier. Unchanged declarations since vendoring still revert byte-for-byte; multi-clause ranges that uv's lock spelling can't be derived from are treated asvendor_lock_entry_drifted(files kept).respell_lock_specifiernow takes a metadatarelso errors name the script file, not onlypyproject.toml.Adds unit tests in
pypi_lock.rs, e2euv_vendor_script_revert_after_declaration_edit, and a note indocs/testing/uv-compatibility.md.Reviewed by Cursor Bugbot for commit 8865851. Configure here.
Generated by Claude Code