Skip to content

Add locked subgraph materialization primitives#19907

Merged
zsol merged 4 commits into
mainfrom
zsol/materialize-locked-subgraphs
Jun 23, 2026
Merged

Add locked subgraph materialization primitives#19907
zsol merged 4 commits into
mainfrom
zsol/materialize-locked-subgraphs

Conversation

@zsol

@zsol zsol commented Jun 18, 2026

Copy link
Copy Markdown
Member

uv check and uv format need to install the exact ty or ruff package already selected in uv.lock, including its source and transitive dependencies, without invoking the resolver again or modifying the project environment. The immediate follow-up is #19884.

This adds Lock::to_resolution, which materializes a Resolution from explicit locked Package identities. It preserves marker-selected universal forks, requested extras and dependency groups, install filtering, and shared dependencies across multiple roots, while excluding unrelated lock-manifest requirements.

CachedEnvironment::from_resolution ensures an environment for an existing Resolution and installs it without resolving again. from_spec now delegates to this path after resolution. Callers must supply the interpreter from which the resolution’s markers and tags were derived.

Concrete roots establish a new conflict context: conflict items inside the resulting subgraph are evaluated from the selected roots, extras, and groups. Materialization fails instead when a reachable dependency still depends on a conflict item outside that subgraph.

This change is a pure refactor, there should be no behavior change.

@zsol
zsol force-pushed the zsol/materialize-locked-subgraphs branch from a34a2f8 to 11dce4a Compare June 18, 2026 18:37
@astral-sh-bot

astral-sh-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

uv test inventory changes

This PR changes the tests when compared with the latest main baseline.

  • Added tests: 6
  • Removed tests: 0
  • Changed suites: 1
uv-resolver: +6 / -0

Added:

  • uv-resolver::lock::installable::tests::installable_to_resolution_preserves_node_overrides
  • uv-resolver::lock::installable::tests::materializes_conflicting_extras_within_the_synthetic_root
  • uv-resolver::lock::installable::tests::materializes_multiple_concrete_roots_with_shared_dependencies
  • uv-resolver::lock::installable::tests::materializes_the_selected_universal_lock_fork
  • uv-resolver::lock::installable::tests::rejects_conflicts_outside_the_synthetic_root
  • uv-resolver::universal_marker::tests::has_conflict_marker

Removed: none

@@ -0,0 +1,13 @@
---

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we have some external snapshot files, but preferably they're only for very large snapshots. Everything else should be inlined.

@zanieb

zanieb commented Jun 18, 2026

Copy link
Copy Markdown
Member

I think Charlie is probably the best reviewer for this when it's ready. I can try, but it's a struggle.

@zsol

zsol commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Hey, it was a struggle for me too!

/// Get or create a [`CachedEnvironment`] from an existing [`Resolution`].
///
/// This path performs environment reuse or creation and installation without invoking the
/// resolver. `interpreter` must be the interpreter used to produce the resolution.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not take a universal resolution (i.e. lock) and an interpreter and convert to the resolution here (you could even just call the method on Lock still) so a caller can't mess up this invariant?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(for a second I thought the pull request was tiny then realized installable.rs was collapsed, I could learn why it's this way after reading that file)

@zsol
zsol force-pushed the zsol/materialize-locked-subgraphs branch from 11dce4a to ffc092e Compare June 19, 2026 10:20
@zsol
zsol requested a review from charliermarsh June 19, 2026 10:21
@zsol
zsol marked this pull request as ready for review June 19, 2026 10:42
@zsol
zsol force-pushed the zsol/materialize-locked-subgraphs branch from ffc092e to d50d1ac Compare June 19, 2026 15:51
Comment thread crates/uv-resolver/src/lock/installable.rs Outdated
Comment thread crates/uv-resolver/src/lock/installable.rs Outdated
Comment thread crates/uv/src/commands/project/install_target.rs Outdated

@charliermarsh charliermarsh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we determined at some point in the past that it wasn't feasible to allow installation from an arbitrary root -- it had to be a workspace member. I can't find a comprehensive discussion though, just:

I think the concern was that around conflicting extras. We need to know which extras were activated that led to including a given package in order to evaluate its subgraph. But maybe that's not relevant here.

Concrete roots do not retain the workspace path that selected them, so a dependency marker can otherwise silently use conflict items outside the selected subgraph.

Resolve conflict items within the subgraph and reject dependency markers that still depend on packages outside it. Evaluate PEP 508 branches for the target environment before checking ambiguity, and use the standard Installable path for project targets with conflicts.
@zsol zsol added the internal A refactor or improvement that is not user-facing label Jun 22, 2026
@zsol
zsol merged commit cd7f87f into main Jun 23, 2026
113 checks passed
@zsol
zsol deleted the zsol/materialize-locked-subgraphs branch June 23, 2026 09:39
zsol added a commit that referenced this pull request Jun 24, 2026
`uv check` currently resolves its standalone `ty` executable from uv’s
built-in compatible range, even when the project’s locked development
dependencies select a different version or source.

When `uv.lock` contains an applicable `ty` package, `uv check` now runs
that exact locked package. It first looks in the project’s `dev` group,
then falls back to a direct runtime dependency. With `--no-sync` (or
when `ty` is excluded by the selected dependency groups), uv
materializes only the locked `ty` subgraph into a cached environment
without modifying the project environment.

The precedence order for ty version selection is now:

- the `TY` executable override
- an explicit `--ty-version`
- the applicable `ty` package from `uv.lock` (if any)
- uv’s default constrained range when the lock contains no applicable
`ty` development dependency

This work was split into several focused pull requests, which are now
dependencies of this PR:

-
[[#19907](https://github.com/astral-sh/uv/pull/19907)](https://github.com/astral-sh/uv/pull/19907)
adds the locked-subgraph materialization and cached-installation
primitives this PR uses. This PR is stacked on it.
-
[[#19909](https://github.com/astral-sh/uv/pull/19909)](https://github.com/astral-sh/uv/pull/19909)
makes `uv check --no-sync` perform the normal lock operation while
skipping environment synchronization.
-
[[#19892](https://github.com/astral-sh/uv/pull/19892)](https://github.com/astral-sh/uv/pull/19892)
adds the hidden `uv check --show-version` flag used to verify version
selection.
-
[[#19913](https://github.com/astral-sh/uv/pull/19913)](https://github.com/astral-sh/uv/pull/19913)
moves related `uv check` tests from live PyPI dependencies to
deterministic Packse fixtures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal A refactor or improvement that is not user-facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants