Avoid sharing state between universal and non-universal resolves (#11051)

## Summary

This is a really subtle issue. I'm actually having trouble writing a
test for it, though the problem makes sense. In short, we're sharing the
`SharedState` between the `BuildContext` and the universal resolver. The
`SharedState` includes `VersionMap`, which tracks incompatibilities...
The incompatibilities use the platform tags, which are only present when
resolving from the `BuildContext` (i.e., when resolving build
dependencies). The universal resolver then fails because it sees a bunch
of "incompatible" wheels that are incompatible with the current platform
(i.e., the current Python interpreter).

In short, we _cannot_ share a `SharedState` across two operations that
perform a universal and then a platform-specific resolution. So this PR
adds separate types and fixes up any overlapping usages.

A better setup, for the future, would be to somehow share the underlying
simple metadata, and only track separate `VersionMap` -- since there
_is_ a bunch of data we can share. But that's a larger change.

Closes https://github.com/astral-sh/uv/issues/10977.
This commit is contained in:
Charlie Marsh 2025-01-28 22:27:28 -05:00 committed by GitHub
parent 3878c00dbd
commit bc3eac2bc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 411 additions and 77 deletions

View file

@ -511,41 +511,44 @@ impl BuildContext for BuildDispatch<'_> {
pub struct SharedState {
/// The resolved Git references.
git: GitResolver,
/// The discovered capabilities for each registry index.
capabilities: IndexCapabilities,
/// The fetched package versions and metadata.
index: InMemoryIndex,
/// The downloaded distributions.
in_flight: InFlight,
/// The discovered capabilities for each registry index.
capabilities: IndexCapabilities,
}
impl SharedState {
pub fn new(
git: GitResolver,
index: InMemoryIndex,
in_flight: InFlight,
capabilities: IndexCapabilities,
) -> Self {
/// Fork the [`SharedState`], creating a new in-memory index and in-flight cache.
///
/// State that is universally applicable (like the Git resolver and index capabilities)
/// are retained.
#[must_use]
pub fn fork(&self) -> Self {
Self {
git,
index,
in_flight,
capabilities,
git: self.git.clone(),
capabilities: self.capabilities.clone(),
..Default::default()
}
}
/// Return the [`GitResolver`] used by the [`SharedState`].
pub fn git(&self) -> &GitResolver {
&self.git
}
/// Return the [`InMemoryIndex`] used by the [`SharedState`].
pub fn index(&self) -> &InMemoryIndex {
&self.index
}
/// Return the [`InFlight`] used by the [`SharedState`].
pub fn in_flight(&self) -> &InFlight {
&self.in_flight
}
/// Return the [`IndexCapabilities`] used by the [`SharedState`].
pub fn capabilities(&self) -> &IndexCapabilities {
&self.capabilities
}