Skip to content

Fix uv project with no env falling back to system Python (#964) - #965

Open
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
mainfrom
agent/fix-uv-global-fallback
Open

Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
mainfrom
agent/fix-uv-global-fallback

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

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

Fixes #964

Summary

A vendored scan of a fresh uv checkout (uv.lock, no .venv synced yet), or of a directory holding only PEP 723 script locks, no longer pulls the system Python's packages into the candidate set. Before this, every OS-Python package with a Socket patch (PyYAML, requests, … on stock Ubuntu runners) made scan --mode vendored exit 1 with pypi_uv_lock_package_missing, even when nothing the project depends on was patched.

Root cause

PythonCrawler::get_site_packages_paths falls back to get_global_python_site_packages() whenever the cwd has a Python project marker but no project env was found. A uv project only ever installs into uv's own env (./.venv or UV_PROJECT_ENVIRONMENT), and a script lock's env lives in uv's cache. So with nothing synced yet, nothing is installed for the project, and its lock-only packages already come from the lock (lockfileOnlyPackages).

Same root cause as #947 / #504. PR #950 handles the Pipenv half with an is_pipenv_project guard at the same spot; this PR adds the uv half. The two touch adjacent lines, so whichever lands second needs a trivial merge.

Change

  • uv_owns_project_env(cwd) is true for a uv.lock that no other manager shares, or a directory whose only Python markers are *.py.lock script locks. In that case the crawl returns no env instead of falling back.
  • A uv.lock next to Poetry / PDM / Pipenv files keeps the old fallback, because Poetry with virtualenvs.create = false installs into the interpreter it runs on. The "another manager claims this project" check moved out of uv_project_environment_site_packages into claimed_by_non_uv_manager, so both share it.
  • Removed get_site_packages_paths_falls_back_via_uv_lock_marker, which pinned the buggy behaviour. CLI_CONTRACT's "cwd-only" section already describes the new behaviour.
  • The pypi_uv_lock_package_missing remedy text in vendor/pypi_uv.rs is unchanged. Without the fallback, a package the project doesn't depend on can't reach it any more.
  • Ported CI fix: 064b21d is Route Gradle digests through utils::digest #878's change (Gradle digests go through utils::digest). main is red on production_digests_go_through_the_helpers, and the change no-ops once Route Gradle digests through utils::digest #878 lands.
  • 1822d82 reverts a cargo fmt --all sweep that had reformatted 118 files this fix doesn't touch. Each reverted file is byte-identical to rustfmt's output on the merge-base version. The diff is now the 6 files listed in the PR.

Tests (red → green)

Issue cell Test Without fix With fix
#964 uv.lock alone / pyproject.toml + uv.lock / unsynced UV_PROJECT_ENVIRONMENT / tool.py + tool.py.lock crawler_python_e2e::get_site_packages_paths_uv_without_env_never_falls_back_to_global ❌ global path returned ✅
#964 scan in agent / vendored / hosted mode for the same 3 shapes never queries a system-only package in_process_python_envs::uv_fresh_checkout_never_scans_the_system_python ❌ system-decoy@6.6.6 reached the batch query ✅
Guard stays narrow: uv.lock + poetry.lock keeps the fallback crawler_python_e2e::get_site_packages_paths_uv_lock_beside_poetry_keeps_fallback ✅ ✅

Local runs

  • cargo clippy --workspace --all-features -- -D warnings ✅ (re-run on 1822d82)
  • crawler_python_e2e (62) and in_process_python_envs (20) ✅ (re-run on 1822d82)
  • cargo test -p socket-patch-core --all-features: all pass except 4 lib tests that inject failures with chmod (copy_tree::relax_loop_must_not_traverse_symlinked_root, vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, pypi_poetry::wire_write_failure_…, pypi_requirements::wire_failure_rolls_back_…). They can't fail as uid 0, which is how this container runs, and they pass in CI.
  • CLI: in_process_python_envs, e2e_pypi, hosted_superseding_pypi, mode_migration_pypi ✅. covgap_commands_vendor: 3 tests that inject state-write failures fail for the same root reason.
  • e2e_vendor_pypi_build -- --include-ignored (uv 0.11.32): 35/35 ✅
  • e2e_redirect_uv_build -- --ignored: 5 ✅. 4 fail because the test binary can't reach https://pypi.org/pypi/six/1.16.0/json through this sandbox's TLS proxy during the upstream restore step, which this diff doesn't touch. CI runs these with real network.
  • The full cargo test --workspace can't link within this session's disk allowance, so CI is the full-suite gate.

🤖 Generated with Claude Code

https://claude.ai/code/session_012H7zqyRTeMzzAxit6xfV6r


Note

Medium Risk
Changes which Python installs are considered in project-scoped crawls; misclassification could skip real envs or still leak globals, but the guard is narrow and heavily tested.

Overview
Fixes #964: project-scoped scans no longer treat the OS Python as part of a uv-managed tree when uv has not synced an env yet.

PythonCrawler::get_site_packages_paths used to fall back to global site-packages whenever the cwd looked like a Python project but had no local venv—so a fresh uv.lock checkout (or a PEP 723 *.py.lock script dir) could pull conda/system packages into vendored/agent scans and fail on unrelated patched wheels. The crawler now returns no env paths when uv_owns_project_env is true (uv.lock not shared with Poetry/PDM/Pipenv, or only *.py.lock markers), mirroring the existing Pipenv guard; lockfile-only deps still come from the lock.

claimed_by_non_uv_manager was extracted so UV_PROJECT_ENVIRONMENT handling and the new guard share the same “another manager owns this repo” logic; uv.lock + poetry.lock still keeps the global fallback for Poetry-on-system-interpreter setups.

Tests were flipped/added: e2e asserts empty site-packages (and no global anaconda decoy) for uv/script shapes; CLI asserts system-decoy never hits the batch API across agent/vendored/hosted modes.

Reviewed by Cursor Bugbot for commit c47aa0d. Configure here.

Assisted-by: Claude Code:claude-opus-5-5
A fresh uv checkout (uv.lock with no .venv synced yet, or a
UV_PROJECT_ENVIRONMENT that doesn't exist yet) and a directory
holding only PEP 723 script locks fell back to the global
site-packages. Every OS-Python package then joined the candidate
set, so a vendored scan tried to vendor packages the project never
depends on and exited 1 with pypi_uv_lock_package_missing.

uv only ever installs such a project into its own env, and the
lock already supplies the lock-only packages, so the crawl now
returns no env for it. A uv.lock shared with Poetry, PDM or Pipenv
files keeps the old fallback.

Fixes #964

Assisted-by: Claude Code:claude-opus-5-5
main's coverage job is red: the digest guard test from #865
requires production hashing to go through the utils::digest
helpers, and the Gradle code from #646 still hashes inline. This
is the same change as #878, ported so this PR's CI can go green;
it no-ops once #878 lands.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI notes on the earlier heads:


Generated by Claude Code

@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 6, 2026 22:05
@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.

Stale Bugbot comment from a previous run.

661c117 ran `cargo fmt --all`, which reformatted 118 files the uv
fix never changes. main isn't rustfmt-clean and CI doesn't check
formatting, so the sweep adds nothing. It also hides the real
change and conflicts with every other open PR that touches those
files. Each reverted file is byte-identical to rustfmt's output on
the merge-base version, so this drops formatting only. The six
files that carry the fix and the ported #878 change keep their
formatting.

Co-Authored-By: Claude <noreply@anthropic.com>
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


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.

Stale Bugbot comment from a previous run.

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

Copy link
Copy Markdown
Collaborator Author

[burn-down agent] Ready for review at 1822d82.

  • What changed in this round: 1822d82 reverts a cargo fmt --all sweep that had reformatted 118 files the fix never touches. main isn't rustfmt-clean and CI doesn't check formatting, so the sweep was pure churn, and it would have conflicted with most other open PRs. Each reverted file is byte-identical to rustfmt's output on the merge-base version, so only formatting was dropped. The diff went from 124 files (+2407/−855) to 6 files.
  • CI: 409/409 check runs green on 1822d82 (6 skipped); mergeable, not behind main.
  • Bugbot: reviewed 1822d82, no new issues. There are no review threads.
  • For the reviewer: the behaviour change is uv_owns_project_env / claimed_by_non_uv_manager in crawlers/python_crawler.rs. 064b21d is a port of Route Gradle digests through utils::digest #878 and becomes a no-op once that lands. Fix Pipenv project falling back to system Python (#504, #947) #950 (the Pipenv half of the same fallback) touches adjacent lines, so whichever merges second needs a small merge.

Generated by Claude Code

@mikolalysenko Mikola Lysenko (mikolalysenko) removed the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 7, 2026
Conflict in crates/socket-patch-core/src/crawlers/python_crawler.rs:
main (#950) added an is_pipenv_project guard and this branch added a
uv_owns_project_env guard at the same spot in get_site_packages_paths.
Kept both: the Pipenv guard first, then the uv guard, and merged the
doc comment to name both.

Co-Authored-By: Claude <noreply@anthropic.com>
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


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 c47aa0d. Configure here.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

3 participants