Repository navigation
Fix uv script no-sources in a parent config (#1501) - #1504
Merged
Mikola Lysenko (mikolalysenko) merged 6 commits intoOct 13, 2026
Merged
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A PEP 723 script takes uv's settings from the nearest directory at or above it holding a uv.toml or a pyproject.toml with [tool.uv]. When a script was scanned from a subdirectory (scripts/ under a project whose root sets no-sources = true), both modes read only the scanned root, wired the script and exited 0. uv then ignored the source: `uv run --locked` failed and a plain `uv run` imported the unpatched package. The script lane now walks above the root when the root sets nothing, the way uv does, and refuses with the parent file named (vendored pypi_uv_no_sources, hosted redirect_uv_script_unsupported). The project lane is unchanged, since a project stops uv's lookup at itself. Fixes #1501 Assisted-by: Claude Code:claude-opus-5-5
The parent lookup now skips the walk when the scanned root's own uv.toml or [tool.uv] already governs the script, opens each file through the FIFO-safe regular-file reader, and stops at a settings file that exists but cannot be read, the way uv does, instead of going on to a farther one. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 13, 2026 21:11
Collaborator
Author
|
BugBot review Generated by Claude Code |
uv warns about a pyproject.toml it cannot parse during settings discovery and goes on to the parent directory. The script lookup treated such a file as governing, so a broken pyproject beside the script or in between hid a parent uv.toml that sets no-sources, and the script was wired anyway. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
In a uv workspace, uv takes a member's script settings from the workspace root, so a member's own [tool.uv] does not shield the script from the root's no-sources. The #1138 workspace-member guard already refuses that run in both modes before the script lookup; this pins it. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review 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 11a9e45. Configure here.
Tanmay Singla (Tanmay182003)
approved these changes
Oct 13, 2026
Mikola Lysenko (mikolalysenko)
deleted the
agent/fix-uv-script-ancestor-config
branch
October 14, 2026 00:00
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.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1501
Summary
A PEP 723 script scanned from a subdirectory (say
scripts/under a uv project whose rootpyproject.tomlsets[tool.uv] no-sources = true) was wired in both modes with exit 0. uv applies the parent's settings to the script, so it ignored the wired source:uv run --locked --scriptfailed, and a plainuv runimported the unpatched package. Both modes now refuse it before writing anything, the same way they do when the setting sits beside the script. The refusal names the parent file:pypi_uv_no_sources, e.g.job.py: ../uv.toml sets no-sources = true, ...redirect_uv_script_unsupported(skipped with a warning, as with the other uv script shapes hosted can't wire)Root cause
#1434 (the #564 fix) read uv's settings only beside the scanned root. For a script, uv uses the nearest directory at or above it that holds a
uv.toml, or apyproject.tomlwith a[tool.uv]table, checkinguv.tomlfirst in each directory. A rootpyproject.tomlwithout[tool.uv]doesn't stop that lookup.Changes
utils::python_script:find_ancestor_uv_configwalks above the root the way uv does. It uses the FIFO-safe regular-file reader and stops at a settings file that exists but can't be read.root_governs_scriptdecides whether the walk is needed at all.refuse_uv_script_no_sourcesapplies the ancestor's settings only when the root sets none. The shared check now carries the file's label, so messages name../uv.tomlor../pyproject.toml [tool.uv].vendor/pypi_lock.rs): the script lane uses the walk.disk_root_reading) and hands the file to the planner under a new synthetic key,UV_ANCESTOR_CONFIG_KEY. The in-memory engine has no "above", so it is unchanged.uv.lock) is unchanged. The issue's matrix shows uv 0.5.31, 0.8.17 and 0.13.0 all ignore a parent config for a project in a subdirectory.docs/testing/uv-compatibility.md: the Hosted and vendored uv wiring ignoreno-sources = true: scan and vendor report success,uv sync --lockedthen fails, and a plainuv syncreinstalls the unpatched wheel #564 bullet now describes the parent lookup.Tests (red → green)
mode_migration_pypi::uv_script_no_sources_in_a_parent_is_refused_in_both_modescovers a parentuv.toml, a parentpyproject [tool.uv] no-sources, a parentno-sources-package, and the control (a parent pyproject without[tool.uv]is still wired). Red on main with the core fix stashed:hosted uv.tomlpinned the script, panicking atmode_migration_pypi.rs:900. Green with the fix, and the whole suite passes (62/62).python_script::ancestor_config_tests(lookup order,uv.tomlbefore pyproject, skipping a pyproject without[tool.uv],..in the root, stopping at an unreadable file, the root's settings and the script's own setting winning),pypi_lock::script_no_sources_in_a_parent_refuses_before_writing,redirect::no_sources_above_the_root_refuses_a_script. The existing Hosted and vendored uv wiring ignoreno-sources = true: scan and vendor report success,uv sync --lockedthen fails, and a plainuv syncreinstalls the unpatched wheel #564 tests still pass.script_no_sources_in_a_parent_refuses_before_writing, e2e vendored halfno_sources_above_the_root_refuses_a_script, e2e hosted halfLocal:
cargo fmt --all -- --checkandcargo clippy --workspace --all-features -- -D warningsare clean.cargo test --workspace --all-features --no-fail-fast: 246 test binaries pass. 13 tests fail only because this sandbox runs as root: they inject write failures withchmod 0o555/0o600(covgap_commands_vendor, repair, vlt heal, poetry/requirements write-failure tests,copy_treesymlinked root, onee2e_bun_lockbrollback). None touch the code in this PR, and CI is the judge. The npm/pypi/gem wrappers only dispatch, so they need no change./code-review high
Fixed:
uv.tomlvs pyproject kind now comes from the file name.Not changed:
uv runwill be invoked. The issue's repro and matrix (cd scripts, andscan scriptsfrom the root) match the script-directory model.🤖 Generated with Claude Code
https://claude.ai/code/session_01CP8zKCXPGdH2FsXSCPx4E4
Note
Medium Risk
Changes PEP 723 script wiring/refusal in vendored and hosted paths; behavior is narrower (more refusals) but touches redirect and lock loading logic.
Overview
Fixes #1501: PEP 723 scripts scanned from a subdirectory (e.g.
scripts/) were still wired when a parentuv.tomlorpyproject.toml[tool.uv]setno-sources/no-sources-package, even though uv applies those settings to the script and would ignore wired sources.Vendored and hosted now walk ancestor config the way uv does when the scan root has no governing
[tool.uv]/uv.toml(root_governs_script). Refusal happens before any writes, naming the parent file (e.g.../uv.toml sets no-sources = true): vendoredpypi_uv_no_sources, hostedredirect_uv_script_unsupported.Hosted disk scans attach the discovered parent settings via synthetic
UV_ANCESTOR_CONFIG_KEY; the redirect planner usesrefuse_uv_script_no_sources. Theuv.lockproject lane is unchanged. Docs and e2e/unit tests cover parent configs, workspace-member guard, and lookup order.Reviewed by Cursor Bugbot for commit 11a9e45. Configure here.
Generated by Claude Code