Skip to content

Commit 22c7c0b

Browse files
committed
Allow multiple self-dependencies
In uv, we don't use the `DependencyConstraints` map, but pass in the dependencies through an iterator. This means there can be duplicate dependencies in the input. This would previously make `merge_dependents` panic if a package dependent on itself twice with the same range: ```toml [package] name = "foo" version = "0.1.0" dependencies = ["foo", "foo"] ``` The fix is to ignore self-dependencies when merging dependents, given that they are always trivially true or trivially false.
1 parent b90cd72 commit 22c7c0b

File tree

1 file changed

+73
-1
lines changed

1 file changed

+73
-1
lines changed

src/internal/incompatibility.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ impl<P: Package, VS: VersionSet, M: Eq + Clone + Debug + Display> Incompatibilit
180180
return None;
181181
}
182182
let (p1, p2) = self_pkgs;
183+
// We ignore self-dependencies. They are always either trivially true or trivially false,
184+
// as the package version implies whether the constraint will always be fulfilled or always
185+
// violated.
186+
// At time of writing, the public crate API only allowed a map of dependencies,
187+
// meaning it can't hit this branch, which requires two self-dependencies.
188+
if p1 == p2 {
189+
return None;
190+
}
183191
let dep_term = self.get(p2);
184192
// The dependency range for p2 must be the same in both case
185193
// to be able to merge multiple p1 ranges.
@@ -381,10 +389,12 @@ impl<P: Package, VS: VersionSet, M: Eq + Clone + Debug + Display> Incompatibilit
381389
#[cfg(test)]
382390
pub(crate) mod tests {
383391
use proptest::prelude::*;
392+
use std::cmp::Reverse;
393+
use std::collections::BTreeMap;
384394

385395
use super::*;
386396
use crate::term::tests::strategy as term_strat;
387-
use crate::Ranges;
397+
use crate::{OfflineDependencyProvider, Ranges, State};
388398

389399
proptest! {
390400

@@ -421,4 +431,66 @@ pub(crate) mod tests {
421431
}
422432

423433
}
434+
435+
/// Check that multiple self-dependencies are supported.
436+
///
437+
/// The current public API deduplicates dependencies through a map, so we test them here
438+
/// manually.
439+
///
440+
/// https://github.com/astral-sh/uv/issues/13344
441+
#[test]
442+
fn package_depend_on_self() {
443+
let cases: &[Vec<(String, Ranges<usize>)>] = &[
444+
vec![("foo".to_string(), Ranges::full())],
445+
vec![
446+
("foo".to_string(), Ranges::full()),
447+
("foo".to_string(), Ranges::full()),
448+
],
449+
vec![
450+
("foo".to_string(), Ranges::full()),
451+
("foo".to_string(), Ranges::singleton(1usize)),
452+
],
453+
vec![
454+
("foo".to_string(), Ranges::singleton(1usize)),
455+
("foo".to_string(), Ranges::from_range_bounds(1usize..2)),
456+
("foo".to_string(), Ranges::from_range_bounds(1usize..3)),
457+
],
458+
];
459+
460+
for case in cases {
461+
let mut state: State<OfflineDependencyProvider<String, Ranges<usize>>> =
462+
State::init("root".to_string(), 0);
463+
state.unit_propagation(state.root_package).unwrap();
464+
465+
// Add the root package
466+
state.add_package_version_dependencies(
467+
state.root_package,
468+
0,
469+
[("foo".to_string(), Ranges::singleton(1usize))],
470+
);
471+
state.unit_propagation(state.root_package).unwrap();
472+
473+
// Add a package that depends on itself twice
474+
let (next, _) = state
475+
.partial_solution
476+
.pick_highest_priority_pkg(|_p, _r| (0, Reverse(0)))
477+
.unwrap();
478+
state.add_package_version_dependencies(next, 1, case.clone());
479+
state.unit_propagation(next).unwrap();
480+
481+
assert!(state
482+
.partial_solution
483+
.pick_highest_priority_pkg(|_p, _r| (0, Reverse(0)))
484+
.is_none());
485+
486+
let solution: BTreeMap<String, usize> = state
487+
.partial_solution
488+
.extract_solution()
489+
.map(|(p, v)| (state.package_store[p].clone(), v))
490+
.collect();
491+
let expected = BTreeMap::from([("root".to_string(), 0), ("foo".to_string(), 1)]);
492+
493+
assert_eq!(solution, expected, "{:?}", case);
494+
}
495+
}
424496
}

0 commit comments

Comments
 (0)