mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
uv-resolver: use Requires-Python to filter dependencies during universal resolution
In the time before universal resolving, we would always pass a `MarkerEnvironment`, and this environment would capture any relevant `Requires-Python` specifier (including if `-p/--python` was provided on the CLI). But in universal resolution, we very specifically do not use a `MarkerEnvironment` because we want to produce a resolution that is compatible across potentially multiple environments. This in turn meant that we lost `Requires-Python` filtering. This PR adds it back. We do this by converting our `PythonRequirement` into a `MarkerTree` that encodes the version specifiers in a `Requires-Python` specifier. We then ask whether that `MarkerTree` is disjoint with a dependency specification's `MarkerTree`. If it is, then we know it's impossible for that dependency specification to every be true, and we can completely ignore it.
This commit is contained in:
parent
c32667caec
commit
75b323232d
8 changed files with 147 additions and 422 deletions
|
@ -47,6 +47,7 @@ use crate::pubgrub::{
|
|||
PubGrubPriorities, PubGrubPython, PubGrubSpecifier,
|
||||
};
|
||||
use crate::python_requirement::PythonRequirement;
|
||||
use crate::requires_python::RequiresPython;
|
||||
use crate::resolution::ResolutionGraph;
|
||||
pub(crate) use crate::resolver::availability::{
|
||||
IncompletePackage, ResolverVersion, UnavailablePackage, UnavailableReason, UnavailableVersion,
|
||||
|
@ -94,6 +95,14 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
|
|||
/// When not set, the resolver is in "universal" mode.
|
||||
markers: Option<MarkerEnvironment>,
|
||||
python_requirement: PythonRequirement,
|
||||
/// This is derived from `PythonRequirement` once at initialization
|
||||
/// time. It's used in universal mode to filter our dependencies with
|
||||
/// a `python_version` marker expression that has no overlap with the
|
||||
/// `Requires-Python` specifier.
|
||||
///
|
||||
/// This is non-None if and only if the resolver is operating in
|
||||
/// universal mode. (i.e., when `markers` is `None`.)
|
||||
requires_python: Option<MarkerTree>,
|
||||
selector: CandidateSelector,
|
||||
index: InMemoryIndex,
|
||||
installed_packages: InstalledPackages,
|
||||
|
@ -181,6 +190,16 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
provider: Provider,
|
||||
installed_packages: InstalledPackages,
|
||||
) -> Result<Self, ResolveError> {
|
||||
let requires_python = if markers.is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
python_requirement
|
||||
.requires_python()
|
||||
.map(RequiresPython::to_marker_tree)
|
||||
.unwrap_or_else(|| MarkerTree::And(vec![])),
|
||||
)
|
||||
};
|
||||
let state = ResolverState {
|
||||
index: index.clone(),
|
||||
git: git.clone(),
|
||||
|
@ -200,6 +219,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
hasher: hasher.clone(),
|
||||
markers: markers.cloned(),
|
||||
python_requirement: python_requirement.clone(),
|
||||
requires_python,
|
||||
reporter: None,
|
||||
installed_packages,
|
||||
};
|
||||
|
@ -959,6 +979,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&self.locals,
|
||||
&self.git,
|
||||
self.markers.as_ref(),
|
||||
self.requires_python.as_ref(),
|
||||
);
|
||||
|
||||
let dependencies = match dependencies {
|
||||
|
@ -1109,6 +1130,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&self.locals,
|
||||
&self.git,
|
||||
self.markers.as_ref(),
|
||||
self.requires_python.as_ref(),
|
||||
)?;
|
||||
|
||||
for (dep_package, dep_version) in dependencies.iter() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue