Skip to content

Commit

Permalink
Avoid inner vec
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Jan 17, 2025
1 parent 08878b0 commit e5e8e8f
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 24 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/uv-resolver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ rustc-hash = { workspace = true }
same-file = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true }
smallvec = { workspace = true }
textwrap = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
Expand Down
55 changes: 31 additions & 24 deletions crates/uv-resolver/src/marker.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use pubgrub::Ranges;
use smallvec::SmallVec;
use std::ops::Bound;

use uv_pep440::Version;
use uv_pep508::{CanonicalMarkerValueVersion, MarkerTree, MarkerTreeKind};
Expand All @@ -7,75 +9,74 @@ use crate::requires_python::{LowerBound, RequiresPythonRange, UpperBound};

/// Returns the bounding Python versions that can satisfy the [`MarkerTree`], if it's constrained.
pub(crate) fn requires_python(tree: MarkerTree) -> Option<RequiresPythonRange> {
/// A small vector of Python version markers.
type Markers = SmallVec<[Ranges<Version>; 3]>;

/// Collect the Python version markers from the tree.
///
/// Specifically, performs a DFS to collect all Python requirements on the path to every
/// `MarkerTreeKind::True` node.
fn collect_python_markers(
tree: MarkerTree,
markers: &mut Vec<Vec<Ranges<Version>>>,
current_path: &mut Vec<Ranges<Version>>,
) {
fn collect_python_markers(tree: MarkerTree, markers: &mut Markers, range: &Ranges<Version>) {
match tree.kind() {
MarkerTreeKind::True => {
markers.push(current_path.clone());
markers.push(range.clone());
}
MarkerTreeKind::False => {}
MarkerTreeKind::Version(marker) => match marker.key() {
CanonicalMarkerValueVersion::PythonFullVersion => {
for (range, tree) in marker.edges() {
current_path.push(range.clone());
collect_python_markers(tree, markers, current_path);
current_path.pop();
collect_python_markers(tree, markers, range);
}
}
CanonicalMarkerValueVersion::ImplementationVersion => {
for (_, tree) in marker.edges() {
collect_python_markers(tree, markers, current_path);
collect_python_markers(tree, markers, range);
}
}
},
MarkerTreeKind::String(marker) => {
for (_, tree) in marker.children() {
collect_python_markers(tree, markers, current_path);
collect_python_markers(tree, markers, range);
}
}
MarkerTreeKind::In(marker) => {
for (_, tree) in marker.children() {
collect_python_markers(tree, markers, current_path);
collect_python_markers(tree, markers, range);
}
}
MarkerTreeKind::Contains(marker) => {
for (_, tree) in marker.children() {
collect_python_markers(tree, markers, current_path);
collect_python_markers(tree, markers, range);
}
}
MarkerTreeKind::Extra(marker) => {
for (_, tree) in marker.children() {
collect_python_markers(tree, markers, current_path);
collect_python_markers(tree, markers, range);
}
}
}
}

let mut markers = Vec::new();
collect_python_markers(tree, &mut markers, &mut Vec::new());
if tree.is_true() || tree.is_false() {
return None;
}

let mut markers = Markers::new();
collect_python_markers(tree, &mut markers, &Ranges::full());

// If there are no Python version markers, return `None`.
if markers.iter().all(Vec::is_empty) {
if markers.iter().all(|range| {
let Some((lower, upper)) = range.bounding_range() else {
return true;
};
matches!((lower, upper), (Bound::Unbounded, Bound::Unbounded))
}) {
return None;
}

// Take the union of the intersections of the Python version markers.
let range = markers
.into_iter()
.map(|ranges| {
ranges
.into_iter()
.fold(Ranges::full(), |acc: Ranges<Version>, range| {
acc.intersection(&range)
})
})
.fold(Ranges::empty(), |acc: Ranges<Version>, range| {
acc.union(&range)
});
Expand Down Expand Up @@ -173,5 +174,11 @@ mod tests {
*range.upper(),
UpperBound::new(Bound::Excluded(Version::from_str("3.11").unwrap()))
);

// An unbounded range across two specifiers.
let tree =
MarkerTree::from_str("python_full_version > '3.8' or python_full_version <= '3.8'")
.unwrap();
assert_eq!(requires_python(tree), None);
}
}

0 comments on commit e5e8e8f

Please sign in to comment.