mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-02 06:51:14 +00:00
uv-resolver: document some of our intermediate data structures
There are some key invariants that I had to re-learn by reading the code. This hopefully makes those invariants easier to discover by future me (and others).
This commit is contained in:
parent
147aec4d37
commit
07db2b167f
1 changed files with 74 additions and 2 deletions
|
@ -1854,17 +1854,30 @@ enum Response {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An enum used by [`DependencyProvider`] that holds information about package dependencies.
|
/// Information about the dependencies for a particular package.
|
||||||
/// For each [Package] there is a set of versions allowed as a dependency.
|
///
|
||||||
|
/// This effectively distills the dependency metadata of a package down into
|
||||||
|
/// its pubgrub specific constituent parts: each dependency package has a range
|
||||||
|
/// of possible versions.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum Dependencies {
|
enum Dependencies {
|
||||||
/// Package dependencies are not available.
|
/// Package dependencies are not available.
|
||||||
Unavailable(UnavailableVersion),
|
Unavailable(UnavailableVersion),
|
||||||
/// Container for all available package versions.
|
/// Container for all available package versions.
|
||||||
|
///
|
||||||
|
/// Note that in universal mode, it is possible and allowed for multiple
|
||||||
|
/// `PubGrubPackage` values in this list to have the same package name.
|
||||||
|
/// These conflicts are resolved via `Dependencies::fork`.
|
||||||
Available(Vec<(PubGrubPackage, Range<Version>)>),
|
Available(Vec<(PubGrubPackage, Range<Version>)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dependencies {
|
impl Dependencies {
|
||||||
|
/// Turn this flat list of dependencies into a potential set of forked
|
||||||
|
/// groups of dependencies.
|
||||||
|
///
|
||||||
|
/// A fork *only* occurs when there are multiple dependencies with the same
|
||||||
|
/// name *and* those dependency specifications have corresponding marker
|
||||||
|
/// expressions that are completely disjoint with one another.
|
||||||
fn fork(self) -> ForkedDependencies {
|
fn fork(self) -> ForkedDependencies {
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
|
@ -1991,11 +2004,19 @@ impl Dependencies {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about the (possibly forked) dependencies for a particular
|
||||||
|
/// package.
|
||||||
|
///
|
||||||
|
/// This is like `Dependencies` but with an extra variant that only occurs when
|
||||||
|
/// a `Dependencies` list has multiple dependency specifications with the same
|
||||||
|
/// name and non-overlapping marker expressions (i.e., a fork occurs).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ForkedDependencies {
|
enum ForkedDependencies {
|
||||||
/// Package dependencies are not available.
|
/// Package dependencies are not available.
|
||||||
Unavailable(UnavailableVersion),
|
Unavailable(UnavailableVersion),
|
||||||
/// No forking occurred.
|
/// No forking occurred.
|
||||||
|
///
|
||||||
|
/// This is the same as `Dependencies::Available`.
|
||||||
Unforked(Vec<(PubGrubPackage, Range<Version>)>),
|
Unforked(Vec<(PubGrubPackage, Range<Version>)>),
|
||||||
/// Forked containers for all available package versions.
|
/// Forked containers for all available package versions.
|
||||||
///
|
///
|
||||||
|
@ -2005,14 +2026,48 @@ enum ForkedDependencies {
|
||||||
Forked(Vec<Fork>),
|
Forked(Vec<Fork>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single fork in a list of dependencies.
|
||||||
|
///
|
||||||
|
/// A fork corresponds to the full list of dependencies for a package,
|
||||||
|
/// but with any conflicting dependency specifications omitted. For
|
||||||
|
/// example, if we have `a<2 ; sys_platform == 'foo'` and `a>=2 ;
|
||||||
|
/// sys_platform == 'bar'`, then because the dependency specifications
|
||||||
|
/// have the same name and because the marker expressions are disjoint,
|
||||||
|
/// a fork occurs. One fork will contain `a<2` but not `a>=2`, while
|
||||||
|
/// the other fork will contain `a>=2` but not `a<2`.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Fork {
|
struct Fork {
|
||||||
|
/// The list of dependencies for this fork, guaranteed to be conflict
|
||||||
|
/// free. (i.e., There are no two packages with the same name with
|
||||||
|
/// non-overlapping marker expressions.)
|
||||||
dependencies: Vec<(PubGrubPackage, Range<Version>)>,
|
dependencies: Vec<(PubGrubPackage, Range<Version>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Intermediate state that represents a *possible* grouping of forks
|
||||||
|
/// for one package name.
|
||||||
|
///
|
||||||
|
/// This accumulates state while examining a `Dependencies` list. In
|
||||||
|
/// particular, it accumulates conflicting dependency specifications and marker
|
||||||
|
/// expressions. As soon as a fork can be ruled out, this state is switched to
|
||||||
|
/// `NoForkPossible`. If, at the end of visiting all `Dependencies`, we still
|
||||||
|
/// have `PossibleForks::PossiblyForking`, then a fork exists if and only if
|
||||||
|
/// one of its groups has length bigger than `1`.
|
||||||
|
///
|
||||||
|
/// One common way for a fork to be known to be impossible is if there exists
|
||||||
|
/// conflicting dependency specifications where at least one is unconditional.
|
||||||
|
/// For example, `a<2` and `a>=2 ; sys_platform == 'foo'`. In this case, `a<2`
|
||||||
|
/// has a marker expression that is always true and thus never disjoint with
|
||||||
|
/// any other marker expression. Therefore, there can be no fork for `a`.
|
||||||
|
///
|
||||||
|
/// Note that we use indices into a `Dependencies` list to represent packages.
|
||||||
|
/// This avoids excessive cloning.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PossibleForks<'a> {
|
enum PossibleForks<'a> {
|
||||||
|
/// A group of dependencies (all with the same package name) where it is
|
||||||
|
/// known that no forks exist.
|
||||||
NoForkPossible(Vec<usize>),
|
NoForkPossible(Vec<usize>),
|
||||||
|
/// A group of groups dependencies (all with the same package name) where
|
||||||
|
/// it is possible for each group to correspond to a fork.
|
||||||
PossiblyForking(PossibleForkGroups<'a>),
|
PossiblyForking(PossibleForkGroups<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2064,8 +2119,11 @@ impl<'a> PossibleForks<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of groups of dependencies (all with the same package name), where
|
||||||
|
/// each group may correspond to a fork.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PossibleForkGroups<'a> {
|
struct PossibleForkGroups<'a> {
|
||||||
|
/// The list of forks.
|
||||||
forks: Vec<PossibleFork<'a>>,
|
forks: Vec<PossibleFork<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2083,6 +2141,20 @@ impl<'a> PossibleForkGroups<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Intermediate state representing a single possible fork.
|
||||||
|
///
|
||||||
|
/// The key invariant here is that, beyond a singleton fork, for all packages
|
||||||
|
/// in this fork, its marker expression must be overlapping with at least one
|
||||||
|
/// other package's marker expression. That is, when considering whether a
|
||||||
|
/// dependency specification with a conflicting package name provokes a fork
|
||||||
|
/// or not, one must look at the existing possible groups of forks. If any of
|
||||||
|
/// those groups have a package with an overlapping marker expression, then
|
||||||
|
/// the conflicting package name cannot possibly introduce a new fork. But if
|
||||||
|
/// there is no existing fork with an overlapping marker expression, then the
|
||||||
|
/// conflict provokes a new fork.
|
||||||
|
///
|
||||||
|
/// As with other intermediate data types, we use indices into a list of
|
||||||
|
/// `Dependencies` to represent packages to avoid excessive cloning.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PossibleFork<'a> {
|
struct PossibleFork<'a> {
|
||||||
packages: Vec<(usize, &'a MarkerTree)>,
|
packages: Vec<(usize, &'a MarkerTree)>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue