mirror of https://github.com/astral-sh/uv
Start using the version ranges crate (#8667)
This commit is contained in:
parent
a56e521179
commit
36102dbd0e
|
|
@ -2498,13 +2498,14 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pubgrub"
|
name = "pubgrub"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "git+https://github.com/astral-sh/pubgrub?rev=7243f4faf8e54837aa8a401a18406e7173de4ad5#7243f4faf8e54837aa8a401a18406e7173de4ad5"
|
source = "git+https://github.com/astral-sh/pubgrub?rev=95e1390399cdddee986b658be19587eb1fdb2d79#95e1390399cdddee986b658be19587eb1fdb2d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"priority-queue",
|
"priority-queue",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"version-ranges",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4885,7 +4886,6 @@ dependencies = [
|
||||||
"insta",
|
"insta",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"log",
|
"log",
|
||||||
"pubgrub",
|
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"schemars",
|
"schemars",
|
||||||
|
|
@ -4901,6 +4901,7 @@ dependencies = [
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-pep440",
|
"uv-pep440",
|
||||||
"uv-pubgrub",
|
"uv-pubgrub",
|
||||||
|
"version-ranges",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4933,9 +4934,9 @@ name = "uv-pubgrub"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"pubgrub",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uv-pep440",
|
"uv-pep440",
|
||||||
|
"version-ranges",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5376,6 +5377,14 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version-ranges"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/astral-sh/pubgrub?rev=95e1390399cdddee986b658be19587eb1fdb2d79#95e1390399cdddee986b658be19587eb1fdb2d79"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,8 @@ pathdiff = { version = "0.2.1" }
|
||||||
petgraph = { version = "0.6.5" }
|
petgraph = { version = "0.6.5" }
|
||||||
platform-info = { version = "2.0.3" }
|
platform-info = { version = "2.0.3" }
|
||||||
proc-macro2 = { version = "1.0.86" }
|
proc-macro2 = { version = "1.0.86" }
|
||||||
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "7243f4faf8e54837aa8a401a18406e7173de4ad5" }
|
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "95e1390399cdddee986b658be19587eb1fdb2d79" }
|
||||||
|
version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "95e1390399cdddee986b658be19587eb1fdb2d79" }
|
||||||
quote = { version = "1.0.37" }
|
quote = { version = "1.0.37" }
|
||||||
rayon = { version = "1.10.0" }
|
rayon = { version = "1.10.0" }
|
||||||
reflink-copy = { version = "0.1.19" }
|
reflink-copy = { version = "0.1.19" }
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ uv-pubgrub = { workspace = true }
|
||||||
boxcar = { workspace = true }
|
boxcar = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
pubgrub = { workspace = true }
|
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
schemars = { workspace = true, optional = true }
|
schemars = { workspace = true, optional = true }
|
||||||
|
|
@ -40,6 +39,7 @@ thiserror = { workspace = true }
|
||||||
tracing = { workspace = true, optional = true }
|
tracing = { workspace = true, optional = true }
|
||||||
unicode-width = { workspace = true }
|
unicode-width = { workspace = true }
|
||||||
url = { workspace = true, features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
|
version-ranges = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = { version = "1.40.0" }
|
insta = { version = "1.40.0" }
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,12 @@ use std::sync::Mutex;
|
||||||
use std::sync::MutexGuard;
|
use std::sync::MutexGuard;
|
||||||
|
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
use pubgrub::Range;
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use uv_pep440::Operator;
|
use uv_pep440::Operator;
|
||||||
use uv_pep440::{Version, VersionSpecifier};
|
use uv_pep440::{Version, VersionSpecifier};
|
||||||
use uv_pubgrub::PubGrubSpecifier;
|
use uv_pubgrub::PubGrubSpecifier;
|
||||||
|
use version_ranges::Ranges;
|
||||||
|
|
||||||
use crate::marker::MarkerValueExtra;
|
use crate::marker::MarkerValueExtra;
|
||||||
use crate::ExtraOperator;
|
use crate::ExtraOperator;
|
||||||
|
|
@ -403,7 +403,7 @@ impl InternerGuard<'_> {
|
||||||
});
|
});
|
||||||
return self.create_node(node.var.clone(), children);
|
return self.create_node(node.var.clone(), children);
|
||||||
};
|
};
|
||||||
let py_range = Range::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
let py_range = Ranges::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
||||||
if py_range.is_empty() {
|
if py_range.is_empty() {
|
||||||
// Oops, the bounds imply there is nothing that can match,
|
// Oops, the bounds imply there is nothing that can match,
|
||||||
// so we always evaluate to false.
|
// so we always evaluate to false.
|
||||||
|
|
@ -428,12 +428,12 @@ impl InternerGuard<'_> {
|
||||||
// are known to be satisfied.
|
// are known to be satisfied.
|
||||||
let &(ref first_range, first_node_id) = new.first().unwrap();
|
let &(ref first_range, first_node_id) = new.first().unwrap();
|
||||||
let first_upper = first_range.bounding_range().unwrap().1;
|
let first_upper = first_range.bounding_range().unwrap().1;
|
||||||
let clipped = Range::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
let clipped = Ranges::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
||||||
*new.first_mut().unwrap() = (clipped, first_node_id);
|
*new.first_mut().unwrap() = (clipped, first_node_id);
|
||||||
|
|
||||||
let &(ref last_range, last_node_id) = new.last().unwrap();
|
let &(ref last_range, last_node_id) = new.last().unwrap();
|
||||||
let last_lower = last_range.bounding_range().unwrap().0;
|
let last_lower = last_range.bounding_range().unwrap().0;
|
||||||
let clipped = Range::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
let clipped = Ranges::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
||||||
*new.last_mut().unwrap() = (clipped, last_node_id);
|
*new.last_mut().unwrap() = (clipped, last_node_id);
|
||||||
|
|
||||||
self.create_node(node.var.clone(), Edges::Version { edges: new })
|
self.create_node(node.var.clone(), Edges::Version { edges: new })
|
||||||
|
|
@ -459,7 +459,7 @@ impl InternerGuard<'_> {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
let py_range = Range::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
let py_range = Ranges::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
||||||
if py_range.is_empty() {
|
if py_range.is_empty() {
|
||||||
// Oops, the bounds imply there is nothing that can match,
|
// Oops, the bounds imply there is nothing that can match,
|
||||||
// so we always evaluate to false.
|
// so we always evaluate to false.
|
||||||
|
|
@ -521,14 +521,14 @@ impl InternerGuard<'_> {
|
||||||
// adjacent ranges map to the same node, which would not be
|
// adjacent ranges map to the same node, which would not be
|
||||||
// a canonical representation.
|
// a canonical representation.
|
||||||
if exclude_node_id == first_node_id {
|
if exclude_node_id == first_node_id {
|
||||||
let clipped = Range::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
let clipped = Ranges::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
||||||
*new.first_mut().unwrap() = (clipped, first_node_id);
|
*new.first_mut().unwrap() = (clipped, first_node_id);
|
||||||
} else {
|
} else {
|
||||||
let clipped = Range::from_range_bounds((py_lower.cloned(), first_upper.cloned()));
|
let clipped = Ranges::from_range_bounds((py_lower.cloned(), first_upper.cloned()));
|
||||||
*new.first_mut().unwrap() = (clipped, first_node_id);
|
*new.first_mut().unwrap() = (clipped, first_node_id);
|
||||||
|
|
||||||
let py_range_lower =
|
let py_range_lower =
|
||||||
Range::from_range_bounds((py_lower.cloned(), Bound::Unbounded));
|
Ranges::from_range_bounds((py_lower.cloned(), Bound::Unbounded));
|
||||||
new.insert(0, (py_range_lower.complement(), NodeId::FALSE.negate(i)));
|
new.insert(0, (py_range_lower.complement(), NodeId::FALSE.negate(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -539,14 +539,14 @@ impl InternerGuard<'_> {
|
||||||
// same reasoning applies here: to maintain a canonical
|
// same reasoning applies here: to maintain a canonical
|
||||||
// representation.
|
// representation.
|
||||||
if exclude_node_id == last_node_id {
|
if exclude_node_id == last_node_id {
|
||||||
let clipped = Range::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
let clipped = Ranges::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
||||||
*new.last_mut().unwrap() = (clipped, last_node_id);
|
*new.last_mut().unwrap() = (clipped, last_node_id);
|
||||||
} else {
|
} else {
|
||||||
let clipped = Range::from_range_bounds((last_lower.cloned(), py_upper.cloned()));
|
let clipped = Ranges::from_range_bounds((last_lower.cloned(), py_upper.cloned()));
|
||||||
*new.last_mut().unwrap() = (clipped, last_node_id);
|
*new.last_mut().unwrap() = (clipped, last_node_id);
|
||||||
|
|
||||||
let py_range_upper =
|
let py_range_upper =
|
||||||
Range::from_range_bounds((Bound::Unbounded, py_upper.cloned()));
|
Ranges::from_range_bounds((Bound::Unbounded, py_upper.cloned()));
|
||||||
new.push((py_range_upper.complement(), exclude_node_id));
|
new.push((py_range_upper.complement(), exclude_node_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -688,7 +688,7 @@ pub(crate) enum Edges {
|
||||||
// Invariant: All ranges are simple, meaning they can be represented by a bounded
|
// Invariant: All ranges are simple, meaning they can be represented by a bounded
|
||||||
// interval without gaps. Additionally, there are at least two edges in the set.
|
// interval without gaps. Additionally, there are at least two edges in the set.
|
||||||
Version {
|
Version {
|
||||||
edges: SmallVec<(Range<Version>, NodeId)>,
|
edges: SmallVec<(Ranges<Version>, NodeId)>,
|
||||||
},
|
},
|
||||||
// The edges of a string variable, representing a disjoint set of ranges that cover
|
// The edges of a string variable, representing a disjoint set of ranges that cover
|
||||||
// the output space.
|
// the output space.
|
||||||
|
|
@ -696,7 +696,7 @@ pub(crate) enum Edges {
|
||||||
// Invariant: All ranges are simple, meaning they can be represented by a bounded
|
// Invariant: All ranges are simple, meaning they can be represented by a bounded
|
||||||
// interval without gaps. Additionally, there are at least two edges in the set.
|
// interval without gaps. Additionally, there are at least two edges in the set.
|
||||||
String {
|
String {
|
||||||
edges: SmallVec<(Range<String>, NodeId)>,
|
edges: SmallVec<(Ranges<String>, NodeId)>,
|
||||||
},
|
},
|
||||||
// The edges of a boolean variable, representing the values `true` (the `high` child)
|
// The edges of a boolean variable, representing the values `true` (the `high` child)
|
||||||
// and `false` (the `low` child).
|
// and `false` (the `low` child).
|
||||||
|
|
@ -727,13 +727,13 @@ impl Edges {
|
||||||
/// This function will panic for the `In` and `Contains` marker operators, which
|
/// This function will panic for the `In` and `Contains` marker operators, which
|
||||||
/// should be represented as separate boolean variables.
|
/// should be represented as separate boolean variables.
|
||||||
fn from_string(operator: MarkerOperator, value: String) -> Edges {
|
fn from_string(operator: MarkerOperator, value: String) -> Edges {
|
||||||
let range: Range<String> = match operator {
|
let range: Ranges<String> = match operator {
|
||||||
MarkerOperator::Equal => Range::singleton(value),
|
MarkerOperator::Equal => Ranges::singleton(value),
|
||||||
MarkerOperator::NotEqual => Range::singleton(value).complement(),
|
MarkerOperator::NotEqual => Ranges::singleton(value).complement(),
|
||||||
MarkerOperator::GreaterThan => Range::strictly_higher_than(value),
|
MarkerOperator::GreaterThan => Ranges::strictly_higher_than(value),
|
||||||
MarkerOperator::GreaterEqual => Range::higher_than(value),
|
MarkerOperator::GreaterEqual => Ranges::higher_than(value),
|
||||||
MarkerOperator::LessThan => Range::strictly_lower_than(value),
|
MarkerOperator::LessThan => Ranges::strictly_lower_than(value),
|
||||||
MarkerOperator::LessEqual => Range::lower_than(value),
|
MarkerOperator::LessEqual => Ranges::lower_than(value),
|
||||||
MarkerOperator::TildeEqual => unreachable!("string comparisons with ~= are ignored"),
|
MarkerOperator::TildeEqual => unreachable!("string comparisons with ~= are ignored"),
|
||||||
_ => unreachable!("`in` and `contains` are treated as boolean variables"),
|
_ => unreachable!("`in` and `contains` are treated as boolean variables"),
|
||||||
};
|
};
|
||||||
|
|
@ -756,7 +756,7 @@ impl Edges {
|
||||||
///
|
///
|
||||||
/// Only for use when the `key` is a `PythonVersion`. Normalizes to `PythonFullVersion`.
|
/// Only for use when the `key` is a `PythonVersion`. Normalizes to `PythonFullVersion`.
|
||||||
fn from_python_versions(versions: Vec<Version>, negated: bool) -> Result<Edges, NodeId> {
|
fn from_python_versions(versions: Vec<Version>, negated: bool) -> Result<Edges, NodeId> {
|
||||||
let mut range = Range::empty();
|
let mut range = Ranges::empty();
|
||||||
|
|
||||||
// TODO(zanieb): We need to make sure this is performant, repeated unions like this do not
|
// TODO(zanieb): We need to make sure this is performant, repeated unions like this do not
|
||||||
// seem efficient.
|
// seem efficient.
|
||||||
|
|
@ -779,12 +779,12 @@ impl Edges {
|
||||||
|
|
||||||
/// Returns an [`Edges`] where values in the given range are `true`.
|
/// Returns an [`Edges`] where values in the given range are `true`.
|
||||||
fn from_versions(versions: &Vec<Version>, negated: bool) -> Edges {
|
fn from_versions(versions: &Vec<Version>, negated: bool) -> Edges {
|
||||||
let mut range = Range::empty();
|
let mut range = Ranges::empty();
|
||||||
|
|
||||||
// TODO(zanieb): We need to make sure this is performant, repeated unions like this do not
|
// TODO(zanieb): We need to make sure this is performant, repeated unions like this do not
|
||||||
// seem efficient.
|
// seem efficient.
|
||||||
for version in versions {
|
for version in versions {
|
||||||
range = range.union(&Range::singleton(version.clone()));
|
range = range.union(&Ranges::singleton(version.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if negated {
|
if negated {
|
||||||
|
|
@ -797,7 +797,7 @@ impl Edges {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an [`Edges`] where values in the given range are `true`.
|
/// Returns an [`Edges`] where values in the given range are `true`.
|
||||||
fn from_range<T>(range: &Range<T>) -> SmallVec<(Range<T>, NodeId)>
|
fn from_range<T>(range: &Ranges<T>) -> SmallVec<(Ranges<T>, NodeId)>
|
||||||
where
|
where
|
||||||
T: Ord + Clone,
|
T: Ord + Clone,
|
||||||
{
|
{
|
||||||
|
|
@ -805,13 +805,13 @@ impl Edges {
|
||||||
|
|
||||||
// Add the `true` edges.
|
// Add the `true` edges.
|
||||||
for (start, end) in range.iter() {
|
for (start, end) in range.iter() {
|
||||||
let range = Range::from_range_bounds((start.clone(), end.clone()));
|
let range = Ranges::from_range_bounds((start.clone(), end.clone()));
|
||||||
edges.push((range, NodeId::TRUE));
|
edges.push((range, NodeId::TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the `false` edges.
|
// Add the `false` edges.
|
||||||
for (start, end) in range.complement().iter() {
|
for (start, end) in range.complement().iter() {
|
||||||
let range = Range::from_range_bounds((start.clone(), end.clone()));
|
let range = Ranges::from_range_bounds((start.clone(), end.clone()));
|
||||||
edges.push((range, NodeId::FALSE));
|
edges.push((range, NodeId::FALSE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,12 +891,12 @@ impl Edges {
|
||||||
/// In that case, we drop any ranges that do not exist in the domain of both edges. Note that
|
/// In that case, we drop any ranges that do not exist in the domain of both edges. Note that
|
||||||
/// this should not occur in practice because `requires-python` bounds are global.
|
/// this should not occur in practice because `requires-python` bounds are global.
|
||||||
fn apply_ranges<T>(
|
fn apply_ranges<T>(
|
||||||
left_edges: &SmallVec<(Range<T>, NodeId)>,
|
left_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||||
left_parent: NodeId,
|
left_parent: NodeId,
|
||||||
right_edges: &SmallVec<(Range<T>, NodeId)>,
|
right_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||||
right_parent: NodeId,
|
right_parent: NodeId,
|
||||||
mut apply: impl FnMut(NodeId, NodeId) -> NodeId,
|
mut apply: impl FnMut(NodeId, NodeId) -> NodeId,
|
||||||
) -> SmallVec<(Range<T>, NodeId)>
|
) -> SmallVec<(Ranges<T>, NodeId)>
|
||||||
where
|
where
|
||||||
T: Clone + Ord,
|
T: Clone + Ord,
|
||||||
{
|
{
|
||||||
|
|
@ -968,9 +968,9 @@ impl Edges {
|
||||||
|
|
||||||
// Returns `true` if all intersecting ranges in two range maps are disjoint.
|
// Returns `true` if all intersecting ranges in two range maps are disjoint.
|
||||||
fn is_disjoint_ranges<T>(
|
fn is_disjoint_ranges<T>(
|
||||||
left_edges: &SmallVec<(Range<T>, NodeId)>,
|
left_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||||
left_parent: NodeId,
|
left_parent: NodeId,
|
||||||
right_edges: &SmallVec<(Range<T>, NodeId)>,
|
right_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||||
right_parent: NodeId,
|
right_parent: NodeId,
|
||||||
interner: &mut InternerGuard<'_>,
|
interner: &mut InternerGuard<'_>,
|
||||||
) -> bool
|
) -> bool
|
||||||
|
|
@ -1179,7 +1179,7 @@ fn python_version_to_full_version(specifier: VersionSpecifier) -> Result<Version
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares the start of two ranges that are known to be disjoint.
|
/// Compares the start of two ranges that are known to be disjoint.
|
||||||
fn compare_disjoint_range_start<T>(range1: &Range<T>, range2: &Range<T>) -> Ordering
|
fn compare_disjoint_range_start<T>(range1: &Ranges<T>, range2: &Ranges<T>) -> Ordering
|
||||||
where
|
where
|
||||||
T: Ord,
|
T: Ord,
|
||||||
{
|
{
|
||||||
|
|
@ -1199,7 +1199,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if two disjoint ranges can be conjoined seamlessly without introducing a gap.
|
/// Returns `true` if two disjoint ranges can be conjoined seamlessly without introducing a gap.
|
||||||
fn can_conjoin<T>(range1: &Range<T>, range2: &Range<T>) -> bool
|
fn can_conjoin<T>(range1: &Ranges<T>, range2: &Ranges<T>) -> bool
|
||||||
where
|
where
|
||||||
T: Ord + Clone,
|
T: Ord + Clone,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ use std::ops::Bound;
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use pubgrub::Range;
|
|
||||||
use rustc_hash::FxBuildHasher;
|
use rustc_hash::FxBuildHasher;
|
||||||
use uv_pep440::{Version, VersionSpecifier};
|
use uv_pep440::{Version, VersionSpecifier};
|
||||||
|
use version_ranges::Ranges;
|
||||||
|
|
||||||
use crate::{ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeKind};
|
use crate::{ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeKind};
|
||||||
|
|
||||||
|
|
@ -280,17 +280,17 @@ fn simplify(dnf: &mut Vec<Vec<MarkerExpression>>) {
|
||||||
|
|
||||||
/// Merge any edges that lead to identical subtrees into a single range.
|
/// Merge any edges that lead to identical subtrees into a single range.
|
||||||
pub(crate) fn collect_edges<'a, T>(
|
pub(crate) fn collect_edges<'a, T>(
|
||||||
map: impl ExactSizeIterator<Item = (&'a Range<T>, MarkerTree)>,
|
map: impl ExactSizeIterator<Item = (&'a Ranges<T>, MarkerTree)>,
|
||||||
) -> IndexMap<MarkerTree, Range<T>, FxBuildHasher>
|
) -> IndexMap<MarkerTree, Ranges<T>, FxBuildHasher>
|
||||||
where
|
where
|
||||||
T: Ord + Clone + 'a,
|
T: Ord + Clone + 'a,
|
||||||
{
|
{
|
||||||
let mut paths: IndexMap<_, Range<_>, FxBuildHasher> = IndexMap::default();
|
let mut paths: IndexMap<_, Ranges<_>, FxBuildHasher> = IndexMap::default();
|
||||||
for (range, tree) in map {
|
for (range, tree) in map {
|
||||||
// OK because all ranges are guaranteed to be non-empty.
|
// OK because all ranges are guaranteed to be non-empty.
|
||||||
let (start, end) = range.bounding_range().unwrap();
|
let (start, end) = range.bounding_range().unwrap();
|
||||||
// Combine the ranges.
|
// Combine the ranges.
|
||||||
let range = Range::from_range_bounds((start.cloned(), end.cloned()));
|
let range = Ranges::from_range_bounds((start.cloned(), end.cloned()));
|
||||||
paths
|
paths
|
||||||
.entry(tree)
|
.entry(tree)
|
||||||
.and_modify(|union| *union = union.union(&range))
|
.and_modify(|union| *union = union.union(&range))
|
||||||
|
|
@ -305,7 +305,7 @@ where
|
||||||
///
|
///
|
||||||
/// For example, `os_name < 'Linux' or os_name > 'Linux'` can be simplified to
|
/// For example, `os_name < 'Linux' or os_name > 'Linux'` can be simplified to
|
||||||
/// `os_name != 'Linux'`.
|
/// `os_name != 'Linux'`.
|
||||||
fn range_inequality<T>(range: &Range<T>) -> Option<Vec<&T>>
|
fn range_inequality<T>(range: &Ranges<T>) -> Option<Vec<&T>>
|
||||||
where
|
where
|
||||||
T: Ord + Clone + fmt::Debug,
|
T: Ord + Clone + fmt::Debug,
|
||||||
{
|
{
|
||||||
|
|
@ -329,7 +329,7 @@ where
|
||||||
///
|
///
|
||||||
/// For example, `python_full_version < '3.8' or python_full_version >= '3.9'` can be simplified to
|
/// For example, `python_full_version < '3.8' or python_full_version >= '3.9'` can be simplified to
|
||||||
/// `python_full_version != '3.8.*'`.
|
/// `python_full_version != '3.8.*'`.
|
||||||
fn star_range_inequality(range: &Range<Version>) -> Option<VersionSpecifier> {
|
fn star_range_inequality(range: &Ranges<Version>) -> Option<VersionSpecifier> {
|
||||||
let (b1, b2) = range.iter().collect_tuple()?;
|
let (b1, b2) = range.iter().collect_tuple()?;
|
||||||
|
|
||||||
match (b1, b2) {
|
match (b1, b2) {
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ use std::ops::{Bound, Deref};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use pubgrub::Range;
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use uv_normalize::ExtraName;
|
use uv_normalize::ExtraName;
|
||||||
use uv_pep440::{Version, VersionParseError, VersionSpecifier};
|
use uv_pep440::{Version, VersionParseError, VersionSpecifier};
|
||||||
|
use version_ranges::Ranges;
|
||||||
|
|
||||||
use crate::cursor::Cursor;
|
use crate::cursor::Cursor;
|
||||||
use crate::marker::parse;
|
use crate::marker::parse;
|
||||||
|
|
@ -1396,7 +1396,7 @@ pub enum MarkerTreeKind<'a> {
|
||||||
pub struct VersionMarkerTree<'a> {
|
pub struct VersionMarkerTree<'a> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
key: MarkerValueVersion,
|
key: MarkerValueVersion,
|
||||||
map: &'a [(Range<Version>, NodeId)],
|
map: &'a [(Ranges<Version>, NodeId)],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VersionMarkerTree<'_> {
|
impl VersionMarkerTree<'_> {
|
||||||
|
|
@ -1406,7 +1406,7 @@ impl VersionMarkerTree<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The edges of this node, corresponding to possible output ranges of the given variable.
|
/// The edges of this node, corresponding to possible output ranges of the given variable.
|
||||||
pub fn edges(&self) -> impl ExactSizeIterator<Item = (&Range<Version>, MarkerTree)> + '_ {
|
pub fn edges(&self) -> impl ExactSizeIterator<Item = (&Ranges<Version>, MarkerTree)> + '_ {
|
||||||
self.map
|
self.map
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
|
.map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
|
||||||
|
|
@ -1432,7 +1432,7 @@ impl Ord for VersionMarkerTree<'_> {
|
||||||
pub struct StringMarkerTree<'a> {
|
pub struct StringMarkerTree<'a> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
key: MarkerValueString,
|
key: MarkerValueString,
|
||||||
map: &'a [(Range<String>, NodeId)],
|
map: &'a [(Ranges<String>, NodeId)],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringMarkerTree<'_> {
|
impl StringMarkerTree<'_> {
|
||||||
|
|
@ -1442,7 +1442,7 @@ impl StringMarkerTree<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The edges of this node, corresponding to possible output ranges of the given variable.
|
/// The edges of this node, corresponding to possible output ranges of the given variable.
|
||||||
pub fn children(&self) -> impl ExactSizeIterator<Item = (&Range<String>, MarkerTree)> {
|
pub fn children(&self) -> impl ExactSizeIterator<Item = (&Ranges<String>, MarkerTree)> {
|
||||||
self.map
|
self.map
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
|
.map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,4 @@ uv-pep440 = { workspace = true }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
pubgrub = { workspace = true }
|
version-ranges = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use pubgrub::Range;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use version_ranges::Ranges;
|
||||||
|
|
||||||
use uv_pep440::{Operator, Prerelease, Version, VersionSpecifier, VersionSpecifiers};
|
use uv_pep440::{Operator, Prerelease, Version, VersionSpecifier, VersionSpecifiers};
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub enum PubGrubSpecifierError {
|
||||||
|
|
||||||
/// A range of versions that can be used to satisfy a requirement.
|
/// A range of versions that can be used to satisfy a requirement.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PubGrubSpecifier(Range<Version>);
|
pub struct PubGrubSpecifier(Ranges<Version>);
|
||||||
|
|
||||||
impl PubGrubSpecifier {
|
impl PubGrubSpecifier {
|
||||||
/// Returns an iterator over the bounds of the [`PubGrubSpecifier`].
|
/// Returns an iterator over the bounds of the [`PubGrubSpecifier`].
|
||||||
|
|
@ -22,19 +22,19 @@ impl PubGrubSpecifier {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the bounding [`Range`] of the [`PubGrubSpecifier`].
|
/// Return the bounding [`Ranges`] of the [`PubGrubSpecifier`].
|
||||||
pub fn bounding_range(&self) -> Option<(Bound<&Version>, Bound<&Version>)> {
|
pub fn bounding_range(&self) -> Option<(Bound<&Version>, Bound<&Version>)> {
|
||||||
self.0.bounding_range()
|
self.0.bounding_range()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Range<Version>> for PubGrubSpecifier {
|
impl From<Ranges<Version>> for PubGrubSpecifier {
|
||||||
fn from(range: Range<Version>) -> Self {
|
fn from(range: Ranges<Version>) -> Self {
|
||||||
PubGrubSpecifier(range)
|
PubGrubSpecifier(range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PubGrubSpecifier> for Range<Version> {
|
impl From<PubGrubSpecifier> for Ranges<Version> {
|
||||||
/// Convert a PubGrub specifier to a range of versions.
|
/// Convert a PubGrub specifier to a range of versions.
|
||||||
fn from(specifier: PubGrubSpecifier) -> Self {
|
fn from(specifier: PubGrubSpecifier) -> Self {
|
||||||
specifier.0
|
specifier.0
|
||||||
|
|
@ -50,7 +50,7 @@ impl PubGrubSpecifier {
|
||||||
let range = specifiers
|
let range = specifiers
|
||||||
.iter()
|
.iter()
|
||||||
.map(Self::from_pep440_specifier)
|
.map(Self::from_pep440_specifier)
|
||||||
.fold_ok(Range::full(), |range, specifier| {
|
.fold_ok(Ranges::full(), |range, specifier| {
|
||||||
range.intersection(&specifier.into())
|
range.intersection(&specifier.into())
|
||||||
})?;
|
})?;
|
||||||
Ok(Self(range))
|
Ok(Self(range))
|
||||||
|
|
@ -64,15 +64,15 @@ impl PubGrubSpecifier {
|
||||||
let ranges = match specifier.operator() {
|
let ranges = match specifier.operator() {
|
||||||
Operator::Equal => {
|
Operator::Equal => {
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
Range::singleton(version)
|
Ranges::singleton(version)
|
||||||
}
|
}
|
||||||
Operator::ExactEqual => {
|
Operator::ExactEqual => {
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
Range::singleton(version)
|
Ranges::singleton(version)
|
||||||
}
|
}
|
||||||
Operator::NotEqual => {
|
Operator::NotEqual => {
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
Range::singleton(version).complement()
|
Ranges::singleton(version).complement()
|
||||||
}
|
}
|
||||||
Operator::TildeEqual => {
|
Operator::TildeEqual => {
|
||||||
let [rest @ .., last, _] = specifier.version().release() else {
|
let [rest @ .., last, _] = specifier.version().release() else {
|
||||||
|
|
@ -82,38 +82,38 @@ impl PubGrubSpecifier {
|
||||||
.with_epoch(specifier.version().epoch())
|
.with_epoch(specifier.version().epoch())
|
||||||
.with_dev(Some(0));
|
.with_dev(Some(0));
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
Range::from_range_bounds(version..upper)
|
Ranges::from_range_bounds(version..upper)
|
||||||
}
|
}
|
||||||
Operator::LessThan => {
|
Operator::LessThan => {
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
if version.any_prerelease() {
|
if version.any_prerelease() {
|
||||||
Range::strictly_lower_than(version)
|
Ranges::strictly_lower_than(version)
|
||||||
} else {
|
} else {
|
||||||
// Per PEP 440: "The exclusive ordered comparison <V MUST NOT allow a
|
// Per PEP 440: "The exclusive ordered comparison <V MUST NOT allow a
|
||||||
// pre-release of the specified version unless the specified version is itself a
|
// pre-release of the specified version unless the specified version is itself a
|
||||||
// pre-release."
|
// pre-release."
|
||||||
Range::strictly_lower_than(version.with_min(Some(0)))
|
Ranges::strictly_lower_than(version.with_min(Some(0)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::LessThanEqual => {
|
Operator::LessThanEqual => {
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
Range::lower_than(version)
|
Ranges::lower_than(version)
|
||||||
}
|
}
|
||||||
Operator::GreaterThan => {
|
Operator::GreaterThan => {
|
||||||
// Per PEP 440: "The exclusive ordered comparison >V MUST NOT allow a post-release of
|
// Per PEP 440: "The exclusive ordered comparison >V MUST NOT allow a post-release of
|
||||||
// the given version unless V itself is a post release."
|
// the given version unless V itself is a post release."
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
if let Some(dev) = version.dev() {
|
if let Some(dev) = version.dev() {
|
||||||
Range::higher_than(version.with_dev(Some(dev + 1)))
|
Ranges::higher_than(version.with_dev(Some(dev + 1)))
|
||||||
} else if let Some(post) = version.post() {
|
} else if let Some(post) = version.post() {
|
||||||
Range::higher_than(version.with_post(Some(post + 1)))
|
Ranges::higher_than(version.with_post(Some(post + 1)))
|
||||||
} else {
|
} else {
|
||||||
Range::strictly_higher_than(version.with_max(Some(0)))
|
Ranges::strictly_higher_than(version.with_max(Some(0)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::GreaterThanEqual => {
|
Operator::GreaterThanEqual => {
|
||||||
let version = specifier.version().clone();
|
let version = specifier.version().clone();
|
||||||
Range::higher_than(version)
|
Ranges::higher_than(version)
|
||||||
}
|
}
|
||||||
Operator::EqualStar => {
|
Operator::EqualStar => {
|
||||||
let low = specifier.version().clone().with_dev(Some(0));
|
let low = specifier.version().clone().with_dev(Some(0));
|
||||||
|
|
@ -130,7 +130,7 @@ impl PubGrubSpecifier {
|
||||||
*release.last_mut().unwrap() += 1;
|
*release.last_mut().unwrap() += 1;
|
||||||
high = high.with_release(release);
|
high = high.with_release(release);
|
||||||
}
|
}
|
||||||
Range::from_range_bounds(low..high)
|
Ranges::from_range_bounds(low..high)
|
||||||
}
|
}
|
||||||
Operator::NotEqualStar => {
|
Operator::NotEqualStar => {
|
||||||
let low = specifier.version().clone().with_dev(Some(0));
|
let low = specifier.version().clone().with_dev(Some(0));
|
||||||
|
|
@ -147,7 +147,7 @@ impl PubGrubSpecifier {
|
||||||
*release.last_mut().unwrap() += 1;
|
*release.last_mut().unwrap() += 1;
|
||||||
high = high.with_release(release);
|
high = high.with_release(release);
|
||||||
}
|
}
|
||||||
Range::from_range_bounds(low..high).complement()
|
Ranges::from_range_bounds(low..high).complement()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -171,7 +171,7 @@ impl PubGrubSpecifier {
|
||||||
let range = specifiers
|
let range = specifiers
|
||||||
.iter()
|
.iter()
|
||||||
.map(Self::from_release_specifier)
|
.map(Self::from_release_specifier)
|
||||||
.fold_ok(Range::full(), |range, specifier| {
|
.fold_ok(Ranges::full(), |range, specifier| {
|
||||||
range.intersection(&specifier.into())
|
range.intersection(&specifier.into())
|
||||||
})?;
|
})?;
|
||||||
Ok(Self(range))
|
Ok(Self(range))
|
||||||
|
|
@ -194,15 +194,15 @@ impl PubGrubSpecifier {
|
||||||
let ranges = match specifier.operator() {
|
let ranges = match specifier.operator() {
|
||||||
Operator::Equal => {
|
Operator::Equal => {
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::singleton(version)
|
Ranges::singleton(version)
|
||||||
}
|
}
|
||||||
Operator::ExactEqual => {
|
Operator::ExactEqual => {
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::singleton(version)
|
Ranges::singleton(version)
|
||||||
}
|
}
|
||||||
Operator::NotEqual => {
|
Operator::NotEqual => {
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::singleton(version).complement()
|
Ranges::singleton(version).complement()
|
||||||
}
|
}
|
||||||
Operator::TildeEqual => {
|
Operator::TildeEqual => {
|
||||||
let [rest @ .., last, _] = specifier.version().release() else {
|
let [rest @ .., last, _] = specifier.version().release() else {
|
||||||
|
|
@ -210,23 +210,23 @@ impl PubGrubSpecifier {
|
||||||
};
|
};
|
||||||
let upper = Version::new(rest.iter().chain([&(last + 1)]));
|
let upper = Version::new(rest.iter().chain([&(last + 1)]));
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::from_range_bounds(version..upper)
|
Ranges::from_range_bounds(version..upper)
|
||||||
}
|
}
|
||||||
Operator::LessThan => {
|
Operator::LessThan => {
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::strictly_lower_than(version)
|
Ranges::strictly_lower_than(version)
|
||||||
}
|
}
|
||||||
Operator::LessThanEqual => {
|
Operator::LessThanEqual => {
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::lower_than(version)
|
Ranges::lower_than(version)
|
||||||
}
|
}
|
||||||
Operator::GreaterThan => {
|
Operator::GreaterThan => {
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::strictly_higher_than(version)
|
Ranges::strictly_higher_than(version)
|
||||||
}
|
}
|
||||||
Operator::GreaterThanEqual => {
|
Operator::GreaterThanEqual => {
|
||||||
let version = specifier.version().only_release();
|
let version = specifier.version().only_release();
|
||||||
Range::higher_than(version)
|
Ranges::higher_than(version)
|
||||||
}
|
}
|
||||||
Operator::EqualStar => {
|
Operator::EqualStar => {
|
||||||
let low = specifier.version().only_release();
|
let low = specifier.version().only_release();
|
||||||
|
|
@ -237,7 +237,7 @@ impl PubGrubSpecifier {
|
||||||
high = high.with_release(release);
|
high = high.with_release(release);
|
||||||
high
|
high
|
||||||
};
|
};
|
||||||
Range::from_range_bounds(low..high)
|
Ranges::from_range_bounds(low..high)
|
||||||
}
|
}
|
||||||
Operator::NotEqualStar => {
|
Operator::NotEqualStar => {
|
||||||
let low = specifier.version().only_release();
|
let low = specifier.version().only_release();
|
||||||
|
|
@ -248,7 +248,7 @@ impl PubGrubSpecifier {
|
||||||
high = high.with_release(release);
|
high = high.with_release(release);
|
||||||
high
|
high
|
||||||
};
|
};
|
||||||
Range::from_range_bounds(low..high).complement()
|
Ranges::from_range_bounds(low..high).complement()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Self(ranges))
|
Ok(Self(ranges))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue