Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 90 additions & 18 deletions crates/uv-resolver/src/pubgrub/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,13 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
external2: &External<PubGrubPackage, Range<Version>>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
let external1 = self.format_external(external1);
let external2 = self.format_external(external2);
let external = self.format_both_external(external1, external2);
let terms = self.format_terms(current_terms);

format!(
"Because {}and {}we can conclude that {}",
Padded::from_string("", &external1, " "),
Padded::from_string("", &external2, ", "),
Padded::from_string("", &terms, ".")
"Because {}we can conclude that {}",
Padded::from_string("", &external, ", "),
Padded::from_string("", &terms, "."),
)
}

Expand Down Expand Up @@ -305,20 +303,71 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
external: &External<PubGrubPackage, Range<Version>>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
let prior_external = self.format_external(prior_external);
let external = self.format_external(external);
let external = self.format_both_external(prior_external, external);
let terms = self.format_terms(current_terms);

format!(
"And because {}and {}we can conclude that {}",
Padded::from_string("", &prior_external, " "),
"And because {}we can conclude that {}",
Padded::from_string("", &external, ", "),
Padded::from_string("", &terms, "."),
)
}
}

impl PubGrubReportFormatter<'_> {
/// Format two external incompatibilities, combining them if possible.
fn format_both_external(
&self,
external1: &External<PubGrubPackage, Range<Version>>,
external2: &External<PubGrubPackage, Range<Version>>,
) -> String {
match (external1, external2) {
(
External::FromDependencyOf(package1, package_set1, dependency1, dependency_set1),
External::FromDependencyOf(package2, _, dependency2, dependency_set2),
) if package1 == package2 => {
let dependency_set1 = self.simplify_set(dependency_set1, dependency1);
let dependency1 = PackageRange::dependency(dependency1, &dependency_set1);

let dependency_set2 = self.simplify_set(dependency_set2, dependency2);
let dependency2 = PackageRange::dependency(dependency2, &dependency_set2);

match package1 {
PubGrubPackage::Root(Some(name)) => format!(
"{name} depends on {}and {}",
Padded::new("", &dependency1, " "),
dependency2,
),
PubGrubPackage::Root(None) => format!(
"you require {}and {}",
Padded::new("", &dependency1, " "),
dependency2,
),
_ => {
let package_set = self.simplify_set(package_set1, package1);

format!(
"{}",
PackageRange::compatibility(package1, &package_set)
.depends_on(dependency1.package, &dependency_set1)
.and(dependency2.package, &dependency_set2),
)
}
}
}
_ => {
let external1 = self.format_external(external1);
let external2 = self.format_external(external2);

format!(
"{}and {}",
Padded::from_string("", &external1, " "),
&external2,
)
}
}
}

/// Simplify a [`Range`] of versions using the available versions for a package.
fn simplify_set<'a>(
&self,
Expand Down Expand Up @@ -806,34 +855,57 @@ impl PackageRange<'_> {
kind: PackageRangeKind::Available,
}
}

fn depends_on<'a>(
&'a self,
package: &'a PubGrubPackage,
range: &'a Range<Version>,
) -> DependsOn<'a> {
DependsOn {
first: self,
second: PackageRange::dependency(package, range),
package: self,
dependency1: PackageRange::dependency(package, range),
dependency2: None,
}
}
}

/// A representation of A depends on B.
/// A representation of A depends on B (and C).
#[derive(Debug)]
struct DependsOn<'a> {
first: &'a PackageRange<'a>,
second: PackageRange<'a>,
package: &'a PackageRange<'a>,
dependency1: PackageRange<'a>,
dependency2: Option<PackageRange<'a>>,
}

impl<'a> DependsOn<'a> {
/// Adds an additional dependency.
///
/// Note this overwrites previous calls to `DependsOn::and`.
fn and(mut self, package: &'a PubGrubPackage, range: &'a Range<Version>) -> DependsOn<'a> {
self.dependency2 = Some(PackageRange::dependency(package, range));
self
}
}

impl std::fmt::Display for DependsOn<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Padded::new("", self.first, " "))?;
if self.first.plural() {
write!(f, "{}", Padded::new("", self.package, " "))?;
if self.package.plural() {
write!(f, "depend on ")?;
} else {
write!(f, "depends on ")?;
};
write!(f, "{}", self.second)?;

match self.dependency2 {
Some(ref dependency2) => write!(
f,
"{}and{}",
Padded::new("", &self.dependency1, " "),
Padded::new(" ", &dependency2, "")
)?,
None => write!(f, "{}", self.dependency1)?,
}

Ok(())
}
}
Expand Down
8 changes: 4 additions & 4 deletions crates/uv/tests/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2187,7 +2187,7 @@ dependencies = ["anyio==3.7.0", "anyio==4.0.0"]

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because my-project depends on anyio==3.7.0 and my-project depends on anyio==4.0.0, we can conclude that the requirements are unsatisfiable.
╰─▶ Because my-project depends on anyio==3.7.0 and anyio==4.0.0, we can conclude that the requirements are unsatisfiable.
"###
);

Expand Down Expand Up @@ -5573,7 +5573,7 @@ fn compile_constraints_incompatible_version() -> Result<()> {

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because you require filelock==1.0.0 and you require filelock==3.8.0, we can conclude that the requirements are unsatisfiable.
╰─▶ Because you require filelock==1.0.0 and filelock==3.8.0, we can conclude that the requirements are unsatisfiable.
"###
);

Expand Down Expand Up @@ -5601,7 +5601,7 @@ fn conflicting_url_markers() -> Result<()> {

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because you require filelock==1.0.0 and you require filelock==3.8.0, we can conclude that the requirements are unsatisfiable.
╰─▶ Because you require filelock==1.0.0 and filelock==3.8.0, we can conclude that the requirements are unsatisfiable.
"###
);

Expand Down Expand Up @@ -5749,7 +5749,7 @@ fn override_with_incompatible_constraint() -> Result<()> {

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because you require anyio>=3.0.0 and you require anyio<3.0.0, we can conclude that the requirements are unsatisfiable.
╰─▶ Because you require anyio>=3.0.0 and anyio<3.0.0, we can conclude that the requirements are unsatisfiable.
"###
);

Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ fn no_solution() {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because only flask<=3.0.2 is available and flask==3.0.2 depends on werkzeug>=3.0.0, we can conclude that flask>=3.0.2 depends on werkzeug>=3.0.0.
And because you require flask>=3.0.2 and you require werkzeug<1.0.0, we can conclude that the requirements are unsatisfiable.
And because you require flask>=3.0.2 and werkzeug<1.0.0, we can conclude that the requirements are unsatisfiable.
"###);
}

Expand Down
26 changes: 13 additions & 13 deletions crates/uv/tests/pip_install_scenarios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ fn excluded_only_compatible_version() {
And because you require one of:
package-a<2.0.0
package-a>2.0.0
and you require package-b>=2.0.0,<3.0.0, we can conclude that the requirements are unsatisfiable.
and package-b>=2.0.0,<3.0.0, we can conclude that the requirements are unsatisfiable.
"###);

// Only `a==1.2.0` is available since `a==1.0.0` and `a==3.0.0` require
Expand Down Expand Up @@ -475,7 +475,7 @@ fn dependency_excludes_range_of_compatible_versions() {
package-b<=1.0.0
package-b>=3.0.0

And because you require package-b>=2.0.0,<3.0.0 and you require package-c, we can conclude that the requirements are unsatisfiable.
And because you require package-b>=2.0.0,<3.0.0 and package-c, we can conclude that the requirements are unsatisfiable.
"###);

// Only the `2.x` versions of `a` are available since `a==1.0.0` and `a==3.0.0`
Expand Down Expand Up @@ -600,7 +600,7 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() {
package-b<=1.0.0
package-b>=3.0.0

And because you require package-b>=2.0.0,<3.0.0 and you require package-c, we can conclude that the requirements are unsatisfiable.
And because you require package-b>=2.0.0,<3.0.0 and package-c, we can conclude that the requirements are unsatisfiable.
"###);

// Only the `2.x` versions of `a` are available since `a==1.0.0` and `a==3.0.0`
Expand Down Expand Up @@ -909,7 +909,7 @@ fn extra_incompatible_with_extra() {
× No solution found when resolving dependencies:
╰─▶ Because only package-a[extra-c]==1.0.0 is available and package-a[extra-c]==1.0.0 depends on package-b==2.0.0, we can conclude that all versions of package-a[extra-c] depend on package-b==2.0.0.
And because package-a[extra-b]==1.0.0 depends on package-b==1.0.0 and only package-a[extra-b]==1.0.0 is available, we can conclude that all versions of package-a[extra-b] and all versions of package-a[extra-c] are incompatible.
And because you require package-a[extra-b] and you require package-a[extra-c], we can conclude that the requirements are unsatisfiable.
And because you require package-a[extra-b] and package-a[extra-c], we can conclude that the requirements are unsatisfiable.
"###);

// Because both `extra_b` and `extra_c` are requested and they require incompatible
Expand Down Expand Up @@ -1024,7 +1024,7 @@ fn extra_incompatible_with_root() {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because only package-a[extra]==1.0.0 is available and package-a[extra]==1.0.0 depends on package-b==1.0.0, we can conclude that all versions of package-a[extra] depend on package-b==1.0.0.
And because you require package-a[extra] and you require package-b==2.0.0, we can conclude that the requirements are unsatisfiable.
And because you require package-a[extra] and package-b==2.0.0, we can conclude that the requirements are unsatisfiable.
"###);

// Because the user requested `b==2.0.0` but the requested extra requires
Expand Down Expand Up @@ -1130,7 +1130,7 @@ fn direct_incompatible_versions() {

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because you require package-a==1.0.0 and you require package-a==2.0.0, we can conclude that the requirements are unsatisfiable.
╰─▶ Because you require package-a==1.0.0 and package-a==2.0.0, we can conclude that the requirements are unsatisfiable.
"###);

assert_not_installed(
Expand Down Expand Up @@ -1184,7 +1184,7 @@ fn transitive_incompatible_with_root_version() {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-a==1.0.0 depends on package-b==2.0.0 and only package-a==1.0.0 is available, we can conclude that all versions of package-a depend on package-b==2.0.0.
And because you require package-a and you require package-b==1.0.0, we can conclude that the requirements are unsatisfiable.
And because you require package-a and package-b==1.0.0, we can conclude that the requirements are unsatisfiable.
"###);

assert_not_installed(
Expand Down Expand Up @@ -1243,7 +1243,7 @@ fn transitive_incompatible_with_transitive() {
× No solution found when resolving dependencies:
╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-c==1.0.0, we can conclude that all versions of package-a depend on package-c==1.0.0.
And because package-b==1.0.0 depends on package-c==2.0.0 and only package-b==1.0.0 is available, we can conclude that all versions of package-a and all versions of package-b are incompatible.
And because you require package-a and you require package-b, we can conclude that the requirements are unsatisfiable.
And because you require package-a and package-b, we can conclude that the requirements are unsatisfiable.
"###);

assert_not_installed(
Expand Down Expand Up @@ -1292,7 +1292,7 @@ fn transitive_incompatible_versions() {

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 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.
╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and package-b==2.0.0, we can conclude that package-a==1.0.0 cannot be used.
And because you require package-a==1.0.0, we can conclude that the requirements are unsatisfiable.
"###);

Expand Down Expand Up @@ -1573,7 +1573,7 @@ fn local_transitive_greater_than() {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-a==1.0.0 depends on package-b>2.0.0 and only package-a==1.0.0 is available, we can conclude that all versions of package-a depend on package-b>2.0.0.
And because you require package-a and you require package-b==2.0.0+foo, we can conclude that the requirements are unsatisfiable.
And because you require package-a and package-b==2.0.0+foo, we can conclude that the requirements are unsatisfiable.
"###);

assert_not_installed(
Expand Down Expand Up @@ -1684,7 +1684,7 @@ fn local_transitive_less_than() {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-a==1.0.0 depends on package-b<2.0.0 and only package-a==1.0.0 is available, we can conclude that all versions of package-a depend on package-b<2.0.0.
And because you require package-a and you require package-b==2.0.0+foo, we can conclude that the requirements are unsatisfiable.
And because you require package-a and package-b==2.0.0+foo, we can conclude that the requirements are unsatisfiable.
"###);

assert_not_installed(
Expand Down Expand Up @@ -1843,7 +1843,7 @@ fn local_transitive_conflicting() {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-a==1.0.0 depends on package-b==2.0.0+bar and only package-a==1.0.0 is available, we can conclude that all versions of package-a depend on package-b==2.0.0+bar.
And because you require package-a and you require package-b==2.0.0+foo, we can conclude that the requirements are unsatisfiable.
And because you require package-a and package-b==2.0.0+foo, we can conclude that the requirements are unsatisfiable.
"###);

assert_not_installed(
Expand Down Expand Up @@ -3379,7 +3379,7 @@ fn transitive_prerelease_and_stable_dependency_many_versions() {
╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-c>=2.0.0b1, we can conclude that all versions of package-a depend on package-c>=2.0.0b1.
And because only package-c<2.0.0b1 is available, we can conclude that all versions of package-a depend on package-c>3.0.0.
And because package-b==1.0.0 depends on package-c and only package-b==1.0.0 is available, we can conclude that all versions of package-b and all versions of package-a are incompatible.
And because you require package-a and you require package-b, we can conclude that the requirements are unsatisfiable.
And because you require package-a and package-b, we can conclude that the requirements are unsatisfiable.

hint: package-c was requested with a pre-release marker (e.g., package-c>=2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
Expand Down