From d082763adf05c011188cce0f3d29487823fc092a Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 4 Nov 2024 13:41:07 -0500 Subject: [PATCH] Get representations working --- Cargo.lock | 4 +- Cargo.toml | 4 +- crates/uv-pep440/src/version.rs | 32 ++-- crates/uv-pep440/src/version_ranges.rs | 38 ++--- crates/uv-resolver/src/error.rs | 160 +++++++++++++++++++- crates/uv-resolver/src/pubgrub/report.rs | 10 ++ crates/uv-resolver/src/resolver/mod.rs | 5 +- crates/uv/tests/it/lock_scenarios.rs | 16 +- crates/uv/tests/it/pip_install_scenarios.rs | 110 ++++++++------ 9 files changed, 281 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19107f3f81c5..f51b5b4e571f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2514,7 +2514,7 @@ dependencies = [ [[package]] name = "pubgrub" version = "0.2.1" -source = "git+https://github.com/astral-sh/pubgrub?rev=95e1390399cdddee986b658be19587eb1fdb2d79#95e1390399cdddee986b658be19587eb1fdb2d79" +source = "git+https://github.com/astral-sh/pubgrub?branch=charlie/mut#5aadec2663c058cac0ffdacc73d58b01ebdee5a9" dependencies = [ "indexmap", "log", @@ -5403,7 +5403,7 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version-ranges" version = "0.1.0" -source = "git+https://github.com/astral-sh/pubgrub?rev=95e1390399cdddee986b658be19587eb1fdb2d79#95e1390399cdddee986b658be19587eb1fdb2d79" +source = "git+https://github.com/astral-sh/pubgrub?branch=charlie/mut#5aadec2663c058cac0ffdacc73d58b01ebdee5a9" dependencies = [ "smallvec", ] diff --git a/Cargo.toml b/Cargo.toml index b5be4b26df97..b0bfeeab59bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,8 +127,8 @@ petgraph = { version = "0.6.5" } platform-info = { version = "2.0.3" } procfs = { version = "0.17.0" , default-features = false, features = ["flate2"] } proc-macro2 = { version = "1.0.86" } -pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "95e1390399cdddee986b658be19587eb1fdb2d79" } -version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "95e1390399cdddee986b658be19587eb1fdb2d79" } +pubgrub = { git = "https://github.com/astral-sh/pubgrub", branch = "charlie/mut" } +version-ranges = { git = "https://github.com/astral-sh/pubgrub", branch = "charlie/mut" } quote = { version = "1.0.37" } rayon = { version = "1.10.0" } reflink-copy = { version = "0.1.19" } diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs index fc7edff392d3..b2bbb1ecd902 100644 --- a/crates/uv-pep440/src/version.rs +++ b/crates/uv-pep440/src/version.rs @@ -725,7 +725,12 @@ impl std::fmt::Display for Version { let local = if self.local().is_empty() { String::new() } else { - format!("+{}", self.local().local_identifier_string()) + match self.local() { + LocalVersionSlice::Actual(_) => { + format!("+{}", self.local().local_identifier_string()) + } + LocalVersionSlice::Sentinel => String::new(), + } }; write!(f, "{epoch}{release}{pre}{post}{dev}{local}") } @@ -1389,7 +1394,8 @@ impl std::fmt::Display for Prerelease { } } -/// Either a sequence of local segments or [`LocalVersion::Sentinel`], an internal-only value that compares greater than all other local versions. +/// Either a sequence of local segments or [`LocalVersion::Sentinel`], an internal-only value that +/// compares greater than all other local versions. #[derive(Eq, PartialEq, Debug, Clone, Hash)] #[cfg_attr( feature = "rkyv", @@ -1397,9 +1403,9 @@ impl std::fmt::Display for Prerelease { )] #[cfg_attr(feature = "rkyv", rkyv(derive(Debug, Eq, PartialEq, PartialOrd, Ord)))] pub enum LocalVersion { - /// A sequence of local segments + /// A sequence of local segments. Actual(Vec), - /// An internal-only value that compares greater to all other local versions + /// An internal-only value that compares greater to all other local versions. Max, } @@ -1408,12 +1414,12 @@ pub enum LocalVersion { pub enum LocalVersionSlice<'a> { /// Like [`LocalVersion::Actual`] Actual(&'a [LocalSegment]), - /// Like [`LocalVersion::Sentintel`] + /// Like [`LocalVersion::Sentinel`] Sentinel, } impl LocalVersion { - /// convert into a local version slice + /// Convert the local version segments into a slice. pub fn as_slice(&self) -> LocalVersionSlice<'_> { match self { LocalVersion::Actual(segments) => LocalVersionSlice::Actual(segments), @@ -1421,16 +1427,20 @@ impl LocalVersion { } } - /// clear the local version segments, if they exist + /// Clear the local version segments, if they exist. pub fn clear(&mut self) { - if let Self::Actual(segments) = self { - segments.clear(); + match self { + Self::Actual(segments) => segments.clear(), + Self::Max => *self = Self::Actual(Vec::new()), } } } impl LocalVersionSlice<'_> { - /// output the local version identifier string. [`LocalVersionSlice::Sentinel`] maps to `"[max-local-version]"` which is otherwise an illegal local version because `<` and `>` are not allowed + /// Output the local version identifier string. + /// + /// [`LocalVersionSlice::Sentinel`] maps to `"[max]"` which is otherwise an illegal local + /// version because `[` and `]` are not allowed. pub fn local_identifier_string(&self) -> String { match self { LocalVersionSlice::Actual(segments) => segments @@ -1438,7 +1448,7 @@ impl LocalVersionSlice<'_> { .map(ToString::to_string) .collect::>() .join("."), - LocalVersionSlice::Sentinel => String::from("[max-local-version]"), + LocalVersionSlice::Sentinel => String::from("[max]"), } } } diff --git a/crates/uv-pep440/src/version_ranges.rs b/crates/uv-pep440/src/version_ranges.rs index 0a3a867f1ea0..5a0529808cd3 100644 --- a/crates/uv-pep440/src/version_ranges.rs +++ b/crates/uv-pep440/src/version_ranges.rs @@ -2,7 +2,10 @@ use version_ranges::Ranges; -use crate::{LocalVersion, Operator, Prerelease, Version, VersionSpecifier, VersionSpecifiers}; +use crate::{ + LocalVersion, LocalVersionSlice, Operator, Prerelease, Version, VersionSpecifier, + VersionSpecifiers, +}; impl From for Ranges { /// Convert [`VersionSpecifiers`] to a PubGrub-compatible version range, using PEP 440 @@ -23,13 +26,13 @@ impl From for Ranges { let VersionSpecifier { operator, version } = specifier; match operator { Operator::Equal => match version.local() { - crate::LocalVersionSlice::Actual(&[]) => { + LocalVersionSlice::Actual(&[]) => { let low = version; let high = low.clone().with_local(LocalVersion::Max); Ranges::between(low, high) } - crate::LocalVersionSlice::Actual(_) => Ranges::singleton(version), - crate::LocalVersionSlice::Sentinel => unreachable!( + LocalVersionSlice::Actual(_) => Ranges::singleton(version), + LocalVersionSlice::Sentinel => unreachable!( "found `LocalVersionSlice::Sentinel`, which should be an internal-only value" ), }, @@ -146,34 +149,21 @@ pub fn release_specifier_to_range(specifier: VersionSpecifier) -> Ranges { let version = version.only_release(); - match version.local() { - crate::LocalVersionSlice::Actual(&[]) => { - let low = version; - let high = low.clone().with_local(LocalVersion::Max); - Ranges::between(low, high) - } - crate::LocalVersionSlice::Actual(_) => Ranges::singleton(version), - crate::LocalVersionSlice::Sentinel => unreachable!( - "found `LocalVersionSlice::Sentinel`, which should be an internal-only value" - ), - } + Ranges::singleton(version) } Operator::ExactEqual => { let version = version.only_release(); Ranges::singleton(version) } - Operator::NotEqual => release_specifier_to_range(VersionSpecifier { - operator: Operator::Equal, - version, - }) - .complement(), + Operator::NotEqual => { + let version = version.only_release(); + Ranges::singleton(version).complement() + } Operator::TildeEqual => { let [rest @ .., last, _] = version.release() else { unreachable!("~= must have at least two segments"); }; - let upper = - // greater release includes local - Version::new(rest.iter().chain([&(last + 1)])); + let upper = Version::new(rest.iter().chain([&(last + 1)])); let version = version.only_release(); Ranges::from_range_bounds(version..upper) } @@ -183,7 +173,7 @@ pub fn release_specifier_to_range(specifier: VersionSpecifier) -> Ranges { let version = version.only_release(); - Ranges::lower_than(version.with_local(LocalVersion::Max)) + Ranges::lower_than(version) } Operator::GreaterThan => { let version = version.only_release(); diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index 1547f9d92b2b..d8c2f7863bd4 100644 --- a/crates/uv-resolver/src/error.rs +++ b/crates/uv-resolver/src/error.rs @@ -1,9 +1,11 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, Bound}; use std::fmt::Formatter; use std::sync::Arc; use indexmap::IndexSet; -use pubgrub::{DefaultStringReporter, DerivationTree, Derived, External, Range, Reporter}; +use pubgrub::{ + DefaultStringReporter, DerivationTree, Derived, External, Range, Ranges, Reporter, Term, +}; use rustc_hash::FxHashMap; use crate::candidate_selector::CandidateSelector; @@ -19,7 +21,7 @@ use uv_distribution_types::{ BuiltDist, IndexCapabilities, IndexLocations, IndexUrl, InstalledDist, SourceDist, }; use uv_normalize::PackageName; -use uv_pep440::Version; +use uv_pep440::{LocalVersionSlice, Version}; use uv_pep508::MarkerTree; use uv_static::EnvVars; @@ -210,6 +212,157 @@ impl NoSolutionError { .expect("derivation tree should contain at least one external term") } + /// Simplifies the version ranges on any incompatibilities to remove the `[max]` sentinel. + /// + /// The `[max]` sentinel is used to represent the maximum local version of a package, to + /// implement PEP 440 semantics for local version equality. For example, `1.0.0+foo` needs to + /// satisfy `==1.0.0`. + pub(crate) fn simplify_local_version_segments( + mut derivation_tree: ErrorTree, + ) -> Option { + /// Remove local versions sentinels (`+[max]`) from the given version ranges. + fn strip_sentinel(versions: &mut Ranges) { + versions.iter_mut().for_each(|(lower, upper)| { + match (&lower, &upper) { + (Bound::Unbounded, Bound::Unbounded) => {} + (Bound::Unbounded, Bound::Included(v)) => { + // `<=1.0.0+[max]` is equivalent to `<=1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *upper = Bound::Included(v.clone().without_local()); + } + } + (Bound::Unbounded, Bound::Excluded(v)) => { + // `<1.0.0+[max]` is equivalent to `<1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *upper = Bound::Excluded(v.clone().without_local()); + } + } + (Bound::Included(v), Bound::Unbounded) => { + // `>=1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *lower = Bound::Excluded(v.clone().without_local()); + } + } + (Bound::Included(v), Bound::Included(b)) => { + // `>=1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *lower = Bound::Excluded(v.clone().without_local()); + } + // `<=1.0.0+[max]` is equivalent to `<=1.0.0` + if b.local() == LocalVersionSlice::Sentinel { + *upper = Bound::Included(b.clone().without_local()); + } + } + (Bound::Included(v), Bound::Excluded(b)) => { + // `>=1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *lower = Bound::Excluded(v.clone().without_local()); + } + // `<1.0.0+[max]` is equivalent to `<1.0.0` + if b.local() == LocalVersionSlice::Sentinel { + *upper = Bound::Included(b.clone().without_local()); + } + } + (Bound::Excluded(v), Bound::Unbounded) => { + // `>1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *lower = Bound::Excluded(v.clone().without_local()); + } + } + (Bound::Excluded(v), Bound::Included(b)) => { + // `>1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *lower = Bound::Excluded(v.clone().without_local()); + } + // `<=1.0.0+[max]` is equivalent to `<=1.0.0` + if b.local() == LocalVersionSlice::Sentinel { + *upper = Bound::Included(b.clone().without_local()); + } + } + (Bound::Excluded(v), Bound::Excluded(b)) => { + // `>1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Sentinel { + *lower = Bound::Excluded(v.clone().without_local()); + } + // `<1.0.0+[max]` is equivalent to `<1.0.0` + if b.local() == LocalVersionSlice::Sentinel { + *upper = Bound::Excluded(b.clone().without_local()); + } + } + } + }); + } + + /// Returns `true` if the range appears to be, e.g., `>1.0.0, <1.0.0+[max]`. + fn is_sentinel(versions: &mut Ranges) -> bool { + versions.iter().all(|(lower, upper)| { + let (Bound::Excluded(lower), Bound::Excluded(upper)) = (lower, upper) else { + return false; + }; + if lower.local() == LocalVersionSlice::Sentinel { + return false; + } + if upper.local() != LocalVersionSlice::Sentinel { + return false; + } + *lower == upper.clone().without_local() + }) + } + + match derivation_tree { + DerivationTree::External(External::NotRoot(_, _)) => Some(derivation_tree), + DerivationTree::External(External::NoVersions(_, ref mut versions)) => { + if is_sentinel(versions) { + None + } else { + strip_sentinel(versions); + Some(derivation_tree) + } + } + DerivationTree::External(External::FromDependencyOf( + _, + ref mut versions1, + _, + ref mut versions2, + )) => { + strip_sentinel(versions1); + strip_sentinel(versions2); + Some(derivation_tree) + } + DerivationTree::External(External::Custom(_, ref mut versions, _)) => { + strip_sentinel(versions); + Some(derivation_tree) + } + DerivationTree::Derived(mut derived) => { + let cause1 = Self::simplify_local_version_segments((*derived.cause1).clone()); + let cause2 = Self::simplify_local_version_segments((*derived.cause2).clone()); + match (cause1, cause2) { + (Some(cause1), Some(cause2)) => Some(DerivationTree::Derived(Derived { + cause1: Arc::new(cause1), + cause2: Arc::new(cause2), + terms: std::mem::take(&mut derived.terms) + .into_iter() + .map(|(pkg, mut term)| { + match &mut term { + Term::Positive(versions) => { + strip_sentinel(versions); + } + Term::Negative(versions) => { + strip_sentinel(versions); + } + } + (pkg, term) + }) + .collect(), + shared_id: derived.shared_id, + })), + (Some(cause), None) | (None, Some(cause)) => Some(cause), + _ => None, + } + } + } + } + /// Initialize a [`NoSolutionHeader`] for this error. pub fn header(&self) -> NoSolutionHeader { NoSolutionHeader::new(self.markers.clone()) @@ -238,6 +391,7 @@ impl std::fmt::Display for NoSolutionError { display_tree(&tree, "Resolver derivation tree before reduction"); } + // simplify_local_version_segments(&mut tree); collapse_no_versions_of_workspace_members(&mut tree, &self.workspace_members); if self.workspace_members.len() == 1 { diff --git a/crates/uv-resolver/src/pubgrub/report.rs b/crates/uv-resolver/src/pubgrub/report.rs index 83b45a87e536..15d9e81f6e6c 100644 --- a/crates/uv-resolver/src/pubgrub/report.rs +++ b/crates/uv-resolver/src/pubgrub/report.rs @@ -496,6 +496,16 @@ impl PubGrubReportFormatter<'_> { } } + // fn simplify_locals<'a>( + // &self, + // set: &'a Range, + // ) -> Cow<'a, Range> { + // // for (lower, upper) in set.iter() { + // // + // // } + // Cow::Owned(Range::from((Bound::Included(Version::new([0])), Bound::Excluded(Version::new([1]))))) + // } + /// Generate the [`PubGrubHints`] for a derivation tree. /// /// The [`PubGrubHints`] help users resolve errors by providing additional context or modifying diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 84b89435edfc..1b21cbc28228 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -1980,7 +1980,10 @@ impl ResolverState ResolveError { - err = NoSolutionError::collapse_proxies(err); + err = NoSolutionError::simplify_local_version_segments(NoSolutionError::collapse_proxies( + err, + )) + .unwrap(); let mut unavailable_packages = FxHashMap::default(); for package in err.packages() { diff --git a/crates/uv/tests/it/lock_scenarios.rs b/crates/uv/tests/it/lock_scenarios.rs index b33346d8acbc..9da81be37441 100644 --- a/crates/uv/tests/it/lock_scenarios.rs +++ b/crates/uv/tests/it/lock_scenarios.rs @@ -2773,16 +2773,16 @@ fn fork_non_local_fork_marker_direct() -> Result<()> { let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); cmd.arg("--index-url").arg(packse_index_url()); - uv_snapshot!(filters, cmd, @r#" + uv_snapshot!(filters, cmd, @r###" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0 and package-a{sys_platform == 'linux'}==1.0.0 depends on package-c<2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible. + ╰─▶ Because package-a{sys_platform == 'linux'}==1.0.0 depends on package-c<2.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible. And because your project depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable. - "# + "### ); Ok(()) @@ -2845,20 +2845,20 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> { let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); cmd.arg("--index-url").arg(packse_index_url()); - uv_snapshot!(filters, cmd, @r#" + uv_snapshot!(filters, cmd, @r###" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0 and only package-c{sys_platform == 'darwin'}<=2.0.0 is available, we can conclude that package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}==2.0.0. - And because only the following versions of package-c{sys_platform == 'linux'} are available: + ╰─▶ Because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and only the following versions of package-c{sys_platform == 'linux'} are available: package-c{sys_platform == 'linux'}==1.0.0 package-c{sys_platform == 'linux'}>2.0.0 - and package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible. + we can conclude that package-a==1.0.0 depends on package-c{sys_platform == 'linux'}==1.0.0. + And because only package-c{sys_platform == 'darwin'}<=2.0.0 is available and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible. And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable. - "# + "### ); Ok(()) diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs index 7116d2b4f7ee..f0fc00bb075a 100644 --- a/crates/uv/tests/it/pip_install_scenarios.rs +++ b/crates/uv/tests/it/pip_install_scenarios.rs @@ -342,17 +342,17 @@ fn excluded_only_compatible_version() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: + ╰─▶ Because only the following versions of package-a are available: package-a==1.0.0 package-a==2.0.0 package-a==3.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. + and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all of: package-a<2.0.0 package-a>2.0.0 depend on one of: - package-b<=1.0.0 - package-b>=3.0.0 + package-b==1.0.0 + package-b==3.0.0 And because you require one of: package-a<2.0.0 @@ -441,17 +441,17 @@ fn dependency_excludes_range_of_compatible_versions() { .arg("dependency-excludes-range-of-compatible-versions-a") .arg("dependency-excludes-range-of-compatible-versions-b<3.0.0,>=2.0.0") .arg("dependency-excludes-range-of-compatible-versions-c") - , @r#" + , @r###" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: + ╰─▶ Because only the following versions of package-a are available: package-a==1.0.0 package-a>2.0.0,<=3.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) + and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) Because only the following versions of package-c are available: package-c==1.0.0 @@ -467,7 +467,7 @@ fn dependency_excludes_range_of_compatible_versions() { package-b>=3.0.0 And because you require package-b>=2.0.0,<3.0.0 and package-c, we can conclude that your requirements are unsatisfiable. - "#); + "###); // Only the `2.x` versions of `a` are available since `a==1.0.0` and `a==3.0.0` // require incompatible versions of `b`, but all available versions of `c` exclude @@ -566,17 +566,17 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { .arg("dependency-excludes-non-contiguous-range-of-compatible-versions-a") .arg("dependency-excludes-non-contiguous-range-of-compatible-versions-b<3.0.0,>=2.0.0") .arg("dependency-excludes-non-contiguous-range-of-compatible-versions-c") - , @r#" + , @r###" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: + ╰─▶ Because only the following versions of package-a are available: package-a==1.0.0 package-a>2.0.0,<=3.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) + and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) Because only the following versions of package-c are available: package-c==1.0.0 @@ -592,7 +592,7 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { package-b>=3.0.0 And because you require package-b>=2.0.0,<3.0.0 and package-c, we can conclude that your requirements are unsatisfiable. - "#); + "###); // Only the `2.x` versions of `a` are available since `a==1.0.0` and `a==3.0.0` // require incompatible versions of `b`, but all available versions of `c` exclude @@ -1317,18 +1317,25 @@ fn local_simple() { uv_snapshot!(filters, command(&context) .arg("local-simple-a==1.2.3") - , @r#" - success: false - exit_code: 1 + , @r###" + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because there is no version of package-a==1.2.3 and you require package-a==1.2.3, we can conclude that your requirements are unsatisfiable. - "#); + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3+foo + "###); // The version '1.2.3+foo' satisfies the constraint '==1.2.3'. - assert_not_installed(&context.venv, "local_simple_a", &context.temp_dir); + assert_installed( + &context.venv, + "local_simple_a", + "1.2.3+foo", + &context.temp_dir, + ); } /// If there is a 1.2.3 version with an sdist published and no compatible wheels, @@ -1356,7 +1363,7 @@ fn local_not_used_with_sdist() { uv_snapshot!(filters, command(&context) .arg("local-not-used-with-sdist-a==1.2.3") - , @r#" + , @r###" success: true exit_code: 0 ----- stdout ----- @@ -1365,14 +1372,14 @@ fn local_not_used_with_sdist() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3 - "#); + + package-a==1.2.3+foo + "###); - // The version '1.2.3' with an sdist satisfies the constraint '==1.2.3'. + // The version '1.2.3+foo' with an sdist satisfies the constraint '==1.2.3'. assert_installed( &context.venv, "local_not_used_with_sdist_a", - "1.2.3", + "1.2.3+foo", &context.temp_dir, ); } @@ -1403,20 +1410,23 @@ fn local_used_without_sdist() { uv_snapshot!(filters, command(&context) .arg("local-used-without-sdist-a==1.2.3") - , @r#" - success: false - exit_code: 1 + , @r###" + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.2.3 has no wheels with a matching Python ABI tag and you require package-a==1.2.3, we can conclude that your requirements are unsatisfiable. - "#); + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3+foo + "###); // The version '1.2.3+foo' satisfies the constraint '==1.2.3'. - assert_not_installed( + assert_installed( &context.venv, "local_used_without_sdist_a", + "1.2.3+foo", &context.temp_dir, ); } @@ -1777,21 +1787,24 @@ fn local_transitive_confounding() { uv_snapshot!(filters, command(&context) .arg("local-transitive-confounding-a") - , @r#" - success: false - exit_code: 1 + , @r###" + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because package-b==2.0.0 has no wheels with a matching Python ABI tag and package-a==1.0.0 depends on package-b==2.0.0, we can conclude that package-a==1.0.0 cannot be used. - And because only package-a==1.0.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. - "#); + Resolved 2 packages in [TIME] + Prepared 2 packages in [TIME] + Installed 2 packages in [TIME] + + package-a==1.0.0 + + package-b==2.0.0+foo + "###); // The version '2.0.0+foo' satisfies the constraint '==2.0.0'. - assert_not_installed( + assert_installed( &context.venv, - "local_transitive_confounding_a", + "local_transitive_confounding_b", + "2.0.0+foo", &context.temp_dir, ); } @@ -2048,20 +2061,23 @@ fn local_less_than_or_equal() { uv_snapshot!(filters, command(&context) .arg("local-less-than-or-equal-a<=1.2.3") - , @r#" - success: false - exit_code: 1 + , @r###" + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.2.3+foo is available and you require package-a<=1.2.3, we can conclude that your requirements are unsatisfiable. - "#); + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3+foo + "###); // The version '1.2.3+foo' satisfies the constraint '<=1.2.3'. - assert_not_installed( + assert_installed( &context.venv, "local_less_than_or_equal_a", + "1.2.3+foo", &context.temp_dir, ); }