mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-30 15:27:24 +00:00
uv: use ResolverEnvironment instead of ResolverMarkers
This updates the surrounding code to use the new ResolverEnvironment type. In some cases, this simplifies caller code by removing case analysis. There *shouldn't* be any behavior changes here. Some test snapshots were updated to account for some minor tweaks to error messages. I didn't split this up into separate commits because it would have been too difficult/costly.
This commit is contained in:
parent
44c9ef6aea
commit
acaed763b7
42 changed files with 416 additions and 600 deletions
|
@ -101,7 +101,7 @@ mod resolver {
|
|||
use uv_python::Interpreter;
|
||||
use uv_resolver::{
|
||||
FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, RequiresPython,
|
||||
ResolutionGraph, Resolver, ResolverMarkers,
|
||||
ResolutionGraph, Resolver, ResolverEnvironment,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||
|
||||
|
@ -198,9 +198,9 @@ mod resolver {
|
|||
);
|
||||
|
||||
let markers = if universal {
|
||||
ResolverMarkers::universal(vec![])
|
||||
ResolverEnvironment::universal(vec![])
|
||||
} else {
|
||||
ResolverMarkers::specific_environment(ResolverMarkerEnvironment::from(MARKERS.clone()))
|
||||
ResolverEnvironment::specific(ResolverMarkerEnvironment::from(MARKERS.clone()))
|
||||
};
|
||||
|
||||
let resolver = Resolver::new(
|
||||
|
|
|
@ -30,7 +30,7 @@ use uv_pypi_types::Requirement;
|
|||
use uv_python::{Interpreter, PythonEnvironment};
|
||||
use uv_resolver::{
|
||||
ExcludeNewer, FlatIndex, Flexibility, InMemoryIndex, Manifest, OptionsBuilder,
|
||||
PythonRequirement, Resolver, ResolverMarkers,
|
||||
PythonRequirement, Resolver, ResolverEnvironment,
|
||||
};
|
||||
use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||
|
||||
|
@ -174,7 +174,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
|||
|
||||
async fn resolve<'data>(&'data self, requirements: &'data [Requirement]) -> Result<Resolution> {
|
||||
let python_requirement = PythonRequirement::from_interpreter(self.interpreter);
|
||||
let markers = self.interpreter.resolver_markers();
|
||||
let marker_env = self.interpreter.resolver_marker_environment();
|
||||
let tags = self.interpreter.tags()?;
|
||||
|
||||
let resolver = Resolver::new(
|
||||
|
@ -185,7 +185,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
|||
.flexibility(Flexibility::Fixed)
|
||||
.build(),
|
||||
&python_requirement,
|
||||
ResolverMarkers::specific_environment(markers),
|
||||
ResolverEnvironment::specific(marker_env),
|
||||
Some(tags),
|
||||
self.flat_index,
|
||||
self.index,
|
||||
|
|
|
@ -147,7 +147,7 @@ impl Interpreter {
|
|||
}
|
||||
|
||||
/// Return the [`ResolverMarkerEnvironment`] for this Python executable.
|
||||
pub fn resolver_markers(&self) -> ResolverMarkerEnvironment {
|
||||
pub fn resolver_marker_environment(&self) -> ResolverMarkerEnvironment {
|
||||
ResolverMarkerEnvironment::from(self.markers().clone())
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use uv_distribution::{DistributionDatabase, Reporter};
|
|||
use uv_distribution_types::{Dist, DistributionMetadata};
|
||||
use uv_normalize::GroupName;
|
||||
use uv_pypi_types::{Requirement, RequirementSource};
|
||||
use uv_resolver::{InMemoryIndex, MetadataResponse, ResolverMarkers};
|
||||
use uv_resolver::{InMemoryIndex, MetadataResponse, ResolverEnvironment};
|
||||
use uv_types::{BuildContext, HashStrategy, RequestedRequirements};
|
||||
|
||||
use crate::{required_dist, Error};
|
||||
|
@ -87,7 +87,7 @@ impl<'a, Context: BuildContext> LookaheadResolver<'a, Context> {
|
|||
/// to "only evaluate marker expressions that reference an extra name.")
|
||||
pub async fn resolve(
|
||||
self,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Result<Vec<RequestedRequirements>, Error> {
|
||||
let mut results = Vec::new();
|
||||
let mut futures = FuturesUnordered::new();
|
||||
|
@ -97,7 +97,7 @@ impl<'a, Context: BuildContext> LookaheadResolver<'a, Context> {
|
|||
let mut queue: VecDeque<_> = self
|
||||
.constraints
|
||||
.apply(self.overrides.apply(self.requirements))
|
||||
.filter(|requirement| requirement.evaluate_markers(markers.marker_environment(), &[]))
|
||||
.filter(|requirement| requirement.evaluate_markers(env.marker_environment(), &[]))
|
||||
.map(|requirement| (*requirement).clone())
|
||||
.collect();
|
||||
|
||||
|
@ -117,7 +117,7 @@ impl<'a, Context: BuildContext> LookaheadResolver<'a, Context> {
|
|||
.apply(self.overrides.apply(lookahead.requirements()))
|
||||
{
|
||||
if requirement
|
||||
.evaluate_markers(markers.marker_environment(), lookahead.extras())
|
||||
.evaluate_markers(env.marker_environment(), lookahead.extras())
|
||||
{
|
||||
queue.push_back((*requirement).clone());
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::preferences::Preferences;
|
|||
use crate::prerelease::{AllowPrerelease, PrereleaseStrategy};
|
||||
use crate::resolution_mode::ResolutionStrategy;
|
||||
use crate::version_map::{VersionMap, VersionMapDistHandle};
|
||||
use crate::{Exclusions, Manifest, Options, ResolverMarkers};
|
||||
use crate::{Exclusions, Manifest, Options, ResolverEnvironment};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
|
@ -30,19 +30,19 @@ impl CandidateSelector {
|
|||
pub(crate) fn for_resolution(
|
||||
options: Options,
|
||||
manifest: &Manifest,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Self {
|
||||
Self {
|
||||
resolution_strategy: ResolutionStrategy::from_mode(
|
||||
options.resolution_mode,
|
||||
manifest,
|
||||
markers,
|
||||
env,
|
||||
options.dependency_mode,
|
||||
),
|
||||
prerelease_strategy: PrereleaseStrategy::from_mode(
|
||||
options.prerelease_mode,
|
||||
manifest,
|
||||
markers,
|
||||
env,
|
||||
options.dependency_mode,
|
||||
),
|
||||
index_strategy: options.index_strategy,
|
||||
|
@ -80,7 +80,7 @@ impl CandidateSelector {
|
|||
preferences: &'a Preferences,
|
||||
installed_packages: &'a InstalledPackages,
|
||||
exclusions: &'a Exclusions,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Option<Candidate<'a>> {
|
||||
let is_excluded = exclusions.contains(package_name);
|
||||
|
||||
|
@ -93,7 +93,7 @@ impl CandidateSelector {
|
|||
preferences,
|
||||
installed_packages,
|
||||
is_excluded,
|
||||
markers,
|
||||
env,
|
||||
) {
|
||||
trace!("Using preference {} {}", preferred.name, preferred.version);
|
||||
return Some(preferred);
|
||||
|
@ -111,7 +111,7 @@ impl CandidateSelector {
|
|||
}
|
||||
}
|
||||
|
||||
self.select_no_preference(package_name, range, version_maps, markers)
|
||||
self.select_no_preference(package_name, range, version_maps, env)
|
||||
}
|
||||
|
||||
/// If the package has a preference, an existing version from an existing lockfile or a version
|
||||
|
@ -131,74 +131,26 @@ impl CandidateSelector {
|
|||
preferences: &'a Preferences,
|
||||
installed_packages: &'a InstalledPackages,
|
||||
is_excluded: bool,
|
||||
resolver_markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Option<Candidate> {
|
||||
// In the branches, we "sort" the preferences by marker-matching through an iterator that
|
||||
// first has the matching half and then the mismatching half.
|
||||
match resolver_markers {
|
||||
ResolverMarkers::SpecificEnvironment(env) => {
|
||||
// We may hit a combination of fork markers preferences with specific environment
|
||||
// output in the future when adding support for the PEP 665 successor.
|
||||
let preferences_match =
|
||||
preferences.get(package_name).filter(|(marker, _version)| {
|
||||
// `.unwrap_or(true)` because the universal marker is considered matching.
|
||||
marker
|
||||
.map(|marker| marker.evaluate(env, &[]))
|
||||
.unwrap_or(true)
|
||||
});
|
||||
let preferences_mismatch =
|
||||
preferences.get(package_name).filter(|(marker, _version)| {
|
||||
marker
|
||||
.map(|marker| !marker.evaluate(env, &[]))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
self.get_preferred_from_iter(
|
||||
preferences_match.chain(preferences_mismatch),
|
||||
package_name,
|
||||
range,
|
||||
version_maps,
|
||||
installed_packages,
|
||||
is_excluded,
|
||||
resolver_markers,
|
||||
)
|
||||
}
|
||||
ResolverMarkers::Universal { .. } => {
|
||||
// In universal mode, all preferences are matching.
|
||||
self.get_preferred_from_iter(
|
||||
preferences.get(package_name),
|
||||
package_name,
|
||||
range,
|
||||
version_maps,
|
||||
installed_packages,
|
||||
is_excluded,
|
||||
resolver_markers,
|
||||
)
|
||||
}
|
||||
ResolverMarkers::Fork(fork_markers) => {
|
||||
let preferences_match =
|
||||
preferences.get(package_name).filter(|(marker, _version)| {
|
||||
// `.unwrap_or(true)` because the universal marker is considered matching.
|
||||
marker
|
||||
.map(|marker| !marker.is_disjoint(fork_markers))
|
||||
.unwrap_or(true)
|
||||
});
|
||||
let preferences_mismatch =
|
||||
preferences.get(package_name).filter(|(marker, _version)| {
|
||||
marker
|
||||
.map(|marker| marker.is_disjoint(fork_markers))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
self.get_preferred_from_iter(
|
||||
preferences_match.chain(preferences_mismatch),
|
||||
package_name,
|
||||
range,
|
||||
version_maps,
|
||||
installed_packages,
|
||||
is_excluded,
|
||||
resolver_markers,
|
||||
)
|
||||
}
|
||||
}
|
||||
let preferences_match = preferences.get(package_name).filter(|(marker, _version)| {
|
||||
// `.unwrap_or(true)` because the universal marker is considered matching.
|
||||
marker.map(|marker| env.included(marker)).unwrap_or(true)
|
||||
});
|
||||
let preferences_mismatch = preferences.get(package_name).filter(|(marker, _version)| {
|
||||
marker.map(|marker| !env.included(marker)).unwrap_or(false)
|
||||
});
|
||||
self.get_preferred_from_iter(
|
||||
preferences_match.chain(preferences_mismatch),
|
||||
package_name,
|
||||
range,
|
||||
version_maps,
|
||||
installed_packages,
|
||||
is_excluded,
|
||||
env,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the first preference that satisfies the current range and is allowed.
|
||||
|
@ -210,7 +162,7 @@ impl CandidateSelector {
|
|||
version_maps: &'a [VersionMap],
|
||||
installed_packages: &'a InstalledPackages,
|
||||
is_excluded: bool,
|
||||
resolver_markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Option<Candidate<'a>> {
|
||||
for (marker, version) in preferences {
|
||||
// Respect the version range for this requirement.
|
||||
|
@ -247,10 +199,7 @@ impl CandidateSelector {
|
|||
|
||||
// Respect the pre-release strategy for this fork.
|
||||
if version.any_prerelease() {
|
||||
let allow = match self
|
||||
.prerelease_strategy
|
||||
.allows(package_name, resolver_markers)
|
||||
{
|
||||
let allow = match self.prerelease_strategy.allows(package_name, env) {
|
||||
AllowPrerelease::Yes => true,
|
||||
AllowPrerelease::No => false,
|
||||
// If the pre-release is "global" (i.e., provided via a lockfile, rather than
|
||||
|
@ -321,7 +270,7 @@ impl CandidateSelector {
|
|||
package_name: &'a PackageName,
|
||||
range: &Range<Version>,
|
||||
version_maps: &'a [VersionMap],
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Option<Candidate> {
|
||||
trace!(
|
||||
"Selecting candidate for {package_name} with range {range} with {} remote versions",
|
||||
|
@ -329,7 +278,7 @@ impl CandidateSelector {
|
|||
);
|
||||
let highest = self.use_highest_version(package_name);
|
||||
|
||||
let allow_prerelease = match self.prerelease_strategy.allows(package_name, markers) {
|
||||
let allow_prerelease = match self.prerelease_strategy.allows(package_name, env) {
|
||||
AllowPrerelease::Yes => true,
|
||||
AllowPrerelease::No => false,
|
||||
// Allow pre-releases if there are no stable versions available.
|
||||
|
|
|
@ -12,7 +12,9 @@ use crate::fork_urls::ForkUrls;
|
|||
use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner, PubGrubReportFormatter};
|
||||
use crate::python_requirement::PythonRequirement;
|
||||
use crate::resolution::ConflictingDistributionError;
|
||||
use crate::resolver::{IncompletePackage, ResolverMarkers, UnavailablePackage, UnavailableReason};
|
||||
use crate::resolver::{
|
||||
IncompletePackage, ResolverEnvironment, UnavailablePackage, UnavailableReason,
|
||||
};
|
||||
use crate::Options;
|
||||
use tracing::trace;
|
||||
use uv_distribution_types::{
|
||||
|
@ -20,7 +22,6 @@ use uv_distribution_types::{
|
|||
};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_pep440::Version;
|
||||
use uv_pep508::MarkerTree;
|
||||
use uv_static::EnvVars;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -40,24 +41,34 @@ pub enum ResolveError {
|
|||
#[error("Overrides contain conflicting URLs for package `{0}`:\n- {1}\n- {2}")]
|
||||
ConflictingOverrideUrls(PackageName, String, String),
|
||||
|
||||
#[error("Requirements contain conflicting URLs for package `{0}`:\n- {}", _1.join("\n- "))]
|
||||
ConflictingUrlsUniversal(PackageName, Vec<String>),
|
||||
|
||||
#[error("Requirements contain conflicting URLs for package `{package_name}` in split `{fork_markers:?}`:\n- {}", urls.join("\n- "))]
|
||||
ConflictingUrlsFork {
|
||||
#[error(
|
||||
"Requirements contain conflicting URLs for package `{package_name}`{}:\n- {}",
|
||||
if env.marker_environment().is_some() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" in {env}")
|
||||
},
|
||||
urls.join("\n- "),
|
||||
)]
|
||||
ConflictingUrls {
|
||||
package_name: PackageName,
|
||||
urls: Vec<String>,
|
||||
fork_markers: MarkerTree,
|
||||
env: ResolverEnvironment,
|
||||
},
|
||||
|
||||
#[error("Requirements contain conflicting indexes for package `{0}`:\n- {}", _1.join("\n- "))]
|
||||
ConflictingIndexesUniversal(PackageName, Vec<String>),
|
||||
|
||||
#[error("Requirements contain conflicting indexes for package `{package_name}` in split `{fork_markers:?}`:\n- {}", indexes.join("\n- "))]
|
||||
ConflictingIndexesFork {
|
||||
#[error(
|
||||
"Requirements contain conflicting indexes for package `{package_name}`{}:\n- {}",
|
||||
if env.marker_environment().is_some() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" in {env}")
|
||||
},
|
||||
indexes.join("\n- "),
|
||||
)]
|
||||
ConflictingIndexesForEnvironment {
|
||||
package_name: PackageName,
|
||||
indexes: Vec<String>,
|
||||
fork_markers: MarkerTree,
|
||||
env: ResolverEnvironment,
|
||||
},
|
||||
|
||||
#[error("Requirements contain conflicting indexes for package `{0}`: `{1}` vs. `{2}`")]
|
||||
|
@ -127,7 +138,7 @@ pub struct NoSolutionError {
|
|||
unavailable_packages: FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
fork_urls: ForkUrls,
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
workspace_members: BTreeSet<PackageName>,
|
||||
options: Options,
|
||||
}
|
||||
|
@ -145,7 +156,7 @@ impl NoSolutionError {
|
|||
unavailable_packages: FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
fork_urls: ForkUrls,
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
workspace_members: BTreeSet<PackageName>,
|
||||
options: Options,
|
||||
) -> Self {
|
||||
|
@ -160,7 +171,7 @@ impl NoSolutionError {
|
|||
unavailable_packages,
|
||||
incomplete_packages,
|
||||
fork_urls,
|
||||
markers,
|
||||
env,
|
||||
workspace_members,
|
||||
options,
|
||||
}
|
||||
|
@ -212,7 +223,7 @@ impl NoSolutionError {
|
|||
|
||||
/// Initialize a [`NoSolutionHeader`] for this error.
|
||||
pub fn header(&self) -> NoSolutionHeader {
|
||||
NoSolutionHeader::new(self.markers.clone())
|
||||
NoSolutionHeader::new(self.env.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,7 +277,7 @@ impl std::fmt::Display for NoSolutionError {
|
|||
&self.unavailable_packages,
|
||||
&self.incomplete_packages,
|
||||
&self.fork_urls,
|
||||
&self.markers,
|
||||
&self.env,
|
||||
&self.workspace_members,
|
||||
self.options,
|
||||
&mut additional_hints,
|
||||
|
@ -689,19 +700,16 @@ fn drop_root_dependency_on_project(
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct NoSolutionHeader {
|
||||
/// The [`ResolverMarkers`] that caused the failure.
|
||||
markers: ResolverMarkers,
|
||||
/// The [`ResolverEnvironment`] that caused the failure.
|
||||
env: ResolverEnvironment,
|
||||
/// The additional context for the resolution failure.
|
||||
context: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl NoSolutionHeader {
|
||||
/// Create a new [`NoSolutionHeader`] with the given [`ResolverMarkers`].
|
||||
pub fn new(markers: ResolverMarkers) -> Self {
|
||||
Self {
|
||||
markers,
|
||||
context: None,
|
||||
}
|
||||
/// Create a new [`NoSolutionHeader`] with the given [`ResolverEnvironment`].
|
||||
pub fn new(env: ResolverEnvironment) -> Self {
|
||||
Self { env, context: None }
|
||||
}
|
||||
|
||||
/// Set the context for the resolution failure.
|
||||
|
@ -714,30 +722,20 @@ impl NoSolutionHeader {
|
|||
|
||||
impl std::fmt::Display for NoSolutionHeader {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.markers {
|
||||
ResolverMarkers::SpecificEnvironment(_) | ResolverMarkers::Universal { .. } => {
|
||||
if let Some(context) = self.context {
|
||||
write!(
|
||||
f,
|
||||
"No solution found when resolving {context} dependencies:"
|
||||
)
|
||||
} else {
|
||||
write!(f, "No solution found when resolving dependencies:")
|
||||
}
|
||||
}
|
||||
ResolverMarkers::Fork(markers) => {
|
||||
if let Some(context) = self.context {
|
||||
write!(
|
||||
f,
|
||||
"No solution found when resolving {context} dependencies for split ({markers:?}):",
|
||||
)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"No solution found when resolving dependencies for split ({markers:?}):",
|
||||
)
|
||||
}
|
||||
}
|
||||
match (self.context, self.env.end_user_fork_display()) {
|
||||
(None, None) => write!(f, "No solution found when resolving dependencies:"),
|
||||
(Some(context), None) => write!(
|
||||
f,
|
||||
"No solution found when resolving {context} dependencies:"
|
||||
),
|
||||
(None, Some(split)) => write!(
|
||||
f,
|
||||
"No solution found when resolving dependencies for {split}:"
|
||||
),
|
||||
(Some(context), Some(split)) => write!(
|
||||
f,
|
||||
"No solution found when resolving {context} dependencies for {split}:"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc_hash::FxHashMap;
|
|||
use uv_distribution_types::IndexUrl;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::resolver::ResolverMarkers;
|
||||
use crate::resolver::ResolverEnvironment;
|
||||
use crate::ResolveError;
|
||||
|
||||
/// See [`crate::resolver::ForkState`].
|
||||
|
@ -20,27 +20,17 @@ impl ForkIndexes {
|
|||
&mut self,
|
||||
package_name: &PackageName,
|
||||
index: &IndexUrl,
|
||||
fork_markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Result<(), ResolveError> {
|
||||
if let Some(previous) = self.0.insert(package_name.clone(), index.clone()) {
|
||||
if &previous != index {
|
||||
let mut conflicts = vec![previous.to_string(), index.to_string()];
|
||||
conflicts.sort();
|
||||
return match fork_markers {
|
||||
ResolverMarkers::Universal { .. } | ResolverMarkers::SpecificEnvironment(_) => {
|
||||
Err(ResolveError::ConflictingIndexesUniversal(
|
||||
package_name.clone(),
|
||||
conflicts,
|
||||
))
|
||||
}
|
||||
ResolverMarkers::Fork(fork_markers) => {
|
||||
Err(ResolveError::ConflictingIndexesFork {
|
||||
package_name: package_name.clone(),
|
||||
indexes: conflicts,
|
||||
fork_markers: fork_markers.clone(),
|
||||
})
|
||||
}
|
||||
};
|
||||
return Err(ResolveError::ConflictingIndexesForEnvironment {
|
||||
package_name: package_name.clone(),
|
||||
indexes: conflicts,
|
||||
env: env.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -6,7 +6,7 @@ use uv_distribution_types::Verbatim;
|
|||
use uv_normalize::PackageName;
|
||||
use uv_pypi_types::VerbatimParsedUrl;
|
||||
|
||||
use crate::resolver::ResolverMarkers;
|
||||
use crate::resolver::ResolverEnvironment;
|
||||
use crate::ResolveError;
|
||||
|
||||
/// See [`crate::resolver::ForkState`].
|
||||
|
@ -29,7 +29,7 @@ impl ForkUrls {
|
|||
&mut self,
|
||||
package_name: &PackageName,
|
||||
url: &VerbatimParsedUrl,
|
||||
fork_markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Result<(), ResolveError> {
|
||||
match self.0.entry(package_name.clone()) {
|
||||
Entry::Occupied(previous) => {
|
||||
|
@ -39,22 +39,11 @@ impl ForkUrls {
|
|||
url.verbatim.verbatim().to_string(),
|
||||
];
|
||||
conflicting_url.sort();
|
||||
return match fork_markers {
|
||||
ResolverMarkers::Universal { .. }
|
||||
| ResolverMarkers::SpecificEnvironment(_) => {
|
||||
Err(ResolveError::ConflictingUrlsUniversal(
|
||||
package_name.clone(),
|
||||
conflicting_url,
|
||||
))
|
||||
}
|
||||
ResolverMarkers::Fork(fork_markers) => {
|
||||
Err(ResolveError::ConflictingUrlsFork {
|
||||
package_name: package_name.clone(),
|
||||
urls: conflicting_url,
|
||||
fork_markers: fork_markers.clone(),
|
||||
})
|
||||
}
|
||||
};
|
||||
return Err(ResolveError::ConflictingUrls {
|
||||
package_name: package_name.clone(),
|
||||
urls: conflicting_url,
|
||||
env: env.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Entry::Vacant(vacant) => {
|
||||
|
|
|
@ -19,8 +19,8 @@ pub use resolution::{
|
|||
pub use resolution_mode::ResolutionMode;
|
||||
pub use resolver::{
|
||||
BuildId, DefaultResolverProvider, InMemoryIndex, MetadataResponse, PackageVersionsResult,
|
||||
Reporter as ResolverReporter, Resolver, ResolverMarkers, ResolverProvider, VersionsResponse,
|
||||
WheelMetadataResult,
|
||||
Reporter as ResolverReporter, Resolver, ResolverEnvironment, ResolverProvider,
|
||||
VersionsResponse, WheelMetadataResult,
|
||||
};
|
||||
pub use version_map::VersionMap;
|
||||
pub use yanks::AllowedYanks;
|
||||
|
|
|
@ -9,7 +9,7 @@ use uv_pypi_types::Requirement;
|
|||
use uv_types::RequestedRequirements;
|
||||
|
||||
use crate::preferences::Preferences;
|
||||
use crate::{DependencyMode, Exclusions, ResolverMarkers};
|
||||
use crate::{DependencyMode, Exclusions, ResolverEnvironment};
|
||||
|
||||
/// A manifest of requirements, constraints, and preferences.
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -109,57 +109,55 @@ impl Manifest {
|
|||
/// - Determining which requirements should allow local version specifiers (e.g., `torch==2.2.0+cpu`).
|
||||
pub fn requirements<'a>(
|
||||
&'a self,
|
||||
markers: &'a ResolverMarkers,
|
||||
env: &'a ResolverEnvironment,
|
||||
mode: DependencyMode,
|
||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||
self.requirements_no_overrides(markers, mode)
|
||||
.chain(self.overrides(markers, mode))
|
||||
self.requirements_no_overrides(env, mode)
|
||||
.chain(self.overrides(env, mode))
|
||||
}
|
||||
|
||||
/// Like [`Self::requirements`], but without the overrides.
|
||||
pub fn requirements_no_overrides<'a>(
|
||||
&'a self,
|
||||
markers: &'a ResolverMarkers,
|
||||
env: &'a ResolverEnvironment,
|
||||
mode: DependencyMode,
|
||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||
match mode {
|
||||
// Include all direct and transitive requirements, with constraints and overrides applied.
|
||||
DependencyMode::Transitive => {
|
||||
Either::Left(
|
||||
self.lookaheads
|
||||
.iter()
|
||||
.flat_map(move |lookahead| {
|
||||
self.overrides.apply(lookahead.requirements()).filter(
|
||||
move |requirement| {
|
||||
requirement.evaluate_markers(
|
||||
markers.marker_environment(),
|
||||
lookahead.extras(),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.chain(self.overrides.apply(&self.requirements).filter(
|
||||
move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
},
|
||||
))
|
||||
.chain(
|
||||
self.constraints
|
||||
.requirements()
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
})
|
||||
.map(Cow::Borrowed),
|
||||
),
|
||||
)
|
||||
}
|
||||
DependencyMode::Transitive => Either::Left(
|
||||
self.lookaheads
|
||||
.iter()
|
||||
.flat_map(move |lookahead| {
|
||||
self.overrides
|
||||
.apply(lookahead.requirements())
|
||||
.filter(move |requirement| {
|
||||
requirement
|
||||
.evaluate_markers(env.marker_environment(), lookahead.extras())
|
||||
})
|
||||
})
|
||||
.chain(
|
||||
self.overrides
|
||||
.apply(&self.requirements)
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(env.marker_environment(), &[])
|
||||
}),
|
||||
)
|
||||
.chain(
|
||||
self.constraints
|
||||
.requirements()
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(env.marker_environment(), &[])
|
||||
})
|
||||
.map(Cow::Borrowed),
|
||||
),
|
||||
),
|
||||
// Include direct requirements, with constraints and overrides applied.
|
||||
DependencyMode::Direct => Either::Right(
|
||||
self.overrides
|
||||
.apply(&self.requirements)
|
||||
.chain(self.constraints.requirements().map(Cow::Borrowed))
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
requirement.evaluate_markers(env.marker_environment(), &[])
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
@ -168,7 +166,7 @@ impl Manifest {
|
|||
/// Only the overrides from [`Self::requirements`].
|
||||
pub fn overrides<'a>(
|
||||
&'a self,
|
||||
markers: &'a ResolverMarkers,
|
||||
env: &'a ResolverEnvironment,
|
||||
mode: DependencyMode,
|
||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||
match mode {
|
||||
|
@ -177,7 +175,7 @@ impl Manifest {
|
|||
self.overrides
|
||||
.requirements()
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
requirement.evaluate_markers(env.marker_environment(), &[])
|
||||
})
|
||||
.map(Cow::Borrowed),
|
||||
),
|
||||
|
@ -186,7 +184,7 @@ impl Manifest {
|
|||
self.overrides
|
||||
.requirements()
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
requirement.evaluate_markers(env.marker_environment(), &[])
|
||||
})
|
||||
.map(Cow::Borrowed),
|
||||
),
|
||||
|
@ -205,41 +203,37 @@ impl Manifest {
|
|||
/// the `lowest-direct` strategy is in use.
|
||||
pub fn user_requirements<'a>(
|
||||
&'a self,
|
||||
markers: &'a ResolverMarkers,
|
||||
env: &'a ResolverEnvironment,
|
||||
mode: DependencyMode,
|
||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||
match mode {
|
||||
// Include direct requirements, dependencies of editables, and transitive dependencies
|
||||
// of local packages.
|
||||
DependencyMode::Transitive => {
|
||||
Either::Left(
|
||||
self.lookaheads
|
||||
.iter()
|
||||
.filter(|lookahead| lookahead.direct())
|
||||
.flat_map(move |lookahead| {
|
||||
self.overrides.apply(lookahead.requirements()).filter(
|
||||
move |requirement| {
|
||||
requirement.evaluate_markers(
|
||||
markers.marker_environment(),
|
||||
lookahead.extras(),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.chain(self.overrides.apply(&self.requirements).filter(
|
||||
move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
},
|
||||
)),
|
||||
)
|
||||
}
|
||||
DependencyMode::Transitive => Either::Left(
|
||||
self.lookaheads
|
||||
.iter()
|
||||
.filter(|lookahead| lookahead.direct())
|
||||
.flat_map(move |lookahead| {
|
||||
self.overrides
|
||||
.apply(lookahead.requirements())
|
||||
.filter(move |requirement| {
|
||||
requirement
|
||||
.evaluate_markers(env.marker_environment(), lookahead.extras())
|
||||
})
|
||||
})
|
||||
.chain(
|
||||
self.overrides
|
||||
.apply(&self.requirements)
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(env.marker_environment(), &[])
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
// Restrict to the direct requirements.
|
||||
DependencyMode::Direct => {
|
||||
Either::Right(self.overrides.apply(self.requirements.iter()).filter(
|
||||
move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
},
|
||||
move |requirement| requirement.evaluate_markers(env.marker_environment(), &[]),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -252,13 +246,11 @@ impl Manifest {
|
|||
/// resolution (assuming the user enabled development dependencies).
|
||||
pub fn direct_requirements<'a>(
|
||||
&'a self,
|
||||
markers: &'a ResolverMarkers,
|
||||
env: &'a ResolverEnvironment,
|
||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||
self.overrides
|
||||
.apply(self.requirements.iter())
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||
})
|
||||
.filter(move |requirement| requirement.evaluate_markers(env.marker_environment(), &[]))
|
||||
}
|
||||
|
||||
/// Apply the overrides and constraints to a set of requirements.
|
||||
|
|
|
@ -10,7 +10,7 @@ use uv_pep508::{MarkerTree, VersionOrUrl};
|
|||
use uv_pypi_types::{HashDigest, HashError};
|
||||
use uv_requirements_txt::{RequirementEntry, RequirementsTxtRequirement};
|
||||
|
||||
use crate::ResolverMarkers;
|
||||
use crate::ResolverEnvironment;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum PreferenceError {
|
||||
|
@ -119,16 +119,16 @@ pub struct Preferences(FxHashMap<PackageName, Vec<(Option<MarkerTree>, Pin)>>);
|
|||
impl Preferences {
|
||||
/// Create a map of pinned packages from an iterator of [`Preference`] entries.
|
||||
///
|
||||
/// The provided [`MarkerEnvironment`] will be used to filter the preferences
|
||||
/// The provided [`ResolverEnvironment`] will be used to filter the preferences
|
||||
/// to an applicable subset.
|
||||
pub fn from_iter<PreferenceIterator: IntoIterator<Item = Preference>>(
|
||||
preferences: PreferenceIterator,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Self {
|
||||
let mut slf = Self::default();
|
||||
for preference in preferences {
|
||||
// Filter non-matching preferences when resolving for an environment.
|
||||
if let Some(markers) = markers.marker_environment() {
|
||||
if let Some(markers) = env.marker_environment() {
|
||||
if !preference.marker.evaluate(markers, &[]) {
|
||||
trace!("Excluding {preference} from preferences due to unmatched markers");
|
||||
continue;
|
||||
|
|
|
@ -3,7 +3,7 @@ use uv_pypi_types::RequirementSource;
|
|||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::resolver::ForkSet;
|
||||
use crate::{DependencyMode, Manifest, ResolverMarkers};
|
||||
use crate::{DependencyMode, Manifest, ResolverEnvironment};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
|
@ -67,7 +67,7 @@ impl PrereleaseStrategy {
|
|||
pub(crate) fn from_mode(
|
||||
mode: PrereleaseMode,
|
||||
manifest: &Manifest,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
dependencies: DependencyMode,
|
||||
) -> Self {
|
||||
let mut packages = ForkSet::default();
|
||||
|
@ -77,7 +77,7 @@ impl PrereleaseStrategy {
|
|||
PrereleaseMode::Allow => Self::Allow,
|
||||
PrereleaseMode::IfNecessary => Self::IfNecessary,
|
||||
_ => {
|
||||
for requirement in manifest.requirements(markers, dependencies) {
|
||||
for requirement in manifest.requirements(env, dependencies) {
|
||||
let RequirementSource::Registry { specifier, .. } = &requirement.source else {
|
||||
continue;
|
||||
};
|
||||
|
@ -103,21 +103,21 @@ impl PrereleaseStrategy {
|
|||
pub(crate) fn allows(
|
||||
&self,
|
||||
package_name: &PackageName,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> AllowPrerelease {
|
||||
match self {
|
||||
PrereleaseStrategy::Disallow => AllowPrerelease::No,
|
||||
PrereleaseStrategy::Allow => AllowPrerelease::Yes,
|
||||
PrereleaseStrategy::IfNecessary => AllowPrerelease::IfNecessary,
|
||||
PrereleaseStrategy::Explicit(packages) => {
|
||||
if packages.contains(package_name, markers) {
|
||||
if packages.contains(package_name, env) {
|
||||
AllowPrerelease::Yes
|
||||
} else {
|
||||
AllowPrerelease::No
|
||||
}
|
||||
}
|
||||
PrereleaseStrategy::IfNecessaryOrExplicit(packages) => {
|
||||
if packages.contains(package_name, markers) {
|
||||
if packages.contains(package_name, env) {
|
||||
AllowPrerelease::Yes
|
||||
} else {
|
||||
AllowPrerelease::IfNecessary
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::fork_urls::ForkUrls;
|
|||
use crate::prerelease::AllowPrerelease;
|
||||
use crate::python_requirement::{PythonRequirement, PythonRequirementSource};
|
||||
use crate::resolver::{IncompletePackage, UnavailablePackage, UnavailableReason};
|
||||
use crate::{Flexibility, Options, RequiresPython, ResolverMarkers};
|
||||
use crate::{Flexibility, Options, RequiresPython, ResolverEnvironment};
|
||||
|
||||
use super::{PubGrubPackage, PubGrubPackageInner, PubGrubPython};
|
||||
|
||||
|
@ -510,7 +510,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
unavailable_packages: &FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: &FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
fork_urls: &ForkUrls,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
workspace_members: &BTreeSet<PackageName>,
|
||||
options: Options,
|
||||
output_hints: &mut IndexSet<PubGrubHint>,
|
||||
|
@ -528,7 +528,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
name,
|
||||
set,
|
||||
selector,
|
||||
markers,
|
||||
env,
|
||||
output_hints,
|
||||
);
|
||||
}
|
||||
|
@ -596,7 +596,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
unavailable_packages,
|
||||
incomplete_packages,
|
||||
fork_urls,
|
||||
markers,
|
||||
env,
|
||||
workspace_members,
|
||||
options,
|
||||
output_hints,
|
||||
|
@ -610,7 +610,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
unavailable_packages,
|
||||
incomplete_packages,
|
||||
fork_urls,
|
||||
markers,
|
||||
env,
|
||||
workspace_members,
|
||||
options,
|
||||
output_hints,
|
||||
|
@ -756,7 +756,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
name: &PackageName,
|
||||
set: &Range<Version>,
|
||||
selector: &CandidateSelector,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
hints: &mut IndexSet<PubGrubHint>,
|
||||
) {
|
||||
let any_prerelease = set.iter().any(|(start, end)| {
|
||||
|
@ -775,7 +775,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
|
||||
if any_prerelease {
|
||||
// A pre-release marker appeared in the version requirements.
|
||||
if selector.prerelease_strategy().allows(name, markers) != AllowPrerelease::Yes {
|
||||
if selector.prerelease_strategy().allows(name, env) != AllowPrerelease::Yes {
|
||||
hints.insert(PubGrubHint::PrereleaseRequested {
|
||||
package: package.clone(),
|
||||
range: self.simplify_set(set, package).into_owned(),
|
||||
|
@ -793,7 +793,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
})
|
||||
{
|
||||
// There are pre-release versions available for the package.
|
||||
if selector.prerelease_strategy().allows(name, markers) != AllowPrerelease::Yes {
|
||||
if selector.prerelease_strategy().allows(name, env) != AllowPrerelease::Yes {
|
||||
hints.insert(PubGrubHint::PrereleaseAvailable {
|
||||
package: package.clone(),
|
||||
version: version.clone(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use uv_pep440::Version;
|
||||
use uv_pep508::MarkerTree;
|
||||
use uv_pep508::{MarkerEnvironment, MarkerTree};
|
||||
use uv_python::{Interpreter, PythonVersion};
|
||||
|
||||
use crate::{RequiresPython, RequiresPythonRange};
|
||||
|
@ -38,14 +38,7 @@ impl PythonRequirement {
|
|||
interpreter: &Interpreter,
|
||||
requires_python: RequiresPython,
|
||||
) -> Self {
|
||||
let exact = interpreter.python_full_version().version.clone();
|
||||
let installed = interpreter.python_full_version().version.only_release();
|
||||
Self {
|
||||
exact,
|
||||
installed: RequiresPython::greater_than_equal_version(&installed),
|
||||
target: requires_python,
|
||||
source: PythonRequirementSource::RequiresPython,
|
||||
}
|
||||
Self::from_marker_environment(interpreter.markers(), requires_python)
|
||||
}
|
||||
|
||||
/// Create a [`PythonRequirement`] to resolve against an [`Interpreter`].
|
||||
|
@ -60,6 +53,26 @@ impl PythonRequirement {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a [`PythonRequirement`] from a [`MarkerEnvironment`] and a
|
||||
/// specific `Requires-Python` directive.
|
||||
///
|
||||
/// This has the same "source" as
|
||||
/// [`PythonRequirement::from_requires_python`], but is useful for
|
||||
/// constructing a `PythonRequirement` without an [`Interpreter`].
|
||||
pub fn from_marker_environment(
|
||||
marker_env: &MarkerEnvironment,
|
||||
requires_python: RequiresPython,
|
||||
) -> Self {
|
||||
let exact = marker_env.python_full_version().version.clone();
|
||||
let installed = marker_env.python_full_version().version.only_release();
|
||||
Self {
|
||||
exact,
|
||||
installed: RequiresPython::greater_than_equal_version(&installed),
|
||||
target: requires_python,
|
||||
source: PythonRequirementSource::RequiresPython,
|
||||
}
|
||||
}
|
||||
|
||||
/// Narrow the [`PythonRequirement`] to the given version, if it's stricter (i.e., greater)
|
||||
/// than the current `Requires-Python` minimum.
|
||||
pub fn narrow(&self, target: &RequiresPythonRange) -> Option<Self> {
|
||||
|
|
|
@ -10,7 +10,7 @@ use uv_normalize::PackageName;
|
|||
use uv_pep508::MarkerTree;
|
||||
|
||||
use crate::resolution::{RequirementsTxtDist, ResolutionGraphNode};
|
||||
use crate::{ResolutionGraph, ResolverMarkers};
|
||||
use crate::{ResolutionGraph, ResolverEnvironment};
|
||||
|
||||
/// A [`std::fmt::Display`] implementation for the resolution graph.
|
||||
#[derive(Debug)]
|
||||
|
@ -18,8 +18,8 @@ use crate::{ResolutionGraph, ResolverMarkers};
|
|||
pub struct DisplayResolutionGraph<'a> {
|
||||
/// The underlying graph.
|
||||
resolution: &'a ResolutionGraph,
|
||||
/// The marker environment, used to determine the markers that apply to each package.
|
||||
marker_env: &'a ResolverMarkers,
|
||||
/// The resolver marker environment, used to determine the markers that apply to each package.
|
||||
env: &'a ResolverEnvironment,
|
||||
/// The packages to exclude from the output.
|
||||
no_emit_packages: &'a [PackageName],
|
||||
/// Whether to include hashes in the output.
|
||||
|
@ -58,7 +58,7 @@ impl<'a> DisplayResolutionGraph<'a> {
|
|||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
pub fn new(
|
||||
underlying: &'a ResolutionGraph,
|
||||
marker_env: &'a ResolverMarkers,
|
||||
env: &'a ResolverEnvironment,
|
||||
no_emit_packages: &'a [PackageName],
|
||||
show_hashes: bool,
|
||||
include_extras: bool,
|
||||
|
@ -69,7 +69,7 @@ impl<'a> DisplayResolutionGraph<'a> {
|
|||
) -> DisplayResolutionGraph<'a> {
|
||||
Self {
|
||||
resolution: underlying,
|
||||
marker_env,
|
||||
env,
|
||||
no_emit_packages,
|
||||
show_hashes,
|
||||
include_extras,
|
||||
|
@ -89,7 +89,7 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
let mut sources = SourceAnnotations::default();
|
||||
|
||||
for requirement in self.resolution.requirements.iter().filter(|requirement| {
|
||||
requirement.evaluate_markers(self.marker_env.marker_environment(), &[])
|
||||
requirement.evaluate_markers(self.env.marker_environment(), &[])
|
||||
}) {
|
||||
if let Some(origin) = &requirement.origin {
|
||||
sources.add(
|
||||
|
@ -104,7 +104,7 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
.constraints
|
||||
.requirements()
|
||||
.filter(|requirement| {
|
||||
requirement.evaluate_markers(self.marker_env.marker_environment(), &[])
|
||||
requirement.evaluate_markers(self.env.marker_environment(), &[])
|
||||
})
|
||||
{
|
||||
if let Some(origin) = &requirement.origin {
|
||||
|
@ -120,7 +120,7 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
.overrides
|
||||
.requirements()
|
||||
.filter(|requirement| {
|
||||
requirement.evaluate_markers(self.marker_env.marker_environment(), &[])
|
||||
requirement.evaluate_markers(self.env.marker_environment(), &[])
|
||||
})
|
||||
{
|
||||
if let Some(origin) = &requirement.origin {
|
||||
|
|
|
@ -28,7 +28,7 @@ use crate::resolution_mode::ResolutionStrategy;
|
|||
use crate::resolver::{Resolution, ResolutionDependencyEdge, ResolutionPackage};
|
||||
use crate::{
|
||||
InMemoryIndex, MetadataResponse, Options, PythonRequirement, RequiresPython, ResolveError,
|
||||
ResolverMarkers, VersionsResponse,
|
||||
VersionsResponse,
|
||||
};
|
||||
|
||||
pub(crate) type MarkersForDistribution = Vec<MarkerTree>;
|
||||
|
@ -124,7 +124,7 @@ impl ResolutionGraph {
|
|||
if package.is_base() {
|
||||
// For packages with diverging versions, store which version comes from which
|
||||
// fork.
|
||||
if let Some(markers) = resolution.markers.fork_markers() {
|
||||
if let Some(markers) = resolution.env.try_markers() {
|
||||
package_markers
|
||||
.entry(package.name.clone())
|
||||
.or_default()
|
||||
|
@ -152,11 +152,7 @@ impl ResolutionGraph {
|
|||
|
||||
let mut seen = FxHashSet::default();
|
||||
for resolution in resolutions {
|
||||
let marker = resolution
|
||||
.markers
|
||||
.fork_markers()
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let marker = resolution.env.try_markers().cloned().unwrap_or_default();
|
||||
|
||||
// Add every edge to the graph, propagating the marker for the current fork, if
|
||||
// necessary.
|
||||
|
@ -180,32 +176,31 @@ impl ResolutionGraph {
|
|||
let requires_python = python.target().clone();
|
||||
|
||||
let fork_markers = if let [resolution] = resolutions {
|
||||
match resolution.markers {
|
||||
ResolverMarkers::Universal { .. } | ResolverMarkers::SpecificEnvironment(_) => {
|
||||
vec![]
|
||||
}
|
||||
ResolverMarkers::Fork(_) => {
|
||||
resolution
|
||||
.env
|
||||
.try_markers()
|
||||
.map(|_| {
|
||||
resolutions
|
||||
.iter()
|
||||
.map(|resolution| {
|
||||
resolution
|
||||
.markers
|
||||
.fork_markers()
|
||||
.env
|
||||
.try_markers()
|
||||
.expect("A non-forking resolution exists in forking mode")
|
||||
.clone()
|
||||
})
|
||||
// Any unsatisfiable forks were skipped.
|
||||
.filter(|fork| !fork.is_false())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(Vec::new)
|
||||
} else {
|
||||
resolutions
|
||||
.iter()
|
||||
.map(|resolution| {
|
||||
resolution
|
||||
.markers
|
||||
.fork_markers()
|
||||
.env
|
||||
.try_markers()
|
||||
.expect("A non-forking resolution exists in forking mode")
|
||||
.clone()
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc_hash::FxHashSet;
|
|||
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::{DependencyMode, Manifest, ResolverMarkers};
|
||||
use crate::{DependencyMode, Manifest, ResolverEnvironment};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
|
@ -46,7 +46,7 @@ impl ResolutionStrategy {
|
|||
pub(crate) fn from_mode(
|
||||
mode: ResolutionMode,
|
||||
manifest: &Manifest,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
dependencies: DependencyMode,
|
||||
) -> Self {
|
||||
match mode {
|
||||
|
@ -54,7 +54,7 @@ impl ResolutionStrategy {
|
|||
ResolutionMode::Lowest => Self::Lowest,
|
||||
ResolutionMode::LowestDirect => Self::LowestDirect(
|
||||
manifest
|
||||
.user_requirements(markers, dependencies)
|
||||
.user_requirements(env, dependencies)
|
||||
.map(|requirement| requirement.name.clone())
|
||||
.collect(),
|
||||
),
|
||||
|
|
|
@ -9,7 +9,9 @@ use tracing::{debug, trace};
|
|||
use crate::candidate_selector::CandidateSelector;
|
||||
use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner};
|
||||
use crate::resolver::Request;
|
||||
use crate::{InMemoryIndex, PythonRequirement, ResolveError, ResolverMarkers, VersionsResponse};
|
||||
use crate::{
|
||||
InMemoryIndex, PythonRequirement, ResolveError, ResolverEnvironment, VersionsResponse,
|
||||
};
|
||||
use uv_distribution_types::{CompatibleDist, DistributionMetadata, IndexCapabilities, IndexUrl};
|
||||
use uv_pep440::Version;
|
||||
|
||||
|
@ -55,7 +57,7 @@ impl BatchPrefetcher {
|
|||
in_memory: &InMemoryIndex,
|
||||
capabilities: &IndexCapabilities,
|
||||
selector: &CandidateSelector,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> anyhow::Result<(), ResolveError> {
|
||||
let PubGrubPackageInner::Package {
|
||||
name,
|
||||
|
@ -102,7 +104,7 @@ impl BatchPrefetcher {
|
|||
previous,
|
||||
} => {
|
||||
if let Some(candidate) =
|
||||
selector.select_no_preference(name, &compatible, version_map, markers)
|
||||
selector.select_no_preference(name, &compatible, version_map, env)
|
||||
{
|
||||
let compatible = compatible.intersection(
|
||||
&Range::singleton(candidate.version().clone()).complement(),
|
||||
|
@ -137,7 +139,7 @@ impl BatchPrefetcher {
|
|||
};
|
||||
}
|
||||
if let Some(candidate) =
|
||||
selector.select_no_preference(name, &range, version_map, markers)
|
||||
selector.select_no_preference(name, &range, version_map, env)
|
||||
{
|
||||
phase = BatchPrefetchStrategy::InOrder {
|
||||
previous: candidate.version().clone(),
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc_hash::FxHashMap;
|
|||
use uv_pep508::{MarkerTree, PackageName};
|
||||
use uv_pypi_types::Requirement;
|
||||
|
||||
use crate::ResolverMarkers;
|
||||
use crate::ResolverEnvironment;
|
||||
|
||||
/// A set of package names associated with a given fork.
|
||||
pub(crate) type ForkSet = ForkMap<()>;
|
||||
|
@ -40,8 +40,8 @@ impl<T> ForkMap<T> {
|
|||
|
||||
/// Returns `true` if the map contains any values for a package that are compatible with the
|
||||
/// given fork.
|
||||
pub(crate) fn contains(&self, package_name: &PackageName, markers: &ResolverMarkers) -> bool {
|
||||
!self.get(package_name, markers).is_empty()
|
||||
pub(crate) fn contains(&self, package_name: &PackageName, env: &ResolverEnvironment) -> bool {
|
||||
!self.get(package_name, env).is_empty()
|
||||
}
|
||||
|
||||
/// Returns `true` if the map contains any values for a package.
|
||||
|
@ -54,27 +54,14 @@ impl<T> ForkMap<T> {
|
|||
/// Compatibility implies that the markers on the requirement that contained this value
|
||||
/// are not disjoint with the given fork. Note that this does not imply that the requirement
|
||||
/// diverged in the given fork - values from overlapping forks may be combined.
|
||||
pub(crate) fn get(&self, package_name: &PackageName, markers: &ResolverMarkers) -> Vec<&T> {
|
||||
pub(crate) fn get(&self, package_name: &PackageName, env: &ResolverEnvironment) -> Vec<&T> {
|
||||
let Some(values) = self.0.get(package_name) else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
match markers {
|
||||
// If we are solving for a specific environment we already filtered
|
||||
// compatible requirements `from_manifest`.
|
||||
//
|
||||
// Or, if we haven't forked yet, all values are potentially compatible.
|
||||
ResolverMarkers::SpecificEnvironment(_) | ResolverMarkers::Universal { .. } => {
|
||||
values.iter().map(|entry| &entry.value).collect()
|
||||
}
|
||||
|
||||
// Return all values that were requested with markers that are compatible
|
||||
// with the current fork, i.e. the markers are not disjoint.
|
||||
ResolverMarkers::Fork(fork) => values
|
||||
.iter()
|
||||
.filter(|entry| !fork.is_disjoint(&entry.marker))
|
||||
.map(|entry| &entry.value)
|
||||
.collect(),
|
||||
}
|
||||
values
|
||||
.iter()
|
||||
.filter(|entry| env.included(&entry.marker))
|
||||
.map(|entry| &entry.value)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc_hash::FxHashMap;
|
|||
|
||||
use uv_normalize::{GroupName, PackageName};
|
||||
|
||||
use crate::{Manifest, ResolverMarkers};
|
||||
use crate::{Manifest, ResolverEnvironment};
|
||||
|
||||
/// A map of package names to their activated dependency groups.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -10,13 +10,13 @@ pub(crate) struct Groups(FxHashMap<PackageName, Vec<GroupName>>);
|
|||
|
||||
impl Groups {
|
||||
/// Determine the set of enabled dependency groups in the [`Manifest`].
|
||||
pub(crate) fn from_manifest(manifest: &Manifest, markers: &ResolverMarkers) -> Self {
|
||||
pub(crate) fn from_manifest(manifest: &Manifest, env: &ResolverEnvironment) -> Self {
|
||||
let mut groups = FxHashMap::default();
|
||||
|
||||
// Enable the groups for all direct dependencies. In practice, this tends to mean: when
|
||||
// development dependencies are enabled, enable them for all direct dependencies.
|
||||
for group in &manifest.dev {
|
||||
for requirement in manifest.direct_requirements(markers) {
|
||||
for requirement in manifest.direct_requirements(env) {
|
||||
groups
|
||||
.entry(requirement.name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
|
|
|
@ -4,7 +4,7 @@ use uv_pep508::VerbatimUrl;
|
|||
use uv_pypi_types::RequirementSource;
|
||||
|
||||
use crate::resolver::ForkMap;
|
||||
use crate::{DependencyMode, Manifest, ResolverMarkers};
|
||||
use crate::{DependencyMode, Manifest, ResolverEnvironment};
|
||||
|
||||
/// A map of package names to their explicit index.
|
||||
///
|
||||
|
@ -26,12 +26,12 @@ impl Indexes {
|
|||
/// Determine the set of explicit, pinned indexes in the [`Manifest`].
|
||||
pub(crate) fn from_manifest(
|
||||
manifest: &Manifest,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
dependencies: DependencyMode,
|
||||
) -> Self {
|
||||
let mut indexes = ForkMap::default();
|
||||
|
||||
for requirement in manifest.requirements(markers, dependencies) {
|
||||
for requirement in manifest.requirements(env, dependencies) {
|
||||
let RequirementSource::Registry {
|
||||
index: Some(index), ..
|
||||
} = &requirement.source
|
||||
|
@ -54,8 +54,8 @@ impl Indexes {
|
|||
pub(crate) fn get(
|
||||
&self,
|
||||
package_name: &PackageName,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Vec<&IndexUrl> {
|
||||
self.0.get(package_name, markers)
|
||||
self.0.get(package_name, env)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use uv_pep508::PackageName;
|
|||
use uv_pypi_types::RequirementSource;
|
||||
|
||||
use crate::resolver::ForkMap;
|
||||
use crate::{DependencyMode, Manifest, ResolverMarkers};
|
||||
use crate::{DependencyMode, Manifest, ResolverEnvironment};
|
||||
|
||||
/// A map of package names to their associated, required local versions across all forks.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -17,14 +17,14 @@ impl Locals {
|
|||
/// Determine the set of permitted local versions in the [`Manifest`].
|
||||
pub(crate) fn from_manifest(
|
||||
manifest: &Manifest,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
dependencies: DependencyMode,
|
||||
) -> Self {
|
||||
let mut locals = ForkMap::default();
|
||||
|
||||
// Add all direct requirements and constraints. There's no need to look for conflicts,
|
||||
// since conflicts will be enforced by the solver.
|
||||
for requirement in manifest.requirements(markers, dependencies) {
|
||||
for requirement in manifest.requirements(env, dependencies) {
|
||||
if let Some(local) = from_source(&requirement.source) {
|
||||
locals.add(&requirement, local);
|
||||
}
|
||||
|
@ -37,9 +37,9 @@ impl Locals {
|
|||
pub(crate) fn get(
|
||||
&self,
|
||||
package_name: &PackageName,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
) -> Vec<&Version> {
|
||||
self.0.get(package_name, markers)
|
||||
self.0.get(package_name, env)
|
||||
}
|
||||
|
||||
/// Given a specifier that may include the version _without_ a local segment, return a specifier
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Given a set of requirements, find a set of compatible packages.
|
||||
|
||||
#![allow(warnings)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
|
@ -20,9 +22,9 @@ use tokio::sync::oneshot;
|
|||
use tokio_stream::wrappers::ReceiverStream;
|
||||
use tracing::{debug, info, instrument, trace, warn, Level};
|
||||
|
||||
pub use environment::ResolverEnvironment;
|
||||
pub(crate) use fork_map::{ForkMap, ForkSet};
|
||||
use locals::Locals;
|
||||
pub use resolver_markers::ResolverMarkers;
|
||||
pub(crate) use urls::Urls;
|
||||
use uv_configuration::{Constraints, Overrides};
|
||||
use uv_distribution::{ArchiveMetadata, DistributionDatabase};
|
||||
|
@ -74,6 +76,7 @@ use crate::{marker, DependencyMode, Exclusions, FlatIndex, Options};
|
|||
|
||||
mod availability;
|
||||
mod batch_prefetch;
|
||||
mod environment;
|
||||
mod fork_map;
|
||||
mod groups;
|
||||
mod index;
|
||||
|
@ -81,7 +84,6 @@ mod indexes;
|
|||
mod locals;
|
||||
mod provider;
|
||||
mod reporter;
|
||||
mod resolver_markers;
|
||||
mod urls;
|
||||
|
||||
pub struct Resolver<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider> {
|
||||
|
@ -107,7 +109,7 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
|
|||
indexes: Indexes,
|
||||
dependency_mode: DependencyMode,
|
||||
hasher: HashStrategy,
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
python_requirement: PythonRequirement,
|
||||
workspace_members: BTreeSet<PackageName>,
|
||||
selector: CandidateSelector,
|
||||
|
@ -148,7 +150,7 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
|
|||
manifest: Manifest,
|
||||
options: Options,
|
||||
python_requirement: &'a PythonRequirement,
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
tags: Option<&'a Tags>,
|
||||
flat_index: &'a FlatIndex,
|
||||
index: &'a InMemoryIndex,
|
||||
|
@ -162,7 +164,7 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
|
|||
flat_index,
|
||||
tags,
|
||||
python_requirement.target(),
|
||||
AllowedYanks::from_manifest(&manifest, &markers, options.dependency_mode),
|
||||
AllowedYanks::from_manifest(&manifest, &env, options.dependency_mode),
|
||||
hasher,
|
||||
options.exclude_newer,
|
||||
build_context.build_options(),
|
||||
|
@ -173,7 +175,7 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
|
|||
manifest,
|
||||
options,
|
||||
hasher,
|
||||
markers,
|
||||
env,
|
||||
python_requirement,
|
||||
index,
|
||||
build_context.git(),
|
||||
|
@ -193,7 +195,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
manifest: Manifest,
|
||||
options: Options,
|
||||
hasher: &HashStrategy,
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
python_requirement: &PythonRequirement,
|
||||
index: &InMemoryIndex,
|
||||
git: &GitResolver,
|
||||
|
@ -206,12 +208,12 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
index: index.clone(),
|
||||
git: git.clone(),
|
||||
capabilities: capabilities.clone(),
|
||||
selector: CandidateSelector::for_resolution(options, &manifest, &markers),
|
||||
selector: CandidateSelector::for_resolution(options, &manifest, &env),
|
||||
dependency_mode: options.dependency_mode,
|
||||
urls: Urls::from_manifest(&manifest, &markers, git, options.dependency_mode)?,
|
||||
locals: Locals::from_manifest(&manifest, &markers, options.dependency_mode),
|
||||
indexes: Indexes::from_manifest(&manifest, &markers, options.dependency_mode),
|
||||
groups: Groups::from_manifest(&manifest, &markers),
|
||||
urls: Urls::from_manifest(&manifest, &env, git, options.dependency_mode)?,
|
||||
locals: Locals::from_manifest(&manifest, &env, options.dependency_mode),
|
||||
indexes: Indexes::from_manifest(&manifest, &env, options.dependency_mode),
|
||||
groups: Groups::from_manifest(&manifest, &env),
|
||||
project: manifest.project,
|
||||
workspace_members: manifest.workspace_members,
|
||||
requirements: manifest.requirements,
|
||||
|
@ -221,7 +223,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
exclusions: manifest.exclusions,
|
||||
hasher: hasher.clone(),
|
||||
locations: locations.clone(),
|
||||
markers,
|
||||
env,
|
||||
python_requirement: python_requirement.clone(),
|
||||
installed_packages,
|
||||
unavailable_packages: DashMap::default(),
|
||||
|
@ -304,32 +306,17 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
let state = ForkState::new(
|
||||
State::init(root.clone(), MIN_VERSION.clone()),
|
||||
root,
|
||||
self.markers.clone(),
|
||||
self.env.clone(),
|
||||
self.python_requirement.clone(),
|
||||
);
|
||||
let mut preferences = self.preferences.clone();
|
||||
let mut forked_states =
|
||||
if let ResolverMarkers::Universal { fork_preferences } = &self.markers {
|
||||
if fork_preferences.is_empty() {
|
||||
vec![state]
|
||||
} else {
|
||||
fork_preferences
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|fork_preference| {
|
||||
state.clone().with_markers(fork_preference.clone())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
} else {
|
||||
vec![state]
|
||||
};
|
||||
let mut forked_states = self.env.initial_forked_states(state);
|
||||
let mut resolutions = vec![];
|
||||
|
||||
'FORK: while let Some(mut state) = forked_states.pop() {
|
||||
if let ResolverMarkers::Fork(markers) = &state.markers {
|
||||
if let Some(split) = state.env.end_user_fork_display() {
|
||||
let requires_python = state.python_requirement.target();
|
||||
debug!("Solving split {markers:?} (requires-python: {requires_python:?})");
|
||||
debug!("Solving {split} (requires-python: {requires_python:?})");
|
||||
}
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
|
@ -339,7 +326,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
err,
|
||||
state.fork_urls,
|
||||
&state.fork_indexes,
|
||||
state.markers,
|
||||
state.env,
|
||||
&visited,
|
||||
&self.locations,
|
||||
&self.capabilities,
|
||||
|
@ -367,8 +354,8 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
prefetcher.log_tried_versions();
|
||||
}
|
||||
debug!(
|
||||
"Split {} resolution took {:.3}s",
|
||||
state.markers,
|
||||
"{} resolution took {:.3}s",
|
||||
state.env,
|
||||
start.elapsed().as_secs_f32()
|
||||
);
|
||||
|
||||
|
@ -380,7 +367,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
for (package, version) in &resolution.nodes {
|
||||
preferences.insert(
|
||||
package.name.clone(),
|
||||
resolution.markers.fork_markers().cloned(),
|
||||
resolution.env.try_markers().cloned(),
|
||||
version.clone(),
|
||||
);
|
||||
}
|
||||
|
@ -423,7 +410,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&mut state.pins,
|
||||
&preferences,
|
||||
&state.fork_urls,
|
||||
&state.markers,
|
||||
&state.env,
|
||||
&state.python_requirement,
|
||||
&mut visited,
|
||||
&request_sink,
|
||||
|
@ -488,7 +475,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&self.index,
|
||||
&self.capabilities,
|
||||
&self.selector,
|
||||
&state.markers,
|
||||
&state.env,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -519,7 +506,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&state.next,
|
||||
&version,
|
||||
&state.fork_urls,
|
||||
&state.markers,
|
||||
&state.env,
|
||||
&state.python_requirement,
|
||||
)?;
|
||||
match forked_deps {
|
||||
|
@ -563,8 +550,8 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
diverging_packages,
|
||||
} => {
|
||||
debug!(
|
||||
"Pre-fork split {} took {:.3}s",
|
||||
state.markers,
|
||||
"Pre-fork {} took {:.3}s",
|
||||
state.env,
|
||||
start.elapsed().as_secs_f32()
|
||||
);
|
||||
|
||||
|
@ -590,9 +577,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
);
|
||||
}
|
||||
for resolution in &resolutions {
|
||||
if let Some(markers) = resolution.markers.fork_markers() {
|
||||
if let Some(env) = resolution.env.end_user_fork_display() {
|
||||
debug!(
|
||||
"Distinct solution for ({markers:?}) with {} packages",
|
||||
"Distinct solution for {env} with {} packages",
|
||||
resolution.nodes.len()
|
||||
);
|
||||
}
|
||||
|
@ -623,11 +610,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
if !tracing::enabled!(Level::TRACE) {
|
||||
return;
|
||||
}
|
||||
if let Some(markers) = combined.markers.fork_markers() {
|
||||
trace!("Resolution: {:?}", markers);
|
||||
} else {
|
||||
trace!("Resolution: <matches all marker environments>");
|
||||
}
|
||||
trace!("Resolution: {:?}", combined.env);
|
||||
for edge in &combined.edges {
|
||||
trace!(
|
||||
"Resolution edge: {} -> {}",
|
||||
|
@ -703,7 +686,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
}
|
||||
|
||||
let markers = fork.markers.clone();
|
||||
Some((fork, forked_state.with_markers(markers)?))
|
||||
Some((fork, forked_state.with_env(&markers)?))
|
||||
})
|
||||
.map(move |(fork, mut forked_state)| {
|
||||
forked_state.add_package_version_dependencies(
|
||||
|
@ -738,7 +721,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
// match any marker environments.
|
||||
.filter(|result| {
|
||||
if let Ok(ref forked_state) = result {
|
||||
let markers = forked_state.markers.fork_markers().expect("is a fork");
|
||||
let markers = forked_state.env.try_markers().expect("is a fork");
|
||||
if markers.is_false() {
|
||||
return false;
|
||||
}
|
||||
|
@ -862,7 +845,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
pins: &mut FilePins,
|
||||
preferences: &Preferences,
|
||||
fork_urls: &ForkUrls,
|
||||
fork_markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
python_requirement: &PythonRequirement,
|
||||
visited: &mut FxHashSet<PackageName>,
|
||||
request_sink: &Sender<Request>,
|
||||
|
@ -892,7 +875,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
range,
|
||||
package,
|
||||
preferences,
|
||||
fork_markers,
|
||||
env,
|
||||
python_requirement,
|
||||
pins,
|
||||
visited,
|
||||
|
@ -1011,7 +994,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
range: &Range<Version>,
|
||||
package: &PubGrubPackage,
|
||||
preferences: &Preferences,
|
||||
fork_markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
python_requirement: &PythonRequirement,
|
||||
pins: &mut FilePins,
|
||||
visited: &mut FxHashSet<PackageName>,
|
||||
|
@ -1060,7 +1043,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
preferences,
|
||||
&self.installed_packages,
|
||||
&self.exclusions,
|
||||
fork_markers,
|
||||
env,
|
||||
) else {
|
||||
// Short circuit: we couldn't find _any_ versions for a package.
|
||||
return Ok(None);
|
||||
|
@ -1186,21 +1169,19 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
package: &PubGrubPackage,
|
||||
version: &Version,
|
||||
fork_urls: &ForkUrls,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
python_requirement: &PythonRequirement,
|
||||
) -> Result<ForkedDependencies, ResolveError> {
|
||||
let result =
|
||||
self.get_dependencies(package, version, fork_urls, markers, python_requirement);
|
||||
match markers {
|
||||
ResolverMarkers::SpecificEnvironment(_) => result.map(|deps| match deps {
|
||||
let result = self.get_dependencies(package, version, fork_urls, env, python_requirement);
|
||||
if env.marker_environment().is_some() {
|
||||
result.map(|deps| match deps {
|
||||
Dependencies::Available(deps) | Dependencies::Unforkable(deps) => {
|
||||
ForkedDependencies::Unforked(deps)
|
||||
}
|
||||
Dependencies::Unavailable(err) => ForkedDependencies::Unavailable(err),
|
||||
}),
|
||||
ResolverMarkers::Universal { .. } | ResolverMarkers::Fork(_) => {
|
||||
Ok(result?.fork(python_requirement))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Ok(result?.fork(python_requirement))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1211,7 +1192,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
package: &PubGrubPackage,
|
||||
version: &Version,
|
||||
fork_urls: &ForkUrls,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
python_requirement: &PythonRequirement,
|
||||
) -> Result<Dependencies, ResolveError> {
|
||||
let url = package.name().and_then(|name| fork_urls.get(name));
|
||||
|
@ -1224,7 +1205,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
None,
|
||||
None,
|
||||
None,
|
||||
markers,
|
||||
env,
|
||||
python_requirement,
|
||||
);
|
||||
|
||||
|
@ -1357,7 +1338,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
extra.as_ref(),
|
||||
dev.as_ref(),
|
||||
Some(name),
|
||||
markers,
|
||||
env,
|
||||
python_requirement,
|
||||
);
|
||||
|
||||
|
@ -1481,7 +1462,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
extra: Option<&'a ExtraName>,
|
||||
dev: Option<&'a GroupName>,
|
||||
name: Option<&PackageName>,
|
||||
markers: &'a ResolverMarkers,
|
||||
env: &'a ResolverEnvironment,
|
||||
python_requirement: &'a PythonRequirement,
|
||||
) -> Vec<Cow<'a, Requirement>> {
|
||||
// Start with the requirements for the current extra of the package (for an extra
|
||||
|
@ -1493,12 +1474,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
Either::Right(dependencies.iter())
|
||||
};
|
||||
let mut requirements = self
|
||||
.requirements_for_extra(
|
||||
regular_and_dev_dependencies,
|
||||
extra,
|
||||
markers,
|
||||
python_requirement,
|
||||
)
|
||||
.requirements_for_extra(regular_and_dev_dependencies, extra, env, python_requirement)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Check if there are recursive self inclusions and we need to go into the expensive branch.
|
||||
|
@ -1522,7 +1498,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
continue;
|
||||
}
|
||||
for requirement in
|
||||
self.requirements_for_extra(dependencies, Some(&extra), markers, python_requirement)
|
||||
self.requirements_for_extra(dependencies, Some(&extra), env, python_requirement)
|
||||
{
|
||||
if name == Some(&requirement.name) {
|
||||
// Add each transitively included extra.
|
||||
|
@ -1546,7 +1522,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&'data self,
|
||||
dependencies: impl IntoIterator<Item = &'data Requirement> + 'parameters,
|
||||
extra: Option<&'parameters ExtraName>,
|
||||
markers: &'parameters ResolverMarkers,
|
||||
env: &'parameters ResolverEnvironment,
|
||||
python_requirement: &'parameters PythonRequirement,
|
||||
) -> impl Iterator<Item = Cow<'data, Requirement>> + 'parameters
|
||||
where
|
||||
|
@ -1568,29 +1544,27 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
|
||||
// If we're in a fork in universal mode, ignore any dependency that isn't part of
|
||||
// this fork (but will be part of another fork).
|
||||
if let ResolverMarkers::Fork(markers) = markers {
|
||||
if markers.is_disjoint(&requirement.marker) {
|
||||
trace!("skipping {requirement} because of context resolver markers {markers:?}");
|
||||
return None;
|
||||
}
|
||||
if !env.included(&requirement.marker) {
|
||||
trace!("skipping {requirement} because of {env}");
|
||||
return None;
|
||||
}
|
||||
|
||||
// If the requirement isn't relevant for the current platform, skip it.
|
||||
match extra {
|
||||
Some(source_extra) => {
|
||||
// Only include requirements that are relevant for the current extra.
|
||||
if requirement.evaluate_markers(markers.marker_environment(), &[]) {
|
||||
if requirement.evaluate_markers(env.marker_environment(), &[]) {
|
||||
return None;
|
||||
}
|
||||
if !requirement.evaluate_markers(
|
||||
markers.marker_environment(),
|
||||
env.marker_environment(),
|
||||
std::slice::from_ref(source_extra),
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !requirement.evaluate_markers(markers.marker_environment(), &[]) {
|
||||
if !requirement.evaluate_markers(env.marker_environment(), &[]) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -1662,25 +1636,23 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
|
||||
// If we're in a fork in universal mode, ignore any dependency that isn't part of
|
||||
// this fork (but will be part of another fork).
|
||||
if let ResolverMarkers::Fork(markers) = markers {
|
||||
if markers.is_disjoint(&constraint.marker) {
|
||||
trace!("skipping {constraint} because of context resolver markers {markers:?}");
|
||||
return None;
|
||||
}
|
||||
if !env.included(&constraint.marker) {
|
||||
trace!("skipping {constraint} because of {env}");
|
||||
return None;
|
||||
}
|
||||
|
||||
// If the constraint isn't relevant for the current platform, skip it.
|
||||
match extra {
|
||||
Some(source_extra) => {
|
||||
if !constraint.evaluate_markers(
|
||||
markers.marker_environment(),
|
||||
env.marker_environment(),
|
||||
std::slice::from_ref(source_extra),
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !constraint.evaluate_markers(markers.marker_environment(), &[]) {
|
||||
if !constraint.evaluate_markers(env.marker_environment(), &[]) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -1871,7 +1843,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&self.exclusions,
|
||||
// We don't have access to the fork state when prefetching, so assume that
|
||||
// pre-release versions are allowed.
|
||||
&ResolverMarkers::universal(vec![]),
|
||||
&ResolverEnvironment::universal(vec![]),
|
||||
) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
@ -1975,7 +1947,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
mut err: pubgrub::NoSolutionError<UvDependencyProvider>,
|
||||
fork_urls: ForkUrls,
|
||||
fork_indexes: &ForkIndexes,
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
visited: &FxHashSet<PackageName>,
|
||||
index_locations: &IndexLocations,
|
||||
index_capabilities: &IndexCapabilities,
|
||||
|
@ -2056,7 +2028,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
unavailable_packages,
|
||||
incomplete_packages,
|
||||
fork_urls,
|
||||
markers,
|
||||
env,
|
||||
self.workspace_members.clone(),
|
||||
self.options,
|
||||
))
|
||||
|
@ -2141,7 +2113,7 @@ struct ForkState {
|
|||
/// completely disjoint marker expression (i.e., it can never be true given
|
||||
/// that the marker expression that provoked the fork is true), then that
|
||||
/// dependency is completely ignored.
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
/// The Python requirement for this fork. Defaults to the Python requirement for
|
||||
/// the resolution, but may be narrowed if a `python_version` marker is present
|
||||
/// in a given fork.
|
||||
|
@ -2161,7 +2133,7 @@ impl ForkState {
|
|||
fn new(
|
||||
pubgrub: State<UvDependencyProvider>,
|
||||
root: PubGrubPackage,
|
||||
markers: ResolverMarkers,
|
||||
env: ResolverEnvironment,
|
||||
python_requirement: PythonRequirement,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -2172,7 +2144,7 @@ impl ForkState {
|
|||
fork_indexes: ForkIndexes::default(),
|
||||
priorities: PubGrubPriorities::default(),
|
||||
added_dependencies: FxHashMap::default(),
|
||||
markers,
|
||||
env,
|
||||
python_requirement,
|
||||
}
|
||||
}
|
||||
|
@ -2204,15 +2176,15 @@ impl ForkState {
|
|||
// requirement was a URL requirement. `Urls` applies canonicalization to this and
|
||||
// override URLs to both URL and registry requirements, which we then check for
|
||||
// conflicts using [`ForkUrl`].
|
||||
if let Some(url) = urls.get_url(name, url.as_ref(), git)? {
|
||||
self.fork_urls.insert(name, url, &self.markers)?;
|
||||
if let Some(url) = urls.get_url(&self.env, name, url.as_ref(), git)? {
|
||||
self.fork_urls.insert(name, url, &self.env)?;
|
||||
has_url = true;
|
||||
};
|
||||
|
||||
// If the specifier is an exact version and the user requested a local version for this
|
||||
// fork that's more precise than the specifier, use the local version instead.
|
||||
if let Some(specifier) = specifier {
|
||||
let locals = locals.get(name, &self.markers);
|
||||
let locals = locals.get(name, &self.env);
|
||||
|
||||
// It's possible that there are multiple matching local versions requested with
|
||||
// different marker expressions. All of these are potentially compatible until we
|
||||
|
@ -2235,8 +2207,8 @@ impl ForkState {
|
|||
}
|
||||
|
||||
// If the package is pinned to an exact index, add it to the fork.
|
||||
for index in indexes.get(name, &self.markers) {
|
||||
self.fork_indexes.insert(name, index, &self.markers)?;
|
||||
for index in indexes.get(name, &self.env) {
|
||||
self.fork_indexes.insert(name, index, &self.env)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2321,29 +2293,18 @@ impl ForkState {
|
|||
|
||||
/// Subset the current markers with the new markers and update the python requirements fields
|
||||
/// accordingly.
|
||||
fn with_markers(mut self, markers: MarkerTree) -> Option<Self> {
|
||||
let combined_markers = self.markers.and(markers);
|
||||
let python_marker = self.python_requirement.to_marker_tree();
|
||||
if combined_markers.is_disjoint(&python_marker) {
|
||||
debug!(
|
||||
"Skipping split {combined_markers:?} \
|
||||
because of Python requirement {python_marker:?}",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
///
|
||||
/// If the fork should be dropped (e.g., because its markers can never be true for its
|
||||
/// Python requirement), then this returns `None`.
|
||||
fn with_env(mut self, markers: &MarkerTree) -> Option<Self> {
|
||||
self.env = self
|
||||
.env
|
||||
.narrow_environment(&self.python_requirement, markers)?;
|
||||
// If the fork contains a narrowed Python requirement, apply it.
|
||||
let python_requirement = marker::requires_python(&combined_markers)
|
||||
.and_then(|marker| self.python_requirement.narrow(&marker));
|
||||
if let Some(python_requirement) = python_requirement {
|
||||
debug!(
|
||||
"Narrowed `requires-python` bound to: {}",
|
||||
python_requirement.target()
|
||||
);
|
||||
self.python_requirement = python_requirement;
|
||||
if let Some(req) = self.env.narrow_python_requirement(&self.python_requirement) {
|
||||
debug!("Narrowed `requires-python` bound to: {}", req.target());
|
||||
self.python_requirement = req;
|
||||
}
|
||||
|
||||
self.markers = ResolverMarkers::Fork(combined_markers);
|
||||
Some(self)
|
||||
}
|
||||
|
||||
|
@ -2542,7 +2503,7 @@ impl ForkState {
|
|||
nodes,
|
||||
edges,
|
||||
pins: self.pins,
|
||||
markers: self.markers,
|
||||
env: self.env,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2556,8 +2517,8 @@ pub(crate) struct Resolution {
|
|||
pub(crate) edges: FxHashSet<ResolutionDependencyEdge>,
|
||||
/// Map each package name, version tuple from `packages` to a distribution.
|
||||
pub(crate) pins: FilePins,
|
||||
/// The marker setting this resolution was found under.
|
||||
pub(crate) markers: ResolverMarkers,
|
||||
/// The environment setting this resolution was found under.
|
||||
pub(crate) env: ResolverEnvironment,
|
||||
}
|
||||
|
||||
/// Package representation we used during resolution where each extra and also the dev-dependencies
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use uv_pep508::{MarkerEnvironment, MarkerTree};
|
||||
use uv_pypi_types::ResolverMarkerEnvironment;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Whether we're solving for a specific environment, universally or for a specific fork.
|
||||
pub enum ResolverMarkers {
|
||||
/// We're solving for this specific environment only.
|
||||
SpecificEnvironment(ResolverMarkerEnvironment),
|
||||
/// We're doing a universal resolution for all environments (a python version
|
||||
/// constraint is expressed separately).
|
||||
Universal {
|
||||
/// Start the resolution with these forks.
|
||||
fork_preferences: Vec<MarkerTree>,
|
||||
},
|
||||
/// We're in a fork of the universal resolution solving only for specific markers.
|
||||
Fork(MarkerTree),
|
||||
}
|
||||
|
||||
impl ResolverMarkers {
|
||||
/// Set the resolver to perform a resolution for a specific environment.
|
||||
pub fn specific_environment(markers: ResolverMarkerEnvironment) -> Self {
|
||||
Self::SpecificEnvironment(markers)
|
||||
}
|
||||
|
||||
/// Set the resolver to perform a universal resolution.
|
||||
pub fn universal(fork_preferences: Vec<MarkerTree>) -> Self {
|
||||
Self::Universal { fork_preferences }
|
||||
}
|
||||
|
||||
/// Add the markers of an initial or subsequent fork to the current markers.
|
||||
pub(crate) fn and(self, other: MarkerTree) -> MarkerTree {
|
||||
match self {
|
||||
ResolverMarkers::Universal { .. } => other,
|
||||
ResolverMarkers::Fork(mut current) => {
|
||||
current.and(other);
|
||||
current
|
||||
}
|
||||
ResolverMarkers::SpecificEnvironment(_) => {
|
||||
unreachable!("Specific environment mode must not fork")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If solving for a specific environment, return this environment.
|
||||
pub fn marker_environment(&self) -> Option<&MarkerEnvironment> {
|
||||
match self {
|
||||
ResolverMarkers::Universal { .. } | ResolverMarkers::Fork(_) => None,
|
||||
ResolverMarkers::SpecificEnvironment(env) => Some(env),
|
||||
}
|
||||
}
|
||||
|
||||
/// If solving a fork, return that fork's markers.
|
||||
pub fn fork_markers(&self) -> Option<&MarkerTree> {
|
||||
match self {
|
||||
ResolverMarkers::SpecificEnvironment(_) | ResolverMarkers::Universal { .. } => None,
|
||||
ResolverMarkers::Fork(markers) => Some(markers),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ResolverMarkers {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ResolverMarkers::Universal { .. } => f.write_str("universal"),
|
||||
ResolverMarkers::SpecificEnvironment(_) => f.write_str("specific environment"),
|
||||
ResolverMarkers::Fork(markers) => {
|
||||
write!(f, "({markers:?})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ use uv_normalize::PackageName;
|
|||
use uv_pep508::VerbatimUrl;
|
||||
use uv_pypi_types::{ParsedDirectoryUrl, ParsedUrl, VerbatimParsedUrl};
|
||||
|
||||
use crate::{DependencyMode, Manifest, ResolveError, ResolverMarkers};
|
||||
use crate::{DependencyMode, Manifest, ResolveError, ResolverEnvironment};
|
||||
|
||||
/// The URLs that are allowed for packages.
|
||||
///
|
||||
|
@ -36,7 +36,7 @@ pub(crate) struct Urls {
|
|||
impl Urls {
|
||||
pub(crate) fn from_manifest(
|
||||
manifest: &Manifest,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
git: &GitResolver,
|
||||
dependencies: DependencyMode,
|
||||
) -> Result<Self, ResolveError> {
|
||||
|
@ -44,7 +44,7 @@ impl Urls {
|
|||
let mut overrides: FxHashMap<PackageName, VerbatimParsedUrl> = FxHashMap::default();
|
||||
|
||||
// Add all direct regular requirements and constraints URL.
|
||||
for requirement in manifest.requirements_no_overrides(markers, dependencies) {
|
||||
for requirement in manifest.requirements_no_overrides(env, dependencies) {
|
||||
let Some(url) = requirement.source.to_verbatim_parsed_url() else {
|
||||
// Registry requirement
|
||||
continue;
|
||||
|
@ -77,7 +77,7 @@ impl Urls {
|
|||
|
||||
// Add all URLs from overrides. If there is an override URL, all other URLs from
|
||||
// requirements and constraints are moot and will be removed.
|
||||
for requirement in manifest.overrides(markers, dependencies) {
|
||||
for requirement in manifest.overrides(env, dependencies) {
|
||||
let Some(url) = requirement.source.to_verbatim_parsed_url() else {
|
||||
// Registry requirement
|
||||
continue;
|
||||
|
@ -112,6 +112,7 @@ impl Urls {
|
|||
/// if there is no override.
|
||||
pub(crate) fn get_url<'a>(
|
||||
&'a self,
|
||||
env: &ResolverEnvironment,
|
||||
name: &'a PackageName,
|
||||
url: Option<&'a VerbatimParsedUrl>,
|
||||
git: &'a GitResolver,
|
||||
|
@ -120,6 +121,7 @@ impl Urls {
|
|||
Ok(Some(override_url))
|
||||
} else if let Some(url) = url {
|
||||
Ok(Some(self.canonicalize_allowed_url(
|
||||
env,
|
||||
name,
|
||||
git,
|
||||
&url.verbatim,
|
||||
|
@ -150,6 +152,7 @@ impl Urls {
|
|||
/// Check if a URL is allowed (known), and if so, return its canonical form.
|
||||
fn canonicalize_allowed_url<'a>(
|
||||
&'a self,
|
||||
env: &ResolverEnvironment,
|
||||
package_name: &'a PackageName,
|
||||
git: &'a GitResolver,
|
||||
verbatim_url: &'a VerbatimUrl,
|
||||
|
@ -174,10 +177,11 @@ impl Urls {
|
|||
.chain(iter::once(verbatim_url.verbatim().to_string()))
|
||||
.collect();
|
||||
conflicting_urls.sort();
|
||||
return Err(ResolveError::ConflictingUrlsUniversal(
|
||||
package_name.clone(),
|
||||
conflicting_urls,
|
||||
));
|
||||
return Err(ResolveError::ConflictingUrls {
|
||||
package_name: package_name.clone(),
|
||||
urls: conflicting_urls,
|
||||
env: env.clone(),
|
||||
});
|
||||
};
|
||||
Ok(*allowed_url)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use uv_normalize::PackageName;
|
|||
use uv_pep440::Version;
|
||||
use uv_pypi_types::RequirementSource;
|
||||
|
||||
use crate::{DependencyMode, Manifest, ResolverMarkers};
|
||||
use crate::{DependencyMode, Manifest, ResolverEnvironment};
|
||||
|
||||
/// A set of package versions that are permitted, even if they're marked as yanked by the
|
||||
/// relevant index.
|
||||
|
@ -16,13 +16,13 @@ pub struct AllowedYanks(Arc<FxHashMap<PackageName, FxHashSet<Version>>>);
|
|||
impl AllowedYanks {
|
||||
pub fn from_manifest(
|
||||
manifest: &Manifest,
|
||||
markers: &ResolverMarkers,
|
||||
env: &ResolverEnvironment,
|
||||
dependencies: DependencyMode,
|
||||
) -> Self {
|
||||
let mut allowed_yanks = FxHashMap::<PackageName, FxHashSet<Version>>::default();
|
||||
|
||||
// Allow yanks for any pinned input requirements.
|
||||
for requirement in manifest.requirements(markers, dependencies) {
|
||||
for requirement in manifest.requirements(env, dependencies) {
|
||||
let RequirementSource::Registry { specifier, .. } = &requirement.source else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
@ -438,7 +438,7 @@ async fn build_package(
|
|||
build_constraints
|
||||
.iter()
|
||||
.map(|entry| (&entry.requirement, entry.hashes.as_slice())),
|
||||
Some(&interpreter.resolver_markers()),
|
||||
Some(&interpreter.resolver_marker_environment()),
|
||||
hash_checking,
|
||||
)?
|
||||
} else {
|
||||
|
|
|
@ -48,7 +48,7 @@ pub(crate) fn pip_check(
|
|||
)?;
|
||||
|
||||
// Determine the markers to use for resolution.
|
||||
let markers = environment.interpreter().resolver_markers();
|
||||
let markers = environment.interpreter().resolver_marker_environment();
|
||||
|
||||
// Run the diagnostics.
|
||||
let diagnostics: Vec<SitePackagesDiagnostic> =
|
||||
|
|
|
@ -34,7 +34,7 @@ use uv_requirements::{
|
|||
use uv_resolver::{
|
||||
AnnotationStyle, DependencyMode, DisplayResolutionGraph, ExcludeNewer, FlatIndex,
|
||||
InMemoryIndex, OptionsBuilder, PrereleaseMode, PythonRequirement, RequiresPython,
|
||||
ResolutionMode, ResolverMarkers,
|
||||
ResolutionMode, ResolverEnvironment,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||
use uv_warnings::warn_user;
|
||||
|
@ -251,15 +251,15 @@ pub(crate) async fn pip_compile(
|
|||
};
|
||||
|
||||
// Determine the environment for the resolution.
|
||||
let (tags, markers) = if universal {
|
||||
let (tags, resolver_env) = if universal {
|
||||
(
|
||||
None,
|
||||
ResolverMarkers::universal(environments.into_markers()),
|
||||
ResolverEnvironment::universal(environments.into_markers()),
|
||||
)
|
||||
} else {
|
||||
let (tags, markers) =
|
||||
let (tags, marker_env) =
|
||||
resolution_environment(python_version, python_platform, &interpreter)?;
|
||||
(Some(tags), ResolverMarkers::specific_environment(markers))
|
||||
(Some(tags), ResolverEnvironment::specific(marker_env))
|
||||
};
|
||||
|
||||
// Generate, but don't enforce hashes for the requirements.
|
||||
|
@ -392,7 +392,7 @@ pub(crate) async fn pip_compile(
|
|||
&Reinstall::None,
|
||||
&upgrade,
|
||||
tags.as_deref(),
|
||||
markers.clone(),
|
||||
resolver_env.clone(),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
@ -446,8 +446,8 @@ pub(crate) async fn pip_compile(
|
|||
}
|
||||
|
||||
if include_marker_expression {
|
||||
if let ResolverMarkers::SpecificEnvironment(markers) = &markers {
|
||||
let relevant_markers = resolution.marker_tree(&top_level_index, markers)?;
|
||||
if let Some(marker_env) = resolver_env.marker_environment() {
|
||||
let relevant_markers = resolution.marker_tree(&top_level_index, marker_env)?;
|
||||
if let Some(relevant_markers) = relevant_markers.contents() {
|
||||
writeln!(
|
||||
writer,
|
||||
|
@ -524,7 +524,7 @@ pub(crate) async fn pip_compile(
|
|||
"{}",
|
||||
DisplayResolutionGraph::new(
|
||||
&resolution,
|
||||
&markers,
|
||||
&resolver_env,
|
||||
&no_emit_packages,
|
||||
generate_hashes,
|
||||
include_extras,
|
||||
|
|
|
@ -64,7 +64,7 @@ pub(crate) fn pip_freeze(
|
|||
// Validate that the environment is consistent.
|
||||
if strict {
|
||||
// Determine the markers to use for resolution.
|
||||
let markers = environment.interpreter().resolver_markers();
|
||||
let markers = environment.interpreter().resolver_marker_environment();
|
||||
|
||||
for diagnostic in site_packages.diagnostics(&markers)? {
|
||||
writeln!(
|
||||
|
|
|
@ -27,7 +27,7 @@ use uv_python::{
|
|||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_resolver::{
|
||||
DependencyMode, ExcludeNewer, FlatIndex, OptionsBuilder, PrereleaseMode, PythonRequirement,
|
||||
ResolutionMode, ResolverMarkers,
|
||||
ResolutionMode, ResolverEnvironment,
|
||||
};
|
||||
use uv_types::{BuildIsolation, HashStrategy};
|
||||
|
||||
|
@ -191,7 +191,7 @@ pub(crate) async fn pip_install(
|
|||
|
||||
// Determine the markers to use for the resolution.
|
||||
let interpreter = environment.interpreter();
|
||||
let markers = resolution_markers(
|
||||
let marker_env = resolution_markers(
|
||||
python_version.as_ref(),
|
||||
python_platform.as_ref(),
|
||||
interpreter,
|
||||
|
@ -209,7 +209,7 @@ pub(crate) async fn pip_install(
|
|||
&& overrides.is_empty()
|
||||
&& matches!(modifications, Modifications::Sufficient)
|
||||
{
|
||||
match site_packages.satisfies(&requirements, &constraints, &markers)? {
|
||||
match site_packages.satisfies(&requirements, &constraints, &marker_env)? {
|
||||
// If the requirements are already satisfied, we're done.
|
||||
SatisfiesResult::Fresh {
|
||||
recursive_requirements,
|
||||
|
@ -260,7 +260,7 @@ pub(crate) async fn pip_install(
|
|||
constraints
|
||||
.iter()
|
||||
.map(|entry| (&entry.requirement, entry.hashes.as_slice())),
|
||||
Some(&markers),
|
||||
Some(&marker_env),
|
||||
hash_checking,
|
||||
)?
|
||||
} else {
|
||||
|
@ -334,7 +334,7 @@ pub(crate) async fn pip_install(
|
|||
build_constraints
|
||||
.iter()
|
||||
.map(|entry| (&entry.requirement, entry.hashes.as_slice())),
|
||||
Some(&markers),
|
||||
Some(&marker_env),
|
||||
HashCheckingMode::Verify,
|
||||
)?
|
||||
} else {
|
||||
|
@ -398,7 +398,7 @@ pub(crate) async fn pip_install(
|
|||
&reinstall,
|
||||
&upgrade,
|
||||
Some(&tags),
|
||||
ResolverMarkers::specific_environment(markers.clone()),
|
||||
ResolverEnvironment::specific(marker_env.clone()),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
@ -457,7 +457,7 @@ pub(crate) async fn pip_install(
|
|||
|
||||
// Notify the user of any environment diagnostics.
|
||||
if strict && !dry_run {
|
||||
operations::diagnose_environment(&resolution, &environment, &markers, printer)?;
|
||||
operations::diagnose_environment(&resolution, &environment, &marker_env, printer)?;
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
|
|
|
@ -112,7 +112,7 @@ pub(crate) fn pip_list(
|
|||
// Validate that the environment is consistent.
|
||||
if strict {
|
||||
// Determine the markers to use for resolution.
|
||||
let markers = environment.interpreter().resolver_markers();
|
||||
let markers = environment.interpreter().resolver_marker_environment();
|
||||
|
||||
for diagnostic in site_packages.diagnostics(&markers)? {
|
||||
writeln!(
|
||||
|
|
|
@ -32,7 +32,7 @@ pub(crate) fn resolution_markers(
|
|||
(None, Some(python_version)) => {
|
||||
ResolverMarkerEnvironment::from(python_version.markers(interpreter.markers()))
|
||||
}
|
||||
(None, None) => interpreter.resolver_markers(),
|
||||
(None, None) => interpreter.resolver_marker_environment(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ pub(crate) fn resolution_environment(
|
|||
(None, Some(python_version)) => {
|
||||
ResolverMarkerEnvironment::from(python_version.markers(interpreter.markers()))
|
||||
}
|
||||
(None, None) => interpreter.resolver_markers(),
|
||||
(None, None) => interpreter.resolver_marker_environment(),
|
||||
};
|
||||
|
||||
Ok((tags, markers))
|
||||
|
|
|
@ -37,7 +37,7 @@ use uv_requirements::{
|
|||
};
|
||||
use uv_resolver::{
|
||||
DependencyMode, Exclusions, FlatIndex, InMemoryIndex, Manifest, Options, Preference,
|
||||
Preferences, PythonRequirement, ResolutionGraph, Resolver, ResolverMarkers,
|
||||
Preferences, PythonRequirement, ResolutionGraph, Resolver, ResolverEnvironment,
|
||||
};
|
||||
use uv_types::{HashStrategy, InFlight, InstalledPackagesProvider};
|
||||
use uv_warnings::warn_user;
|
||||
|
@ -102,7 +102,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
reinstall: &Reinstall,
|
||||
upgrade: &Upgrade,
|
||||
tags: Option<&Tags>,
|
||||
markers: ResolverMarkers,
|
||||
resolver_env: ResolverEnvironment,
|
||||
python_requirement: PythonRequirement,
|
||||
client: &RegistryClient,
|
||||
flat_index: &FlatIndex,
|
||||
|
@ -238,7 +238,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
.chain(upgrade.constraints().cloned()),
|
||||
);
|
||||
let overrides = Overrides::from_requirements(overrides);
|
||||
let preferences = Preferences::from_iter(preferences, &markers);
|
||||
let preferences = Preferences::from_iter(preferences, &resolver_env);
|
||||
|
||||
// Determine any lookahead requirements.
|
||||
let lookaheads = match options.dependency_mode {
|
||||
|
@ -253,7 +253,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
|
||||
)
|
||||
.with_reporter(ResolverReporter::from(printer))
|
||||
.resolve(&markers)
|
||||
.resolve(&resolver_env)
|
||||
.await?
|
||||
}
|
||||
DependencyMode::Direct => Vec::new(),
|
||||
|
@ -289,7 +289,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
manifest,
|
||||
options,
|
||||
&python_requirement,
|
||||
markers,
|
||||
resolver_env,
|
||||
tags,
|
||||
flat_index,
|
||||
index,
|
||||
|
|
|
@ -54,7 +54,7 @@ pub(crate) fn pip_show(
|
|||
let site_packages = SitePackages::from_environment(&environment)?;
|
||||
|
||||
// Determine the markers to use for resolution.
|
||||
let markers = environment.interpreter().resolver_markers();
|
||||
let markers = environment.interpreter().resolver_marker_environment();
|
||||
|
||||
// Sort and deduplicate the packages, which are keyed by name.
|
||||
packages.sort_unstable();
|
||||
|
|
|
@ -23,7 +23,7 @@ use uv_python::{
|
|||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_resolver::{
|
||||
DependencyMode, ExcludeNewer, FlatIndex, OptionsBuilder, PrereleaseMode, PythonRequirement,
|
||||
ResolutionMode, ResolverMarkers,
|
||||
ResolutionMode, ResolverEnvironment,
|
||||
};
|
||||
use uv_types::{BuildIsolation, HashStrategy};
|
||||
|
||||
|
@ -182,7 +182,7 @@ pub(crate) async fn pip_sync(
|
|||
};
|
||||
|
||||
// Determine the markers and tags to use for resolution.
|
||||
let markers = resolution_markers(
|
||||
let marker_env = resolution_markers(
|
||||
python_version.as_ref(),
|
||||
python_platform.as_ref(),
|
||||
interpreter,
|
||||
|
@ -202,7 +202,7 @@ pub(crate) async fn pip_sync(
|
|||
constraints
|
||||
.iter()
|
||||
.map(|entry| (&entry.requirement, entry.hashes.as_slice())),
|
||||
Some(&markers),
|
||||
Some(&marker_env),
|
||||
hash_checking,
|
||||
)?
|
||||
} else {
|
||||
|
@ -270,7 +270,7 @@ pub(crate) async fn pip_sync(
|
|||
build_constraints
|
||||
.iter()
|
||||
.map(|entry| (&entry.requirement, entry.hashes.as_slice())),
|
||||
Some(&markers),
|
||||
Some(&marker_env),
|
||||
HashCheckingMode::Verify,
|
||||
)?
|
||||
} else {
|
||||
|
@ -342,7 +342,7 @@ pub(crate) async fn pip_sync(
|
|||
&reinstall,
|
||||
&upgrade,
|
||||
Some(&tags),
|
||||
ResolverMarkers::specific_environment(markers.clone()),
|
||||
ResolverEnvironment::specific(marker_env.clone()),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
@ -401,7 +401,7 @@ pub(crate) async fn pip_sync(
|
|||
|
||||
// Notify the user of any environment diagnostics.
|
||||
if strict && !dry_run {
|
||||
operations::diagnose_environment(&resolution, &environment, &markers, printer)?;
|
||||
operations::diagnose_environment(&resolution, &environment, &marker_env, printer)?;
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
|
|
|
@ -59,7 +59,7 @@ pub(crate) fn pip_tree(
|
|||
};
|
||||
|
||||
// Determine the markers to use for the resolution.
|
||||
let markers = environment.interpreter().resolver_markers();
|
||||
let markers = environment.interpreter().resolver_marker_environment();
|
||||
|
||||
// Render the tree.
|
||||
let rendered_tree = DisplayDependencyGraph::new(
|
||||
|
|
|
@ -28,7 +28,7 @@ use uv_requirements::upgrade::{read_lock_requirements, LockedRequirements};
|
|||
use uv_requirements::ExtrasResolver;
|
||||
use uv_resolver::{
|
||||
FlatIndex, InMemoryIndex, Lock, LockVersion, Options, OptionsBuilder, PythonRequirement,
|
||||
RequiresPython, ResolverManifest, ResolverMarkers, SatisfiesResult, VERSION,
|
||||
RequiresPython, ResolverEnvironment, ResolverManifest, SatisfiesResult, VERSION,
|
||||
};
|
||||
use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
|
@ -590,7 +590,7 @@ async fn do_lock(
|
|||
// `preferences-dependent-forking` packse scenario). To avoid this, we store the forks in the
|
||||
// lockfile. We read those after all the lockfile filters, to allow the forks to change when
|
||||
// the environment changed, e.g. the python bound check above can lead to different forking.
|
||||
let resolver_markers = ResolverMarkers::universal(
|
||||
let resolver_env = ResolverEnvironment::universal(
|
||||
forks_lock
|
||||
.map(|lock| lock.fork_markers().to_vec())
|
||||
.unwrap_or_else(|| {
|
||||
|
@ -633,7 +633,7 @@ async fn do_lock(
|
|||
&Reinstall::default(),
|
||||
upgrade,
|
||||
None,
|
||||
resolver_markers,
|
||||
resolver_env,
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
|
@ -32,7 +32,7 @@ use uv_requirements::upgrade::{read_lock_requirements, LockedRequirements};
|
|||
use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification};
|
||||
use uv_resolver::{
|
||||
FlatIndex, Lock, OptionsBuilder, PythonRequirement, RequiresPython, ResolutionGraph,
|
||||
ResolverMarkers,
|
||||
ResolverEnvironment,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
|
@ -813,7 +813,7 @@ pub(crate) async fn resolve_environment<'a>(
|
|||
|
||||
// Determine the tags, markers, and interpreter to use for resolution.
|
||||
let tags = interpreter.tags()?;
|
||||
let markers = interpreter.resolver_markers();
|
||||
let marker_env = interpreter.resolver_marker_environment();
|
||||
let python_requirement = PythonRequirement::from_interpreter(interpreter);
|
||||
|
||||
// Add all authenticated sources to the cache.
|
||||
|
@ -929,7 +929,7 @@ pub(crate) async fn resolve_environment<'a>(
|
|||
&reinstall,
|
||||
&upgrade,
|
||||
Some(tags),
|
||||
ResolverMarkers::specific_environment(markers),
|
||||
ResolverEnvironment::specific(marker_env),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
@ -1143,12 +1143,12 @@ pub(crate) async fn update_environment(
|
|||
|
||||
// Determine markers to use for resolution.
|
||||
let interpreter = venv.interpreter();
|
||||
let markers = venv.interpreter().resolver_markers();
|
||||
let marker_env = venv.interpreter().resolver_marker_environment();
|
||||
|
||||
// Check if the current environment satisfies the requirements
|
||||
let site_packages = SitePackages::from_environment(&venv)?;
|
||||
if source_trees.is_empty() && reinstall.is_none() && upgrade.is_none() && overrides.is_empty() {
|
||||
match site_packages.satisfies(&requirements, &constraints, &markers)? {
|
||||
match site_packages.satisfies(&requirements, &constraints, &marker_env)? {
|
||||
// If the requirements are already satisfied, we're done.
|
||||
SatisfiesResult::Fresh {
|
||||
recursive_requirements,
|
||||
|
@ -1271,7 +1271,7 @@ pub(crate) async fn update_environment(
|
|||
reinstall,
|
||||
upgrade,
|
||||
Some(tags),
|
||||
ResolverMarkers::specific_environment(markers.clone()),
|
||||
ResolverEnvironment::specific(marker_env.clone()),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
|
@ -1009,7 +1009,7 @@ fn can_skip_ephemeral(
|
|||
match site_packages.satisfies(
|
||||
&spec.requirements,
|
||||
&spec.constraints,
|
||||
&base_interpreter.resolver_markers(),
|
||||
&base_interpreter.resolver_marker_environment(),
|
||||
) {
|
||||
// If the requirements are already satisfied, we're done.
|
||||
Ok(SatisfiesResult::Fresh {
|
||||
|
|
|
@ -277,12 +277,15 @@ pub(super) async fn do_sync(
|
|||
}
|
||||
|
||||
// Determine the markers to use for resolution.
|
||||
let markers = venv.interpreter().resolver_markers();
|
||||
let marker_env = venv.interpreter().resolver_marker_environment();
|
||||
|
||||
// Validate that the platform is supported by the lockfile.
|
||||
let environments = target.lock().supported_environments();
|
||||
if !environments.is_empty() {
|
||||
if !environments.iter().any(|env| env.evaluate(&markers, &[])) {
|
||||
if !environments
|
||||
.iter()
|
||||
.any(|env| env.evaluate(&marker_env, &[]))
|
||||
{
|
||||
return Err(ProjectError::LockedPlatformIncompatibility(
|
||||
// For error reporting, we use the "simplified"
|
||||
// supported environments, because these correspond to
|
||||
|
@ -304,8 +307,14 @@ pub(super) async fn do_sync(
|
|||
let tags = venv.interpreter().tags()?;
|
||||
|
||||
// Read the lockfile.
|
||||
let resolution =
|
||||
target.to_resolution(&markers, tags, extras, dev, build_options, &install_options)?;
|
||||
let resolution = target.to_resolution(
|
||||
&marker_env,
|
||||
tags,
|
||||
extras,
|
||||
dev,
|
||||
build_options,
|
||||
&install_options,
|
||||
)?;
|
||||
|
||||
// Always skip virtual projects, which shouldn't be built or installed.
|
||||
let resolution = apply_no_virtual_project(resolution);
|
||||
|
|
|
@ -540,7 +540,7 @@ async fn get_or_create_environment(
|
|||
site_packages.satisfies(
|
||||
&requirements,
|
||||
&constraints,
|
||||
&interpreter.resolver_markers()
|
||||
&interpreter.resolver_marker_environment()
|
||||
),
|
||||
Ok(SatisfiesResult::Fresh { .. })
|
||||
) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue