uv-resolver: normalize marker expressions (#4017)

This is a quick fix for some flaky tests where the output in the lock
file isn't stable because marker expressions can be combined in a
non-deterministic order.

I believe there is ongoing work to simplify marker expressions which
will help here, but I think some kind of normalization is still
ultimately needed to guarantee consistent output.

I first noticed the flaky test in:
https://github.com/astral-sh/uv/pull/4015
This commit is contained in:
Andrew Gallant 2024-06-04 13:45:54 -04:00 committed by GitHub
parent 3b8f3a7f0d
commit 5c30b39fe3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 30 additions and 1 deletions

View file

@ -1881,6 +1881,30 @@ impl MarkerTree {
exprs.push(tree);
}
}
/// Normalizes this marker tree such that all conjunctions and disjunctions
/// are sorted.
///
/// This is useful in cases where creating conjunctions or disjunctions
/// might occur in a non-deterministic order. This routine will erase the
/// distinction created by such a construction.
pub fn normalize(&mut self) {
match *self {
MarkerTree::Expression(_) => {}
MarkerTree::And(ref mut trees) | MarkerTree::Or(ref mut trees) => {
// This is kind of cheesy, because we're doing a recursive call
// followed by a sort, and that sort is also recursive (due to
// the corresponding Ord impl being recursive).
//
// We should consider refactoring `MarkerTree` to a "smart
// constructor" design that normalizes them by construction.
for tree in &mut *trees {
tree.normalize();
}
trees.sort();
}
}
}
}
impl Display for MarkerTree {

View file

@ -356,7 +356,12 @@ pub struct Distribution {
impl Distribution {
fn from_annotated_dist(annotated_dist: &AnnotatedDist) -> Result<Self, LockError> {
let id = DistributionId::from_annotated_dist(annotated_dist);
let marker = annotated_dist.marker.clone();
let mut marker = annotated_dist.marker.clone();
// Markers can be combined in an unpredictable order, so normalize them
// such that the lock file output is consistent and deterministic.
if let Some(ref mut marker) = marker {
marker.normalize();
}
let sdist = SourceDist::from_annotated_dist(annotated_dist)?;
let wheels = Wheel::from_annotated_dist(annotated_dist)?;
Ok(Distribution {