diff --git a/crates/uv-bench/benches/uv.rs b/crates/uv-bench/benches/uv.rs index 8798d1de2..4713ea9c9 100644 --- a/crates/uv-bench/benches/uv.rs +++ b/crates/uv-bench/benches/uv.rs @@ -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( diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 233d9a378..6003e45b1 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -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 { 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, diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs index cbf7950f6..36b713a61 100644 --- a/crates/uv-python/src/interpreter.rs +++ b/crates/uv-python/src/interpreter.rs @@ -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()) } diff --git a/crates/uv-requirements/src/lookahead.rs b/crates/uv-requirements/src/lookahead.rs index f0b60fa04..fd1b252b0 100644 --- a/crates/uv-requirements/src/lookahead.rs +++ b/crates/uv-requirements/src/lookahead.rs @@ -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, 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()); } diff --git a/crates/uv-resolver/src/candidate_selector.rs b/crates/uv-resolver/src/candidate_selector.rs index 891d035fe..7edc61545 100644 --- a/crates/uv-resolver/src/candidate_selector.rs +++ b/crates/uv-resolver/src/candidate_selector.rs @@ -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> { 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 { // 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> { 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_maps: &'a [VersionMap], - markers: &ResolverMarkers, + env: &ResolverEnvironment, ) -> Option { 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. diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index 1547f9d92..926af4fcf 100644 --- a/crates/uv-resolver/src/error.rs +++ b/crates/uv-resolver/src/error.rs @@ -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), - - #[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, - fork_markers: MarkerTree, + env: ResolverEnvironment, }, - #[error("Requirements contain conflicting indexes for package `{0}`:\n- {}", _1.join("\n- "))] - ConflictingIndexesUniversal(PackageName, Vec), - - #[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, - 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, incomplete_packages: FxHashMap>, fork_urls: ForkUrls, - markers: ResolverMarkers, + env: ResolverEnvironment, workspace_members: BTreeSet, options: Options, } @@ -145,7 +156,7 @@ impl NoSolutionError { unavailable_packages: FxHashMap, incomplete_packages: FxHashMap>, fork_urls: ForkUrls, - markers: ResolverMarkers, + env: ResolverEnvironment, workspace_members: BTreeSet, 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}:" + ), } } } diff --git a/crates/uv-resolver/src/fork_indexes.rs b/crates/uv-resolver/src/fork_indexes.rs index 48af8fbe3..e575dfc8b 100644 --- a/crates/uv-resolver/src/fork_indexes.rs +++ b/crates/uv-resolver/src/fork_indexes.rs @@ -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(()) diff --git a/crates/uv-resolver/src/fork_urls.rs b/crates/uv-resolver/src/fork_urls.rs index 5b11f2117..f9629be53 100644 --- a/crates/uv-resolver/src/fork_urls.rs +++ b/crates/uv-resolver/src/fork_urls.rs @@ -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) => { diff --git a/crates/uv-resolver/src/lib.rs b/crates/uv-resolver/src/lib.rs index 664ca7c3b..cbf7dae2a 100644 --- a/crates/uv-resolver/src/lib.rs +++ b/crates/uv-resolver/src/lib.rs @@ -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; diff --git a/crates/uv-resolver/src/manifest.rs b/crates/uv-resolver/src/manifest.rs index 097ad2fe4..ed5c517f9 100644 --- a/crates/uv-resolver/src/manifest.rs +++ b/crates/uv-resolver/src/manifest.rs @@ -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> + '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> + '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> + '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> + '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> + '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. diff --git a/crates/uv-resolver/src/preferences.rs b/crates/uv-resolver/src/preferences.rs index 487050eb8..42be41b03 100644 --- a/crates/uv-resolver/src/preferences.rs +++ b/crates/uv-resolver/src/preferences.rs @@ -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, 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>( 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; diff --git a/crates/uv-resolver/src/prerelease.rs b/crates/uv-resolver/src/prerelease.rs index c45285877..4a4cd492a 100644 --- a/crates/uv-resolver/src/prerelease.rs +++ b/crates/uv-resolver/src/prerelease.rs @@ -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 diff --git a/crates/uv-resolver/src/pubgrub/report.rs b/crates/uv-resolver/src/pubgrub/report.rs index 83b45a87e..ce6d65176 100644 --- a/crates/uv-resolver/src/pubgrub/report.rs +++ b/crates/uv-resolver/src/pubgrub/report.rs @@ -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, incomplete_packages: &FxHashMap>, fork_urls: &ForkUrls, - markers: &ResolverMarkers, + env: &ResolverEnvironment, workspace_members: &BTreeSet, options: Options, output_hints: &mut IndexSet, @@ -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, selector: &CandidateSelector, - markers: &ResolverMarkers, + env: &ResolverEnvironment, hints: &mut IndexSet, ) { 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(), diff --git a/crates/uv-resolver/src/python_requirement.rs b/crates/uv-resolver/src/python_requirement.rs index e581bdc09..3de9293cd 100644 --- a/crates/uv-resolver/src/python_requirement.rs +++ b/crates/uv-resolver/src/python_requirement.rs @@ -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 { diff --git a/crates/uv-resolver/src/resolution/display.rs b/crates/uv-resolver/src/resolution/display.rs index a103bd73f..a76935ee7 100644 --- a/crates/uv-resolver/src/resolution/display.rs +++ b/crates/uv-resolver/src/resolution/display.rs @@ -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 { diff --git a/crates/uv-resolver/src/resolution/graph.rs b/crates/uv-resolver/src/resolution/graph.rs index 6730a34ac..b4cf97a0c 100644 --- a/crates/uv-resolver/src/resolution/graph.rs +++ b/crates/uv-resolver/src/resolution/graph.rs @@ -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; @@ -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() }) diff --git a/crates/uv-resolver/src/resolution_mode.rs b/crates/uv-resolver/src/resolution_mode.rs index cb0f66bd3..c105cfee4 100644 --- a/crates/uv-resolver/src/resolution_mode.rs +++ b/crates/uv-resolver/src/resolution_mode.rs @@ -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(), ), diff --git a/crates/uv-resolver/src/resolver/batch_prefetch.rs b/crates/uv-resolver/src/resolver/batch_prefetch.rs index 98fe2d5e2..3acea2600 100644 --- a/crates/uv-resolver/src/resolver/batch_prefetch.rs +++ b/crates/uv-resolver/src/resolver/batch_prefetch.rs @@ -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(), diff --git a/crates/uv-resolver/src/resolver/fork_map.rs b/crates/uv-resolver/src/resolver/fork_map.rs index 2cbcaf66e..607d86e15 100644 --- a/crates/uv-resolver/src/resolver/fork_map.rs +++ b/crates/uv-resolver/src/resolver/fork_map.rs @@ -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 ForkMap { /// 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 ForkMap { /// 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() } } diff --git a/crates/uv-resolver/src/resolver/groups.rs b/crates/uv-resolver/src/resolver/groups.rs index c2a5b77e8..dcc96cbf6 100644 --- a/crates/uv-resolver/src/resolver/groups.rs +++ b/crates/uv-resolver/src/resolver/groups.rs @@ -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>); 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) diff --git a/crates/uv-resolver/src/resolver/indexes.rs b/crates/uv-resolver/src/resolver/indexes.rs index 556a4f444..74a27d681 100644 --- a/crates/uv-resolver/src/resolver/indexes.rs +++ b/crates/uv-resolver/src/resolver/indexes.rs @@ -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) } } diff --git a/crates/uv-resolver/src/resolver/locals.rs b/crates/uv-resolver/src/resolver/locals.rs index dedd9ad9b..de7a22619 100644 --- a/crates/uv-resolver/src/resolver/locals.rs +++ b/crates/uv-resolver/src/resolver/locals.rs @@ -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 diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 84b89435e..6d89cd6bd 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -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 { @@ -107,7 +109,7 @@ struct ResolverState { indexes: Indexes, dependency_mode: DependencyMode, hasher: HashStrategy, - markers: ResolverMarkers, + env: ResolverEnvironment, python_requirement: PythonRequirement, workspace_members: BTreeSet, 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 manifest: Manifest, options: Options, hasher: &HashStrategy, - markers: ResolverMarkers, + env: ResolverEnvironment, python_requirement: &PythonRequirement, index: &InMemoryIndex, git: &GitResolver, @@ -206,12 +208,12 @@ impl 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 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 ResolverState ResolverState ResolverState ResolverState ResolverState ResolverState ResolverState ResolverState { 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 ResolverState ResolverState"); - } + trace!("Resolution: {:?}", combined.env); for edge in &combined.edges { trace!( "Resolution edge: {} -> {}", @@ -703,7 +686,7 @@ impl ResolverState ResolverState ResolverState, request_sink: &Sender, @@ -892,7 +875,7 @@ impl ResolverState ResolverState, package: &PubGrubPackage, preferences: &Preferences, - fork_markers: &ResolverMarkers, + env: &ResolverEnvironment, python_requirement: &PythonRequirement, pins: &mut FilePins, visited: &mut FxHashSet, @@ -1060,7 +1043,7 @@ impl ResolverState ResolverState Result { - 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 ResolverState Result { let url = package.name().and_then(|name| fork_urls.get(name)); @@ -1224,7 +1205,7 @@ impl ResolverState ResolverState ResolverState, dev: Option<&'a GroupName>, name: Option<&PackageName>, - markers: &'a ResolverMarkers, + env: &'a ResolverEnvironment, python_requirement: &'a PythonRequirement, ) -> Vec> { // Start with the requirements for the current extra of the package (for an extra @@ -1493,12 +1474,7 @@ impl ResolverState>(); // Check if there are recursive self inclusions and we need to go into the expensive branch. @@ -1522,7 +1498,7 @@ impl ResolverState ResolverState + 'parameters, extra: Option<&'parameters ExtraName>, - markers: &'parameters ResolverMarkers, + env: &'parameters ResolverEnvironment, python_requirement: &'parameters PythonRequirement, ) -> impl Iterator> + 'parameters where @@ -1568,29 +1544,27 @@ impl ResolverState { // 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 ResolverState { 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 ResolverState ResolverState, fork_urls: ForkUrls, fork_indexes: &ForkIndexes, - markers: ResolverMarkers, + env: ResolverEnvironment, visited: &FxHashSet, index_locations: &IndexLocations, index_capabilities: &IndexCapabilities, @@ -2056,7 +2028,7 @@ impl ResolverState, 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 { - 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.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, /// 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 diff --git a/crates/uv-resolver/src/resolver/resolver_markers.rs b/crates/uv-resolver/src/resolver/resolver_markers.rs deleted file mode 100644 index c3b63b0c5..000000000 --- a/crates/uv-resolver/src/resolver/resolver_markers.rs +++ /dev/null @@ -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, - }, - /// 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) -> 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:?})") - } - } - } -} diff --git a/crates/uv-resolver/src/resolver/urls.rs b/crates/uv-resolver/src/resolver/urls.rs index 38065e4ef..eeae4817b 100644 --- a/crates/uv-resolver/src/resolver/urls.rs +++ b/crates/uv-resolver/src/resolver/urls.rs @@ -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 { @@ -44,7 +44,7 @@ impl Urls { let mut overrides: FxHashMap = 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) } diff --git a/crates/uv-resolver/src/yanks.rs b/crates/uv-resolver/src/yanks.rs index 0ffb1cac5..a6f924cc6 100644 --- a/crates/uv-resolver/src/yanks.rs +++ b/crates/uv-resolver/src/yanks.rs @@ -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>>); impl AllowedYanks { pub fn from_manifest( manifest: &Manifest, - markers: &ResolverMarkers, + env: &ResolverEnvironment, dependencies: DependencyMode, ) -> Self { let mut allowed_yanks = FxHashMap::>::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; }; diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 0aad44afd..4823fdf57 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -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 { diff --git a/crates/uv/src/commands/pip/check.rs b/crates/uv/src/commands/pip/check.rs index d53e6a469..a53e124a9 100644 --- a/crates/uv/src/commands/pip/check.rs +++ b/crates/uv/src/commands/pip/check.rs @@ -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 = diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index aa2c4de3b..b3a92a67a 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -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, diff --git a/crates/uv/src/commands/pip/freeze.rs b/crates/uv/src/commands/pip/freeze.rs index 32405b809..62c30bc34 100644 --- a/crates/uv/src/commands/pip/freeze.rs +++ b/crates/uv/src/commands/pip/freeze.rs @@ -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!( diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index 01db48523..7f45f3001 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -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) diff --git a/crates/uv/src/commands/pip/list.rs b/crates/uv/src/commands/pip/list.rs index 13bed2f61..eeb281fb2 100644 --- a/crates/uv/src/commands/pip/list.rs +++ b/crates/uv/src/commands/pip/list.rs @@ -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!( diff --git a/crates/uv/src/commands/pip/mod.rs b/crates/uv/src/commands/pip/mod.rs index 99d1f7da8..528effcad 100644 --- a/crates/uv/src/commands/pip/mod.rs +++ b/crates/uv/src/commands/pip/mod.rs @@ -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)) diff --git a/crates/uv/src/commands/pip/operations.rs b/crates/uv/src/commands/pip/operations.rs index ba257b4a6..88d06a2be 100644 --- a/crates/uv/src/commands/pip/operations.rs +++ b/crates/uv/src/commands/pip/operations.rs @@ -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( 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( .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( 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( manifest, options, &python_requirement, - markers, + resolver_env, tags, flat_index, index, diff --git a/crates/uv/src/commands/pip/show.rs b/crates/uv/src/commands/pip/show.rs index 6afc9aa3a..5c03f6d04 100644 --- a/crates/uv/src/commands/pip/show.rs +++ b/crates/uv/src/commands/pip/show.rs @@ -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(); diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 87f40f750..22d73d094 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -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) diff --git a/crates/uv/src/commands/pip/tree.rs b/crates/uv/src/commands/pip/tree.rs index 703ebef50..920619a3d 100644 --- a/crates/uv/src/commands/pip/tree.rs +++ b/crates/uv/src/commands/pip/tree.rs @@ -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( diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index e6eadcbd5..1ca451232 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -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, diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index ca9c6edee..7e2dfdd0a 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -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, diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 8362e9cdb..a7465cbd3 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -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 { diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index 3f8e230ce..36f8935fd 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -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); diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 33d316977..1f2145a95 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -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 { .. }) ) {