Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid 'are incompatible' for singular bounded versions #4003

Merged
merged 1 commit into from
Jun 4, 2024
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
44 changes: 37 additions & 7 deletions crates/uv-resolver/src/pubgrub/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,15 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
_ => (),
}
}
result.push_str(" are incompatible");
if let [(p, t)] = slice {
if PackageTerm::new(p, t).plural() {
result.push_str(" are incompatible");
} else {
result.push_str(" is incompatible");
}
} else {
result.push_str(" are incompatible");
}
result
}
}
Expand Down Expand Up @@ -749,13 +757,28 @@ impl std::fmt::Display for PackageTerm<'_> {
}

impl PackageTerm<'_> {
/// Create a new [`PackageTerm`] from a [`PubGrubPackage`] and a [`Term`].
fn new<'a>(package: &'a PubGrubPackage, term: &'a Term<Range<Version>>) -> PackageTerm<'a> {
PackageTerm { package, term }
}

/// Returns `true` if the predicate following this package term should be singular or plural.
fn plural(&self) -> bool {
match self.term {
Term::Positive(set) => PackageRange::compatibility(self.package, set).plural(),
Term::Negative(set) => {
if set.as_singleton().is_some() {
false
} else {
PackageRange::compatibility(self.package, &set.complement()).plural()
}
}
}
}
}

/// The kind of version ranges being displayed in [`PackageRange`]
#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PackageRangeKind {
Dependency,
Compatibility,
Expand All @@ -775,12 +798,19 @@ impl PackageRange<'_> {
/// be singular or plural e.g. if false use "<range> depends on <...>" and
/// if true use "<range> depend on <...>"
fn plural(&self) -> bool {
if self.range.is_empty() {
false
let mut segments = self.range.iter();
if let Some(segment) = segments.next() {
// A single unbounded compatibility segment is always plural ("all versions of").
if self.kind == PackageRangeKind::Compatibility {
if matches!(segment, (Bound::Unbounded, Bound::Unbounded)) {
return true;
}
}
// Otherwise, multiple segments are always plural.
segments.next().is_some()
} else {
let segments: Vec<_> = self.range.iter().collect();
// "all versions of" is the only plural case
matches!(segments.as_slice(), [(Bound::Unbounded, Bound::Unbounded)])
// An empty range is always singular.
false
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ fn lock_requires_python() -> Result<()> {
----- stderr -----
warning: `uv lock` is experimental and may change without warning.
× No solution found when resolving dependencies:
╰─▶ Because the requested Python version (>=3.7) does not satisfy Python>=3.8 and the requested Python version (>=3.7) does not satisfy Python>=3.7.9,<3.8, we can conclude that Python>=3.7.9 are incompatible.
╰─▶ Because the requested Python version (>=3.7) does not satisfy Python>=3.8 and the requested Python version (>=3.7) does not satisfy Python>=3.7.9,<3.8, we can conclude that Python>=3.7.9 is incompatible.
And because pygls>=1.1.0,<=1.2.1 depends on Python>=3.7.9,<4 and only pygls<=1.3.0 is available, we can conclude that any of:
pygls>=1.1.0,<1.3.0
pygls>1.3.0
Expand Down
6 changes: 3 additions & 3 deletions crates/uv/tests/pip_install_scenarios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ fn excluded_only_compatible_version() {
And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that any of:
package-a<2.0.0
package-a>2.0.0
depends on one of:
depend on one of:
package-b==1.0.0
package-b==3.0.0

Expand Down Expand Up @@ -4011,14 +4011,14 @@ fn python_greater_than_current_excluded() {
Python>=3.10,<3.11
Python>=3.12
are incompatible.
And because the current Python version (3.9.[X]) does not satisfy Python>=3.11,<3.12, we can conclude that Python>=3.10 are incompatible.
And because the current Python version (3.9.[X]) does not satisfy Python>=3.11,<3.12, we can conclude that Python>=3.10 is incompatible.
And because package-a==2.0.0 depends on Python>=3.10 and only the following versions of package-a are available:
package-a<=2.0.0
package-a==3.0.0
package-a==4.0.0
we can conclude that package-a>=2.0.0,<3.0.0 cannot be used. (1)

Because the current Python version (3.9.[X]) does not satisfy Python>=3.11,<3.12 and the current Python version (3.9.[X]) does not satisfy Python>=3.12, we can conclude that Python>=3.11 are incompatible.
Because the current Python version (3.9.[X]) does not satisfy Python>=3.11,<3.12 and the current Python version (3.9.[X]) does not satisfy Python>=3.12, we can conclude that Python>=3.11 is incompatible.
And because package-a==3.0.0 depends on Python>=3.11, we can conclude that package-a==3.0.0 cannot be used.
And because we know from (1) that package-a>=2.0.0,<3.0.0 cannot be used, we can conclude that package-a>=2.0.0,<4.0.0 cannot be used. (2)

Expand Down
Loading