fix(modern-python): python shim's suggested uv run command fails outside a project#195
Draft
thescribedevelopment wants to merge 1 commit into
Conversation
… work outside a project Outside a uv project there is no venv bin/ on PATH to shadow the shims, so `uv run python script.py` resolves python via PATH lookup and hits the shim again: the exact command the shim suggests can never succeed. The python shim now detects the `uv run` context via the UV environment variable (documented; set by uv >= 0.6.0 for all subprocesses it spawns) and execs the real interpreter, found by walking PATH and skipping the shim itself via a same-file (-ef) test. A PID-based guard (exec keeps the PID) turns the two-shim-copies-on-PATH edge into a clean exit 127 instead of an exec loop, and `python -m pip` stays blocked everywhere. Bare invocations outside `uv run` behave exactly as before. Docs updated (README + setup-shims.sh claimed uv run was unaffected by construction, which only holds inside a project). Existing bats tests made hermetic against ambient UV; six new tests cover the passthrough, symlink/aliased-spelling skip, loop guard, distinct not-found error, and pip precedence. Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
The
python/python3PATH shim suggestsuv run python ...— but outside a uv project that command can never succeed.uv runonly shadows the shims with a managed venvbin/when a project environment exists; outside a project,uv run pythonresolves the interpreter via ordinary PATH lookup, hits the shim again, and errors with the same suggestion:The claim in README.md /
setup-shims.sh("uv runis unaffected because it prepends its managed virtualenv'sbin/to PATH") only holds inside a project. For a coding agent this is a hard dead end: the shim's own advice loops.Fix
The python shim now detects the
uv runcontext and passes through to the real interpreter:UV=<path-to-uv>in the environment of every subprocess it spawns. This is documented, public API (env-var reference; added in astral-sh/uv#11326, listed in the 0.6.0 changelog as uv-owned — uv overwrites user-set values). On uv < 0.6.0 the shim degrades fail-safe to today's blocking behavior.-eftest (robust to symlinked/aliased spellings of the shims dir — a naivepwdcomparison can be tricked into an infinite exec loop, which we hit while testing), andexecthe first real interpreter found. Empty PATH entries are normalized to.per execvp semantics.execpreserves the PID, so the shim exports its PID before exec'ing; a shim that finds its own PID already recorded knows a second shim copy on PATH exec'd it (e.g. two plugin cache versions) and exits 127 with a clear error instead of ping-ponging forever.UVis set but no real interpreter exists on PATH, the shim emits a distinct error (exit 127, mirroring the uv shim) instead of re-suggesting the command that just failed.python -m pipstays blocked everywhere, including underuv run(the block is hoisted above the passthrough).Bare
python/python3invocations outsideuv runbehave exactly as before.Design tradeoffs
UV=1 python3 evil.pybypasses the shim. Accepted: the shims are behavioral nudges for an agent, not a security boundary (absolute-path invocation always bypassed them), and the passthrough is silent, so no error text teaches the bypass.uv run script.pyalone is insufficient: that form works today (uv provisions the interpreter itself), butpython -c ..., bare REPL, and-m moduleinvocations have no script-form equivalent.-m pipblock matches the canonical spelling only (-mpip/ options-before--mreach the interpreter). Same argv-level matching the shims already use; once a real interpreter can be exec'd, argv matching is inherently best-effort.UVis inherited by descendants of any uv-launched process (including uvx /uv tool run), so the nudge is disabled in those subtrees — passthrough there is correct/harmless.uvshim's PATH walk has the same latent aliased-spelling loop issue; happy to port the-effix in a follow-up if wanted.Tests
python-shim.bats: existing 8 tests kept but made hermetic against ambientUV(previously, running the suite viauv run batsfailed 5 tests and hung on the-m http.servercase once the passthrough existed); 6 new tests cover passthrough with arg/exit-code forwarding (via bothpythonand thepython3symlink), aliased-spelling skip, the two-copies loop guard (withtimeoutas a hang backstop), the distinct not-found error, and-m pipprecedence underUV.Verified locally: all 14 pass with bats 1.13 in a clean env and with
UVleaked into the ambient env; full shims suite (37 tests) green;shellcheck -x(0.10.0) clean;shfmt -i 2 -ci(v3.12.0, the pre-commit pin) produces no diff; shim behavior exercised end-to-end on Ubuntu with uv 0.11.18 (bare → blocked;uv run python3 script.py/uv run python -c→ run;uv pip→ still blocked). Constructs also validated against bash 3.2.57 for macOS default-shell compatibility.Version bumped 1.5.2 → 1.5.3 in
plugin.jsonand the rootmarketplace.jsonper AGENTS.md.🤖 Generated with Claude Code