Skip to content

Commit

Permalink
Avoid early error for transistive constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Feb 18, 2024
1 parent b296c04 commit 415d8f1
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 24 deletions.
41 changes: 17 additions & 24 deletions crates/uv-resolver/src/pubgrub/dependencies.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use itertools::Itertools;
use pep440_rs::Version;
use pubgrub::range::Range;
use pubgrub::type_aliases::DependencyConstraints;
use tracing::warn;

use pep440_rs::Version;
use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl};
use uv_normalize::{ExtraName, PackageName};

Expand Down Expand Up @@ -62,14 +62,24 @@ impl PubGrubDependencies {

if let Some(entry) = dependencies.get_key_value(&package) {
// Merge the versions.
let version = merge_versions(&package, entry.1, &version)?;
let intersection = entry.1.intersection(&version);
if intersection.is_empty() {
return Err(ResolveError::ConflictingVersions(
package.to_string(),
format!(
"`{package}{left}` does not intersect with `{package}{right}`",
left = entry.1,
right = version
),
));
}

// Merge the package.
if let Some(package) = merge_package(entry.0, &package)? {
dependencies.remove(&package);
dependencies.insert(package, version);
dependencies.insert(package, intersection);
} else {
dependencies.insert(package, version);
dependencies.insert(package, intersection);
}
} else {
dependencies.insert(package.clone(), version.clone());
Expand Down Expand Up @@ -117,14 +127,14 @@ impl PubGrubDependencies {

if let Some(entry) = dependencies.get_key_value(&package) {
// Merge the versions.
let version = merge_versions(&package, entry.1, &version)?;
let intersection = entry.1.intersection(&version);

// Merge the package.
if let Some(package) = merge_package(entry.0, &package)? {
dependencies.remove(&package);
dependencies.insert(package, version);
dependencies.insert(package, intersection);
} else {
dependencies.insert(package, version);
dependencies.insert(package, intersection);
}
}
}
Expand Down Expand Up @@ -187,23 +197,6 @@ fn to_pubgrub(
}
}

/// Merge two [`Version`] ranges.
fn merge_versions(
package: &PubGrubPackage,
left: &Range<Version>,
right: &Range<Version>,
) -> Result<Range<Version>, ResolveError> {
let result = left.intersection(right);
if result.is_empty() {
Err(ResolveError::ConflictingVersions(
package.to_string(),
format!("`{package}{left}` does not intersect with `{package}{right}`"),
))
} else {
Ok(result)
}
}

/// Merge two [`PubGrubPackage`] instances.
fn merge_package(
left: &PubGrubPackage,
Expand Down
57 changes: 57 additions & 0 deletions crates/uv/tests/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3540,3 +3540,60 @@ fn compile_constraints_incompatible_url() -> Result<()> {

Ok(())
}

/// Resolve a package from a `requirements.in` file, with a `constraints.txt` file pinning one of
/// its transitive dependencies to a specific version.
#[test]
fn compile_constraints_compatible_version() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("virtualenv")?;

let constraints_txt = context.temp_dir.child("constraints.txt");
constraints_txt.write_str("filelock==3.8.0")?;

uv_snapshot!(context.compile()
.arg("requirements.in")
.arg("--constraint")
.arg("constraints.txt"), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because only anyio>=4 is available and you require anyio<4, we can
conclude that the requirements are unsatisfiable.
"###
);

Ok(())
}

/// Resolve a package from a `requirements.in` file, with a `constraints.txt` file pinning one of
/// its direct dependencies to an incompatible version.
#[test]
fn compile_constraints_incompatible_version() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("filelock==1.0.0")?;

let constraints_txt = context.temp_dir.child("constraints.txt");
constraints_txt.write_str("filelock==3.8.0")?;

uv_snapshot!(context.compile()
.arg("requirements.in")
.arg("--constraint")
.arg("constraints.txt"), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ you require ∅
"###
);

Ok(())
}

0 comments on commit 415d8f1

Please sign in to comment.