mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-26 12:09:12 +00:00
Implement PEP 440-compliant local version semantics (#8797)
Implement a full working version of local version semantics. The (AFAIA) major move towards this was implemented in #2430. This added support such that the version specifier `torch==2.1.0+cpu` would install `torch@2.1.0+cpu` and consider `torch@2.1.0+cpu` a valid way to satisfy the requirement `torch==2.1.0` in further dependency resolution. In this feature, we more fully support local version semantics. Namely, we now allow `torch==2.1.0` to install `torch@2.1.0+cpu` regardless of whether `torch@2.1.0` (no local tag) actually exists. We do this by adding an internal-only `Max` value to local versions that compare greater to all other local versions. Then we can translate `torch==2.1.0` into bounds: greater than 2.1.0 with no local tag and less than 2.1.0 with the `Max` local tag. Depends on https://github.com/astral-sh/packse/pull/227.
This commit is contained in:
parent
8ef5949294
commit
c49c7bdf97
15 changed files with 631 additions and 840 deletions
|
@ -27,8 +27,9 @@
|
||||||
pub use version_ranges::{release_specifier_to_range, release_specifiers_to_ranges};
|
pub use version_ranges::{release_specifier_to_range, release_specifiers_to_ranges};
|
||||||
pub use {
|
pub use {
|
||||||
version::{
|
version::{
|
||||||
LocalSegment, Operator, OperatorParseError, Prerelease, PrereleaseKind, Version,
|
LocalSegment, LocalVersion, LocalVersionSlice, Operator, OperatorParseError, Prerelease,
|
||||||
VersionParseError, VersionPattern, VersionPatternParseError, MIN_VERSION,
|
PrereleaseKind, Version, VersionParseError, VersionPattern, VersionPatternParseError,
|
||||||
|
MIN_VERSION,
|
||||||
},
|
},
|
||||||
version_specifier::{
|
version_specifier::{
|
||||||
VersionSpecifier, VersionSpecifierBuildError, VersionSpecifiers,
|
VersionSpecifier, VersionSpecifierBuildError, VersionSpecifiers,
|
||||||
|
|
|
@ -388,10 +388,10 @@ impl Version {
|
||||||
|
|
||||||
/// Returns the local segments in this version, if any exist.
|
/// Returns the local segments in this version, if any exist.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn local(&self) -> &[LocalSegment] {
|
pub fn local(&self) -> LocalVersionSlice {
|
||||||
match *self.inner {
|
match *self.inner {
|
||||||
VersionInner::Small { ref small } => small.local(),
|
VersionInner::Small { ref small } => small.local(),
|
||||||
VersionInner::Full { ref full } => &full.local,
|
VersionInner::Full { ref full } => full.local.as_slice(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,15 +530,28 @@ impl Version {
|
||||||
/// Set the local segments and return the updated version.
|
/// Set the local segments and return the updated version.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_local(mut self, value: Vec<LocalSegment>) -> Self {
|
pub fn with_local_segments(mut self, value: Vec<LocalSegment>) -> Self {
|
||||||
if value.is_empty() {
|
if value.is_empty() {
|
||||||
self.without_local()
|
self.without_local()
|
||||||
} else {
|
} else {
|
||||||
self.make_full().local = value;
|
self.make_full().local = LocalVersion::Segments(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the local version and return the updated version.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_local(mut self, value: LocalVersion) -> Self {
|
||||||
|
match value {
|
||||||
|
LocalVersion::Segments(segments) => self.with_local_segments(segments),
|
||||||
|
LocalVersion::Max => {
|
||||||
|
self.make_full().local = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// For PEP 440 specifier matching: "Except where specifically noted below,
|
/// For PEP 440 specifier matching: "Except where specifically noted below,
|
||||||
/// local version identifiers MUST NOT be permitted in version specifiers,
|
/// local version identifiers MUST NOT be permitted in version specifiers,
|
||||||
/// and local version labels MUST be ignored entirely when checking if
|
/// and local version labels MUST be ignored entirely when checking if
|
||||||
|
@ -615,7 +628,7 @@ impl Version {
|
||||||
pre: small.pre(),
|
pre: small.pre(),
|
||||||
post: small.post(),
|
post: small.post(),
|
||||||
dev: small.dev(),
|
dev: small.dev(),
|
||||||
local: vec![],
|
local: LocalVersion::Segments(vec![]),
|
||||||
};
|
};
|
||||||
*self = Self {
|
*self = Self {
|
||||||
inner: Arc::new(VersionInner::Full { full }),
|
inner: Arc::new(VersionInner::Full { full }),
|
||||||
|
@ -712,14 +725,12 @@ impl std::fmt::Display for Version {
|
||||||
let local = if self.local().is_empty() {
|
let local = if self.local().is_empty() {
|
||||||
String::new()
|
String::new()
|
||||||
} else {
|
} else {
|
||||||
format!(
|
match self.local() {
|
||||||
"+{}",
|
LocalVersionSlice::Segments(_) => {
|
||||||
self.local()
|
format!("+{}", self.local())
|
||||||
.iter()
|
}
|
||||||
.map(ToString::to_string)
|
LocalVersionSlice::Max => String::new(),
|
||||||
.collect::<Vec<String>>()
|
}
|
||||||
.join(".")
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
write!(f, "{epoch}{release}{pre}{post}{dev}{local}")
|
write!(f, "{epoch}{release}{pre}{post}{dev}{local}")
|
||||||
}
|
}
|
||||||
|
@ -1195,10 +1206,10 @@ impl VersionSmall {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn local(&self) -> &[LocalSegment] {
|
fn local(&self) -> LocalVersionSlice {
|
||||||
// A "small" version is never used if the version has a non-zero number
|
// A "small" version is never used if the version has a non-zero number
|
||||||
// of local segments.
|
// of local segments.
|
||||||
&[]
|
LocalVersionSlice::Segments(&[])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1283,7 +1294,7 @@ struct VersionFull {
|
||||||
///
|
///
|
||||||
/// Local versions allow multiple segments separated by periods, such as `deadbeef.1.2.3`, see
|
/// Local versions allow multiple segments separated by periods, such as `deadbeef.1.2.3`, see
|
||||||
/// [`LocalSegment`] for details on the semantics.
|
/// [`LocalSegment`] for details on the semantics.
|
||||||
local: Vec<LocalSegment>,
|
local: LocalVersion,
|
||||||
/// An internal-only segment that does not exist in PEP 440, used to
|
/// An internal-only segment that does not exist in PEP 440, used to
|
||||||
/// represent the smallest possible version of a release, preceding any
|
/// represent the smallest possible version of a release, preceding any
|
||||||
/// `dev`, `pre`, `post` or releases.
|
/// `dev`, `pre`, `post` or releases.
|
||||||
|
@ -1414,6 +1425,93 @@ impl std::fmt::Display for Prerelease {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Either a sequence of local segments or [`LocalVersion::Sentinel`], an internal-only value that
|
||||||
|
/// compares greater than all other local versions.
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "rkyv",
|
||||||
|
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, Eq, PartialEq, PartialOrd, Ord)))]
|
||||||
|
pub enum LocalVersion {
|
||||||
|
/// A sequence of local segments.
|
||||||
|
Segments(Vec<LocalSegment>),
|
||||||
|
/// An internal-only value that compares greater to all other local versions.
|
||||||
|
Max,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like [`LocalVersion`], but using a slice
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
|
||||||
|
pub enum LocalVersionSlice<'a> {
|
||||||
|
/// Like [`LocalVersion::Segments`]
|
||||||
|
Segments(&'a [LocalSegment]),
|
||||||
|
/// Like [`LocalVersion::Sentinel`]
|
||||||
|
Max,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalVersion {
|
||||||
|
/// Convert the local version segments into a slice.
|
||||||
|
pub fn as_slice(&self) -> LocalVersionSlice<'_> {
|
||||||
|
match self {
|
||||||
|
LocalVersion::Segments(segments) => LocalVersionSlice::Segments(segments),
|
||||||
|
LocalVersion::Max => LocalVersionSlice::Max,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the local version segments, if they exist.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::Segments(segments) => segments.clear(),
|
||||||
|
Self::Max => *self = Self::Segments(Vec::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Output the local version identifier string.
|
||||||
|
///
|
||||||
|
/// [`LocalVersionSlice::Max`] maps to `"[max]"` which is otherwise an illegal local
|
||||||
|
/// version because `[` and `]` are not allowed.
|
||||||
|
impl std::fmt::Display for LocalVersionSlice<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
LocalVersionSlice::Segments(segments) => {
|
||||||
|
for (i, segment) in segments.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(f, ".")?;
|
||||||
|
}
|
||||||
|
write!(f, "{segment}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
LocalVersionSlice::Max => write!(f, "[max]"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for LocalVersionSlice<'_> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for LocalVersionSlice<'_> {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
match (self, other) {
|
||||||
|
(LocalVersionSlice::Segments(lv1), LocalVersionSlice::Segments(lv2)) => lv1.cmp(lv2),
|
||||||
|
(LocalVersionSlice::Segments(_), LocalVersionSlice::Max) => Ordering::Less,
|
||||||
|
(LocalVersionSlice::Max, LocalVersionSlice::Segments(_)) => Ordering::Greater,
|
||||||
|
(LocalVersionSlice::Max, LocalVersionSlice::Max) => Ordering::Equal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalVersionSlice<'_> {
|
||||||
|
/// Whether the local version is absent
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
matches!(self, Self::Segments(&[]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A part of the [local version identifier](<https://peps.python.org/pep-0440/#local-version-identifiers>)
|
/// A part of the [local version identifier](<https://peps.python.org/pep-0440/#local-version-identifiers>)
|
||||||
///
|
///
|
||||||
/// Local versions are a mess:
|
/// Local versions are a mess:
|
||||||
|
@ -1855,7 +1953,7 @@ impl<'a> Parser<'a> {
|
||||||
.with_pre(self.pre)
|
.with_pre(self.pre)
|
||||||
.with_post(self.post)
|
.with_post(self.post)
|
||||||
.with_dev(self.dev)
|
.with_dev(self.dev)
|
||||||
.with_local(self.local);
|
.with_local(LocalVersion::Segments(self.local));
|
||||||
VersionPattern {
|
VersionPattern {
|
||||||
version,
|
version,
|
||||||
wildcard: self.wildcard,
|
wildcard: self.wildcard,
|
||||||
|
@ -2326,7 +2424,7 @@ pub(crate) fn compare_release(this: &[u64], other: &[u64]) -> Ordering {
|
||||||
/// implementation
|
/// implementation
|
||||||
///
|
///
|
||||||
/// [pep440-suffix-ordering]: https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering
|
/// [pep440-suffix-ordering]: https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering
|
||||||
fn sortable_tuple(version: &Version) -> (u64, u64, Option<u64>, u64, &[LocalSegment]) {
|
fn sortable_tuple(version: &Version) -> (u64, u64, Option<u64>, u64, LocalVersionSlice) {
|
||||||
// If the version is a "max" version, use a post version larger than any possible post version.
|
// If the version is a "max" version, use a post version larger than any possible post version.
|
||||||
let post = if version.max().is_some() {
|
let post = if version.max().is_some() {
|
||||||
Some(u64::MAX)
|
Some(u64::MAX)
|
||||||
|
|
|
@ -125,46 +125,50 @@ fn test_packaging_versions() {
|
||||||
("1.1.dev1", Version::new([1, 1]).with_dev(Some(1))),
|
("1.1.dev1", Version::new([1, 1]).with_dev(Some(1))),
|
||||||
(
|
(
|
||||||
"1.2+123abc",
|
"1.2+123abc",
|
||||||
Version::new([1, 2]).with_local(vec![LocalSegment::String("123abc".to_string())]),
|
Version::new([1, 2])
|
||||||
|
.with_local_segments(vec![LocalSegment::String("123abc".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2+123abc456",
|
"1.2+123abc456",
|
||||||
Version::new([1, 2]).with_local(vec![LocalSegment::String("123abc456".to_string())]),
|
Version::new([1, 2])
|
||||||
|
.with_local_segments(vec![LocalSegment::String("123abc456".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2+abc",
|
"1.2+abc",
|
||||||
Version::new([1, 2]).with_local(vec![LocalSegment::String("abc".to_string())]),
|
Version::new([1, 2]).with_local_segments(vec![LocalSegment::String("abc".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2+abc123",
|
"1.2+abc123",
|
||||||
Version::new([1, 2]).with_local(vec![LocalSegment::String("abc123".to_string())]),
|
Version::new([1, 2])
|
||||||
|
.with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2+abc123def",
|
"1.2+abc123def",
|
||||||
Version::new([1, 2]).with_local(vec![LocalSegment::String("abc123def".to_string())]),
|
Version::new([1, 2])
|
||||||
|
.with_local_segments(vec![LocalSegment::String("abc123def".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2+1234.abc",
|
"1.2+1234.abc",
|
||||||
Version::new([1, 2]).with_local(vec![
|
Version::new([1, 2]).with_local_segments(vec![
|
||||||
LocalSegment::Number(1234),
|
LocalSegment::Number(1234),
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2+123456",
|
"1.2+123456",
|
||||||
Version::new([1, 2]).with_local(vec![LocalSegment::Number(123_456)]),
|
Version::new([1, 2]).with_local_segments(vec![LocalSegment::Number(123_456)]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2.r32+123456",
|
"1.2.r32+123456",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_post(Some(32))
|
.with_post(Some(32))
|
||||||
.with_local(vec![LocalSegment::Number(123_456)]),
|
.with_local_segments(vec![LocalSegment::Number(123_456)]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1.2.rev33+123456",
|
"1.2.rev33+123456",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_post(Some(33))
|
.with_post(Some(33))
|
||||||
.with_local(vec![LocalSegment::Number(123_456)]),
|
.with_local_segments(vec![LocalSegment::Number(123_456)]),
|
||||||
),
|
),
|
||||||
// Explicit epoch of 1
|
// Explicit epoch of 1
|
||||||
(
|
(
|
||||||
|
@ -316,35 +320,35 @@ fn test_packaging_versions() {
|
||||||
"1!1.2+123abc",
|
"1!1.2+123abc",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_local(vec![LocalSegment::String("123abc".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("123abc".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1!1.2+123abc456",
|
"1!1.2+123abc456",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_local(vec![LocalSegment::String("123abc456".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("123abc456".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1!1.2+abc",
|
"1!1.2+abc",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_local(vec![LocalSegment::String("abc".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("abc".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1!1.2+abc123",
|
"1!1.2+abc123",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_local(vec![LocalSegment::String("abc123".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1!1.2+abc123def",
|
"1!1.2+abc123def",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_local(vec![LocalSegment::String("abc123def".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("abc123def".to_string())]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1!1.2+1234.abc",
|
"1!1.2+1234.abc",
|
||||||
Version::new([1, 2]).with_epoch(1).with_local(vec![
|
Version::new([1, 2]).with_epoch(1).with_local_segments(vec![
|
||||||
LocalSegment::Number(1234),
|
LocalSegment::Number(1234),
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
]),
|
]),
|
||||||
|
@ -353,28 +357,28 @@ fn test_packaging_versions() {
|
||||||
"1!1.2+123456",
|
"1!1.2+123456",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_local(vec![LocalSegment::Number(123_456)]),
|
.with_local_segments(vec![LocalSegment::Number(123_456)]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1!1.2.r32+123456",
|
"1!1.2.r32+123456",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_post(Some(32))
|
.with_post(Some(32))
|
||||||
.with_local(vec![LocalSegment::Number(123_456)]),
|
.with_local_segments(vec![LocalSegment::Number(123_456)]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"1!1.2.rev33+123456",
|
"1!1.2.rev33+123456",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(1)
|
.with_epoch(1)
|
||||||
.with_post(Some(33))
|
.with_post(Some(33))
|
||||||
.with_local(vec![LocalSegment::Number(123_456)]),
|
.with_local_segments(vec![LocalSegment::Number(123_456)]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"98765!1.2.rev33+123456",
|
"98765!1.2.rev33+123456",
|
||||||
Version::new([1, 2])
|
Version::new([1, 2])
|
||||||
.with_epoch(98765)
|
.with_epoch(98765)
|
||||||
.with_post(Some(33))
|
.with_post(Some(33))
|
||||||
.with_local(vec![LocalSegment::Number(123_456)]),
|
.with_local_segments(vec![LocalSegment::Number(123_456)]),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
for (string, structured) in versions {
|
for (string, structured) in versions {
|
||||||
|
@ -879,50 +883,50 @@ fn parse_version_valid() {
|
||||||
// local tests
|
// local tests
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+2"),
|
p("5+2"),
|
||||||
Version::new([5]).with_local(vec![LocalSegment::Number(2)])
|
Version::new([5]).with_local_segments(vec![LocalSegment::Number(2)])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+a"),
|
p("5+a"),
|
||||||
Version::new([5]).with_local(vec![LocalSegment::String("a".to_string())])
|
Version::new([5]).with_local_segments(vec![LocalSegment::String("a".to_string())])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+abc.123"),
|
p("5+abc.123"),
|
||||||
Version::new([5]).with_local(vec![
|
Version::new([5]).with_local_segments(vec![
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
LocalSegment::Number(123),
|
LocalSegment::Number(123),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+123.abc"),
|
p("5+123.abc"),
|
||||||
Version::new([5]).with_local(vec![
|
Version::new([5]).with_local_segments(vec![
|
||||||
LocalSegment::Number(123),
|
LocalSegment::Number(123),
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+18446744073709551615.abc"),
|
p("5+18446744073709551615.abc"),
|
||||||
Version::new([5]).with_local(vec![
|
Version::new([5]).with_local_segments(vec![
|
||||||
LocalSegment::Number(18_446_744_073_709_551_615),
|
LocalSegment::Number(18_446_744_073_709_551_615),
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+18446744073709551616.abc"),
|
p("5+18446744073709551616.abc"),
|
||||||
Version::new([5]).with_local(vec![
|
Version::new([5]).with_local_segments(vec![
|
||||||
LocalSegment::String("18446744073709551616".to_string()),
|
LocalSegment::String("18446744073709551616".to_string()),
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+ABC.123"),
|
p("5+ABC.123"),
|
||||||
Version::new([5]).with_local(vec![
|
Version::new([5]).with_local_segments(vec![
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
LocalSegment::Number(123),
|
LocalSegment::Number(123),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5+ABC-123.4_5_xyz-MNO"),
|
p("5+ABC-123.4_5_xyz-MNO"),
|
||||||
Version::new([5]).with_local(vec![
|
Version::new([5]).with_local_segments(vec![
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
LocalSegment::Number(123),
|
LocalSegment::Number(123),
|
||||||
LocalSegment::Number(4),
|
LocalSegment::Number(4),
|
||||||
|
@ -933,21 +937,21 @@ fn parse_version_valid() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5.6.7+abc-00123"),
|
p("5.6.7+abc-00123"),
|
||||||
Version::new([5, 6, 7]).with_local(vec![
|
Version::new([5, 6, 7]).with_local_segments(vec![
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
LocalSegment::Number(123),
|
LocalSegment::Number(123),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5.6.7+abc-foo00123"),
|
p("5.6.7+abc-foo00123"),
|
||||||
Version::new([5, 6, 7]).with_local(vec![
|
Version::new([5, 6, 7]).with_local_segments(vec![
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
LocalSegment::String("foo00123".to_string()),
|
LocalSegment::String("foo00123".to_string()),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("5.6.7+abc-00123a"),
|
p("5.6.7+abc-00123a"),
|
||||||
Version::new([5, 6, 7]).with_local(vec![
|
Version::new([5, 6, 7]).with_local_segments(vec![
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
LocalSegment::String("00123a".to_string()),
|
LocalSegment::String("00123a".to_string()),
|
||||||
])
|
])
|
||||||
|
@ -992,7 +996,7 @@ fn parse_version_valid() {
|
||||||
assert_eq!(p(" 5 "), Version::new([5]));
|
assert_eq!(p(" 5 "), Version::new([5]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p(" 5.6.7+abc.123.xyz "),
|
p(" 5.6.7+abc.123.xyz "),
|
||||||
Version::new([5, 6, 7]).with_local(vec![
|
Version::new([5, 6, 7]).with_local_segments(vec![
|
||||||
LocalSegment::String("abc".to_string()),
|
LocalSegment::String("abc".to_string()),
|
||||||
LocalSegment::Number(123),
|
LocalSegment::Number(123),
|
||||||
LocalSegment::String("xyz".to_string())
|
LocalSegment::String("xyz".to_string())
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
use version_ranges::Ranges;
|
use version_ranges::Ranges;
|
||||||
|
|
||||||
use crate::{Operator, Prerelease, Version, VersionSpecifier, VersionSpecifiers};
|
use crate::{
|
||||||
|
LocalVersion, LocalVersionSlice, Operator, Prerelease, Version, VersionSpecifier,
|
||||||
|
VersionSpecifiers,
|
||||||
|
};
|
||||||
|
|
||||||
impl From<VersionSpecifiers> for Ranges<Version> {
|
impl From<VersionSpecifiers> for Ranges<Version> {
|
||||||
/// Convert [`VersionSpecifiers`] to a PubGrub-compatible version range, using PEP 440
|
/// Convert [`VersionSpecifiers`] to a PubGrub-compatible version range, using PEP 440
|
||||||
|
@ -22,9 +25,23 @@ impl From<VersionSpecifier> for Ranges<Version> {
|
||||||
fn from(specifier: VersionSpecifier) -> Self {
|
fn from(specifier: VersionSpecifier) -> Self {
|
||||||
let VersionSpecifier { operator, version } = specifier;
|
let VersionSpecifier { operator, version } = specifier;
|
||||||
match operator {
|
match operator {
|
||||||
Operator::Equal => Ranges::singleton(version),
|
Operator::Equal => match version.local() {
|
||||||
|
LocalVersionSlice::Segments(&[]) => {
|
||||||
|
let low = version;
|
||||||
|
let high = low.clone().with_local(LocalVersion::Max);
|
||||||
|
Ranges::between(low, high)
|
||||||
|
}
|
||||||
|
LocalVersionSlice::Segments(_) => Ranges::singleton(version),
|
||||||
|
LocalVersionSlice::Max => unreachable!(
|
||||||
|
"found `LocalVersionSlice::Sentinel`, which should be an internal-only value"
|
||||||
|
),
|
||||||
|
},
|
||||||
Operator::ExactEqual => Ranges::singleton(version),
|
Operator::ExactEqual => Ranges::singleton(version),
|
||||||
Operator::NotEqual => Ranges::singleton(version).complement(),
|
Operator::NotEqual => Ranges::from(VersionSpecifier {
|
||||||
|
operator: Operator::Equal,
|
||||||
|
version,
|
||||||
|
})
|
||||||
|
.complement(),
|
||||||
Operator::TildeEqual => {
|
Operator::TildeEqual => {
|
||||||
let [rest @ .., last, _] = version.release() else {
|
let [rest @ .., last, _] = version.release() else {
|
||||||
unreachable!("~= must have at least two segments");
|
unreachable!("~= must have at least two segments");
|
||||||
|
@ -45,7 +62,7 @@ impl From<VersionSpecifier> for Ranges<Version> {
|
||||||
Ranges::strictly_lower_than(version.with_min(Some(0)))
|
Ranges::strictly_lower_than(version.with_min(Some(0)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::LessThanEqual => Ranges::lower_than(version),
|
Operator::LessThanEqual => Ranges::lower_than(version.with_local(LocalVersion::Max)),
|
||||||
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."
|
||||||
|
|
|
@ -652,12 +652,7 @@ impl std::fmt::Display for VersionSpecifierBuildError {
|
||||||
operator: ref op,
|
operator: ref op,
|
||||||
ref version,
|
ref version,
|
||||||
} => {
|
} => {
|
||||||
let local = version
|
let local = version.local();
|
||||||
.local()
|
|
||||||
.iter()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(".");
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Operator {op} is incompatible with versions \
|
"Operator {op} is incompatible with versions \
|
||||||
|
|
|
@ -579,7 +579,8 @@ fn test_invalid_specifier() {
|
||||||
ParseErrorKind::InvalidSpecifier(
|
ParseErrorKind::InvalidSpecifier(
|
||||||
BuildErrorKind::OperatorLocalCombo {
|
BuildErrorKind::OperatorLocalCombo {
|
||||||
operator: Operator::TildeEqual,
|
operator: Operator::TildeEqual,
|
||||||
version: Version::new([1, 0]).with_local(vec![LocalSegment::Number(5)]),
|
version: Version::new([1, 0])
|
||||||
|
.with_local_segments(vec![LocalSegment::Number(5)]),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
|
@ -591,7 +592,7 @@ fn test_invalid_specifier() {
|
||||||
BuildErrorKind::OperatorLocalCombo {
|
BuildErrorKind::OperatorLocalCombo {
|
||||||
operator: Operator::GreaterThanEqual,
|
operator: Operator::GreaterThanEqual,
|
||||||
version: Version::new([1, 0])
|
version: Version::new([1, 0])
|
||||||
.with_local(vec![LocalSegment::String("deadbeef".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("deadbeef".to_string())]),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
|
@ -603,7 +604,7 @@ fn test_invalid_specifier() {
|
||||||
BuildErrorKind::OperatorLocalCombo {
|
BuildErrorKind::OperatorLocalCombo {
|
||||||
operator: Operator::LessThanEqual,
|
operator: Operator::LessThanEqual,
|
||||||
version: Version::new([1, 0])
|
version: Version::new([1, 0])
|
||||||
.with_local(vec![LocalSegment::String("abc123".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
|
@ -615,7 +616,7 @@ fn test_invalid_specifier() {
|
||||||
BuildErrorKind::OperatorLocalCombo {
|
BuildErrorKind::OperatorLocalCombo {
|
||||||
operator: Operator::GreaterThan,
|
operator: Operator::GreaterThan,
|
||||||
version: Version::new([1, 0])
|
version: Version::new([1, 0])
|
||||||
.with_local(vec![LocalSegment::String("watwat".to_string())]),
|
.with_local_segments(vec![LocalSegment::String("watwat".to_string())]),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
|
@ -626,8 +627,10 @@ fn test_invalid_specifier() {
|
||||||
ParseErrorKind::InvalidSpecifier(
|
ParseErrorKind::InvalidSpecifier(
|
||||||
BuildErrorKind::OperatorLocalCombo {
|
BuildErrorKind::OperatorLocalCombo {
|
||||||
operator: Operator::LessThan,
|
operator: Operator::LessThan,
|
||||||
version: Version::new([1, 0])
|
version: Version::new([1, 0]).with_local_segments(vec![
|
||||||
.with_local(vec![LocalSegment::Number(1), LocalSegment::Number(0)]),
|
LocalSegment::Number(1),
|
||||||
|
LocalSegment::Number(0),
|
||||||
|
]),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -2125,7 +2125,9 @@ impl FromStr for VersionRequest {
|
||||||
return Err(Error::InvalidVersionRequest(s.to_string()));
|
return Err(Error::InvalidVersionRequest(s.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let [uv_pep440::LocalSegment::String(local)] = version.local() else {
|
let uv_pep440::LocalVersionSlice::Segments([uv_pep440::LocalSegment::String(local)]) =
|
||||||
|
version.local()
|
||||||
|
else {
|
||||||
return Err(Error::InvalidVersionRequest(s.to_string()));
|
return Err(Error::InvalidVersionRequest(s.to_string()));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet, Bound};
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use pubgrub::{DefaultStringReporter, DerivationTree, Derived, External, Range, Reporter};
|
use pubgrub::{
|
||||||
|
DefaultStringReporter, DerivationTree, Derived, External, Range, Ranges, Reporter, Term,
|
||||||
|
};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
use uv_distribution_types::{
|
||||||
|
BuiltDist, IndexCapabilities, IndexLocations, IndexUrl, InstalledDist, SourceDist,
|
||||||
|
};
|
||||||
|
use uv_normalize::PackageName;
|
||||||
|
use uv_pep440::{LocalVersionSlice, Version};
|
||||||
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
use crate::candidate_selector::CandidateSelector;
|
use crate::candidate_selector::CandidateSelector;
|
||||||
use crate::dependency_provider::UvDependencyProvider;
|
use crate::dependency_provider::UvDependencyProvider;
|
||||||
|
@ -16,13 +26,6 @@ use crate::resolver::{
|
||||||
IncompletePackage, ResolverEnvironment, UnavailablePackage, UnavailableReason,
|
IncompletePackage, ResolverEnvironment, UnavailablePackage, UnavailableReason,
|
||||||
};
|
};
|
||||||
use crate::Options;
|
use crate::Options;
|
||||||
use tracing::trace;
|
|
||||||
use uv_distribution_types::{
|
|
||||||
BuiltDist, IndexCapabilities, IndexLocations, IndexUrl, InstalledDist, SourceDist,
|
|
||||||
};
|
|
||||||
use uv_normalize::PackageName;
|
|
||||||
use uv_pep440::Version;
|
|
||||||
use uv_static::EnvVars;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum ResolveError {
|
pub enum ResolveError {
|
||||||
|
@ -221,6 +224,178 @@ impl NoSolutionError {
|
||||||
.expect("derivation tree should contain at least one external term")
|
.expect("derivation tree should contain at least one external term")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Simplifies the version ranges on any incompatibilities to remove the `[max]` sentinel.
|
||||||
|
///
|
||||||
|
/// The `[max]` sentinel is used to represent the maximum local version of a package, to
|
||||||
|
/// implement PEP 440 semantics for local version equality. For example, `1.0.0+foo` needs to
|
||||||
|
/// satisfy `==1.0.0`.
|
||||||
|
pub(crate) fn collapse_local_version_segments(derivation_tree: ErrorTree) -> ErrorTree {
|
||||||
|
/// Remove local versions sentinels (`+[max]`) from the interval.
|
||||||
|
fn strip_sentinel(
|
||||||
|
mut lower: Bound<Version>,
|
||||||
|
mut upper: Bound<Version>,
|
||||||
|
) -> (Bound<Version>, Bound<Version>) {
|
||||||
|
match (&lower, &upper) {
|
||||||
|
(Bound::Unbounded, Bound::Unbounded) => {}
|
||||||
|
(Bound::Unbounded, Bound::Included(v)) => {
|
||||||
|
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
upper = Bound::Included(v.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Unbounded, Bound::Excluded(v)) => {
|
||||||
|
// `<1.0.0+[max]` is equivalent to `<1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
upper = Bound::Excluded(v.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Included(v), Bound::Unbounded) => {
|
||||||
|
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
lower = Bound::Excluded(v.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Included(v), Bound::Included(b)) => {
|
||||||
|
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
lower = Bound::Excluded(v.clone().without_local());
|
||||||
|
}
|
||||||
|
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
|
||||||
|
if b.local() == LocalVersionSlice::Max {
|
||||||
|
upper = Bound::Included(b.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Included(v), Bound::Excluded(b)) => {
|
||||||
|
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
lower = Bound::Excluded(v.clone().without_local());
|
||||||
|
}
|
||||||
|
// `<1.0.0+[max]` is equivalent to `<1.0.0`
|
||||||
|
if b.local() == LocalVersionSlice::Max {
|
||||||
|
upper = Bound::Included(b.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Excluded(v), Bound::Unbounded) => {
|
||||||
|
// `>1.0.0+[max]` is equivalent to `>1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
lower = Bound::Excluded(v.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Excluded(v), Bound::Included(b)) => {
|
||||||
|
// `>1.0.0+[max]` is equivalent to `>1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
lower = Bound::Excluded(v.clone().without_local());
|
||||||
|
}
|
||||||
|
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
|
||||||
|
if b.local() == LocalVersionSlice::Max {
|
||||||
|
upper = Bound::Included(b.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Excluded(v), Bound::Excluded(b)) => {
|
||||||
|
// `>1.0.0+[max]` is equivalent to `>1.0.0`
|
||||||
|
if v.local() == LocalVersionSlice::Max {
|
||||||
|
lower = Bound::Excluded(v.clone().without_local());
|
||||||
|
}
|
||||||
|
// `<1.0.0+[max]` is equivalent to `<1.0.0`
|
||||||
|
if b.local() == LocalVersionSlice::Max {
|
||||||
|
upper = Bound::Excluded(b.clone().without_local());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(lower, upper)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove local versions sentinels (`+[max]`) from the version ranges.
|
||||||
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
fn strip_sentinels(versions: Ranges<Version>) -> Ranges<Version> {
|
||||||
|
let mut range = Ranges::empty();
|
||||||
|
for (lower, upper) in versions.iter() {
|
||||||
|
let (lower, upper) = strip_sentinel(lower.clone(), upper.clone());
|
||||||
|
range = range.union(&Range::from_range_bounds((lower, upper)));
|
||||||
|
}
|
||||||
|
range
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the range appears to be, e.g., `>1.0.0, <1.0.0+[max]`.
|
||||||
|
fn is_sentinel(versions: &Ranges<Version>) -> bool {
|
||||||
|
versions.iter().all(|(lower, upper)| {
|
||||||
|
let (Bound::Excluded(lower), Bound::Excluded(upper)) = (lower, upper) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if lower.local() == LocalVersionSlice::Max {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if upper.local() != LocalVersionSlice::Max {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*lower == upper.clone().without_local()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strip(derivation_tree: ErrorTree) -> Option<ErrorTree> {
|
||||||
|
match derivation_tree {
|
||||||
|
DerivationTree::External(External::NotRoot(_, _)) => Some(derivation_tree),
|
||||||
|
DerivationTree::External(External::NoVersions(package, versions)) => {
|
||||||
|
if is_sentinel(&versions) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let versions = strip_sentinels(versions);
|
||||||
|
Some(DerivationTree::External(External::NoVersions(
|
||||||
|
package, versions,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
DerivationTree::External(External::FromDependencyOf(
|
||||||
|
package1,
|
||||||
|
versions1,
|
||||||
|
package2,
|
||||||
|
versions2,
|
||||||
|
)) => {
|
||||||
|
let versions1 = strip_sentinels(versions1);
|
||||||
|
let versions2 = strip_sentinels(versions2);
|
||||||
|
Some(DerivationTree::External(External::FromDependencyOf(
|
||||||
|
package1, versions1, package2, versions2,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
DerivationTree::External(External::Custom(package, versions, reason)) => {
|
||||||
|
let versions = strip_sentinels(versions);
|
||||||
|
Some(DerivationTree::External(External::Custom(
|
||||||
|
package, versions, reason,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
DerivationTree::Derived(mut derived) => {
|
||||||
|
let cause1 = strip((*derived.cause1).clone());
|
||||||
|
let cause2 = strip((*derived.cause2).clone());
|
||||||
|
match (cause1, cause2) {
|
||||||
|
(Some(cause1), Some(cause2)) => Some(DerivationTree::Derived(Derived {
|
||||||
|
cause1: Arc::new(cause1),
|
||||||
|
cause2: Arc::new(cause2),
|
||||||
|
terms: std::mem::take(&mut derived.terms)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(pkg, term)| {
|
||||||
|
let term = match term {
|
||||||
|
Term::Positive(versions) => {
|
||||||
|
Term::Positive(strip_sentinels(versions))
|
||||||
|
}
|
||||||
|
Term::Negative(versions) => {
|
||||||
|
Term::Negative(strip_sentinels(versions))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(pkg, term)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
shared_id: derived.shared_id,
|
||||||
|
})),
|
||||||
|
(Some(cause), None) | (None, Some(cause)) => Some(cause),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strip(derivation_tree).expect("derivation tree should contain at least one term")
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize a [`NoSolutionHeader`] for this error.
|
/// Initialize a [`NoSolutionHeader`] for this error.
|
||||||
pub fn header(&self) -> NoSolutionHeader {
|
pub fn header(&self) -> NoSolutionHeader {
|
||||||
NoSolutionHeader::new(self.env.clone())
|
NoSolutionHeader::new(self.env.clone())
|
||||||
|
|
|
@ -1964,7 +1964,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
index_locations: &IndexLocations,
|
index_locations: &IndexLocations,
|
||||||
index_capabilities: &IndexCapabilities,
|
index_capabilities: &IndexCapabilities,
|
||||||
) -> ResolveError {
|
) -> ResolveError {
|
||||||
err = NoSolutionError::collapse_proxies(err);
|
err = NoSolutionError::collapse_local_version_segments(NoSolutionError::collapse_proxies(
|
||||||
|
err,
|
||||||
|
));
|
||||||
|
|
||||||
let mut unavailable_packages = FxHashMap::default();
|
let mut unavailable_packages = FxHashMap::default();
|
||||||
for package in err.packages() {
|
for package in err.packages() {
|
||||||
|
|
|
@ -2780,7 +2780,7 @@ fn fork_non_local_fork_marker_direct() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0 and package-a{sys_platform == 'linux'}==1.0.0 depends on package-c<2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible.
|
╰─▶ Because package-a{sys_platform == 'linux'}==1.0.0 depends on package-c<2.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible.
|
||||||
And because your project depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable.
|
And because your project depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
@ -2852,11 +2852,11 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0 and only package-c{sys_platform == 'darwin'}<=2.0.0 is available, we can conclude that package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}==2.0.0.
|
╰─▶ Because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and only the following versions of package-c{sys_platform == 'linux'} are available:
|
||||||
And because only the following versions of package-c{sys_platform == 'linux'} are available:
|
|
||||||
package-c{sys_platform == 'linux'}==1.0.0
|
package-c{sys_platform == 'linux'}==1.0.0
|
||||||
package-c{sys_platform == 'linux'}>2.0.0
|
package-c{sys_platform == 'linux'}>2.0.0
|
||||||
and package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
|
we can conclude that package-a==1.0.0 depends on package-c{sys_platform == 'linux'}==1.0.0.
|
||||||
|
And because only package-c{sys_platform == 'darwin'}<=2.0.0 is available and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
|
||||||
And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable.
|
And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
|
@ -7179,7 +7179,7 @@ fn universal_transitive_disjoint_locals() -> Result<()> {
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# torchvision
|
# torchvision
|
||||||
# triton
|
# triton
|
||||||
torchvision==0.15.1
|
torchvision==0.15.1+rocm5.4.2
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux'
|
triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux'
|
||||||
# via torch
|
# via torch
|
||||||
|
@ -7452,30 +7452,33 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
# This file was autogenerated by uv via the following command:
|
# This file was autogenerated by uv via the following command:
|
||||||
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --universal
|
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --universal
|
||||||
cmake==3.28.4 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
cmake==3.28.4 ; python_full_version < '3.11' or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux')
|
||||||
# via triton
|
# via
|
||||||
|
# pytorch-triton-rocm
|
||||||
|
# triton
|
||||||
.
|
.
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
filelock==3.13.1
|
filelock==3.13.1
|
||||||
# via
|
# via
|
||||||
|
# pytorch-triton-rocm
|
||||||
# torch
|
# torch
|
||||||
# triton
|
# triton
|
||||||
jinja2==3.1.3
|
jinja2==3.1.3
|
||||||
# via torch
|
# via torch
|
||||||
lit==18.1.2 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
lit==18.1.2 ; python_full_version < '3.11' or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux')
|
||||||
# via triton
|
# via
|
||||||
|
# pytorch-triton-rocm
|
||||||
|
# triton
|
||||||
markupsafe==2.1.5
|
markupsafe==2.1.5
|
||||||
# via jinja2
|
# via jinja2
|
||||||
mpmath==1.3.0
|
mpmath==1.3.0
|
||||||
# via sympy
|
# via sympy
|
||||||
networkx==3.2.1
|
networkx==3.2.1
|
||||||
# via torch
|
# via torch
|
||||||
|
pytorch-triton-rocm==2.0.2 ; python_full_version < '3.11'
|
||||||
|
# via torch
|
||||||
sympy==1.12
|
sympy==1.12
|
||||||
# via torch
|
# via torch
|
||||||
torch==2.0.0 ; python_full_version < '3.11'
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# example
|
|
||||||
torch==2.0.0+cpu ; python_full_version >= '3.13'
|
torch==2.0.0+cpu ; python_full_version >= '3.13'
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
|
@ -7485,13 +7488,18 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# example
|
# example
|
||||||
# triton
|
# triton
|
||||||
|
torch==2.0.0+rocm5.4.2 ; python_full_version < '3.11'
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# example
|
||||||
|
# pytorch-triton-rocm
|
||||||
triton==2.0.0 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
triton==2.0.0 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
||||||
# via torch
|
# via torch
|
||||||
typing-extensions==4.10.0
|
typing-extensions==4.10.0
|
||||||
# via torch
|
# via torch
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Resolved 14 packages in [TIME]
|
Resolved 15 packages in [TIME]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -7539,6 +7547,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
filelock==3.13.1
|
filelock==3.13.1
|
||||||
# via
|
# via
|
||||||
|
# pytorch-triton-rocm
|
||||||
# torch
|
# torch
|
||||||
# triton
|
# triton
|
||||||
fsspec==2024.3.1 ; platform_machine != 'x86_64'
|
fsspec==2024.3.1 ; platform_machine != 'x86_64'
|
||||||
|
@ -7557,6 +7566,8 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# via sympy
|
# via sympy
|
||||||
networkx==3.2.1
|
networkx==3.2.1
|
||||||
# via torch
|
# via torch
|
||||||
|
pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64'
|
||||||
|
# via torch
|
||||||
sympy==1.12
|
sympy==1.12
|
||||||
# via torch
|
# via torch
|
||||||
tbb==2021.11.0 ; platform_machine != 'x86_64' and platform_system == 'Windows'
|
tbb==2021.11.0 ; platform_machine != 'x86_64' and platform_system == 'Windows'
|
||||||
|
@ -7566,7 +7577,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# example
|
# example
|
||||||
# triton
|
# triton
|
||||||
torch==2.3.0 ; platform_machine != 'x86_64'
|
torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64'
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux'
|
triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux'
|
||||||
# via torch
|
# via torch
|
||||||
|
@ -7574,7 +7585,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# via torch
|
# via torch
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Resolved 17 packages in [TIME]
|
Resolved 18 packages in [TIME]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -7613,6 +7624,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
filelock==3.13.1
|
filelock==3.13.1
|
||||||
# via
|
# via
|
||||||
|
# pytorch-triton-rocm
|
||||||
# torch
|
# torch
|
||||||
# triton
|
# triton
|
||||||
fsspec==2024.3.1 ; platform_machine != 'x86_64'
|
fsspec==2024.3.1 ; platform_machine != 'x86_64'
|
||||||
|
@ -7631,6 +7643,8 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# via sympy
|
# via sympy
|
||||||
networkx==3.2.1
|
networkx==3.2.1
|
||||||
# via torch
|
# via torch
|
||||||
|
pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64'
|
||||||
|
# via torch
|
||||||
sympy==1.12
|
sympy==1.12
|
||||||
# via torch
|
# via torch
|
||||||
tbb==2021.11.0 ; platform_machine != 'x86_64' and platform_system == 'Windows'
|
tbb==2021.11.0 ; platform_machine != 'x86_64' and platform_system == 'Windows'
|
||||||
|
@ -7640,7 +7654,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# example
|
# example
|
||||||
# triton
|
# triton
|
||||||
torch==2.3.0 ; platform_machine != 'x86_64'
|
torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64'
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux'
|
triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux'
|
||||||
# via torch
|
# via torch
|
||||||
|
@ -7648,7 +7662,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
|
||||||
# via torch
|
# via torch
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Resolved 17 packages in [TIME]
|
Resolved 18 packages in [TIME]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -7698,6 +7712,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> {
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
filelock==3.13.1
|
filelock==3.13.1
|
||||||
# via
|
# via
|
||||||
|
# pytorch-triton-rocm
|
||||||
# torch
|
# torch
|
||||||
# triton
|
# triton
|
||||||
fsspec==2024.3.1 ; os_name != 'Linux'
|
fsspec==2024.3.1 ; os_name != 'Linux'
|
||||||
|
@ -7716,36 +7731,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> {
|
||||||
# via sympy
|
# via sympy
|
||||||
networkx==3.2.1
|
networkx==3.2.1
|
||||||
# via torch
|
# via torch
|
||||||
nvidia-cublas-cu12==12.1.3.1 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
pytorch-triton-rocm==2.3.0 ; os_name != 'Linux'
|
||||||
# via
|
|
||||||
# nvidia-cudnn-cu12
|
|
||||||
# nvidia-cusolver-cu12
|
|
||||||
# torch
|
|
||||||
nvidia-cuda-cupti-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-cuda-nvrtc-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-cuda-runtime-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-cudnn-cu12==8.9.2.26 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-cufft-cu12==11.0.2.54 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-curand-cu12==10.3.2.106 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-cusolver-cu12==11.4.5.107 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-cusparse-cu12==12.1.0.106 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via
|
|
||||||
# nvidia-cusolver-cu12
|
|
||||||
# torch
|
|
||||||
nvidia-nccl-cu12==2.20.5 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
|
||||||
nvidia-nvjitlink-cu12==12.4.99 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via
|
|
||||||
# nvidia-cusolver-cu12
|
|
||||||
# nvidia-cusparse-cu12
|
|
||||||
nvidia-nvtx-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
|
||||||
# via torch
|
# via torch
|
||||||
sympy==1.12
|
sympy==1.12
|
||||||
# via torch
|
# via torch
|
||||||
|
@ -7760,7 +7746,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> {
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# example
|
# example
|
||||||
# triton
|
# triton
|
||||||
torch==2.3.0 ; os_name != 'Linux'
|
torch==2.3.0+rocm6.0 ; os_name != 'Linux'
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
triton==2.0.0 ; os_name == 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
triton==2.0.0 ; os_name == 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux'
|
||||||
# via torch
|
# via torch
|
||||||
|
@ -7768,7 +7754,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> {
|
||||||
# via torch
|
# via torch
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Resolved 30 packages in [TIME]
|
Resolved 19 packages in [TIME]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -8515,14 +8501,20 @@ fn universal_marker_propagation() -> Result<()> {
|
||||||
# via requests
|
# via requests
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.3.2
|
||||||
# via requests
|
# via requests
|
||||||
|
cmake==3.28.4 ; platform_machine == 'x86_64'
|
||||||
|
# via pytorch-triton-rocm
|
||||||
filelock==3.13.1
|
filelock==3.13.1
|
||||||
# via torch
|
# via
|
||||||
|
# pytorch-triton-rocm
|
||||||
|
# torch
|
||||||
fsspec==2024.3.1 ; platform_machine != 'x86_64'
|
fsspec==2024.3.1 ; platform_machine != 'x86_64'
|
||||||
# via torch
|
# via torch
|
||||||
idna==3.6
|
idna==3.6
|
||||||
# via requests
|
# via requests
|
||||||
jinja2==3.1.3
|
jinja2==3.1.3
|
||||||
# via torch
|
# via torch
|
||||||
|
lit==18.1.2 ; platform_machine == 'x86_64'
|
||||||
|
# via pytorch-triton-rocm
|
||||||
markupsafe==2.1.5
|
markupsafe==2.1.5
|
||||||
# via jinja2
|
# via jinja2
|
||||||
mpmath==1.3.0
|
mpmath==1.3.0
|
||||||
|
@ -8533,15 +8525,20 @@ fn universal_marker_propagation() -> Result<()> {
|
||||||
# via torchvision
|
# via torchvision
|
||||||
pillow==10.2.0
|
pillow==10.2.0
|
||||||
# via torchvision
|
# via torchvision
|
||||||
|
pytorch-triton-rocm==2.0.2 ; platform_machine == 'x86_64'
|
||||||
|
# via torch
|
||||||
|
pytorch-triton-rocm==2.2.0 ; platform_machine != 'x86_64'
|
||||||
|
# via torch
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
# via torchvision
|
# via torchvision
|
||||||
sympy==1.12
|
sympy==1.12
|
||||||
# via torch
|
# via torch
|
||||||
torch==2.0.0 ; platform_machine == 'x86_64'
|
torch==2.0.0+rocm5.4.2 ; platform_machine == 'x86_64'
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
|
# pytorch-triton-rocm
|
||||||
# torchvision
|
# torchvision
|
||||||
torch==2.2.0 ; platform_machine != 'x86_64'
|
torch==2.2.0+rocm5.7 ; platform_machine != 'x86_64'
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# torchvision
|
# torchvision
|
||||||
|
@ -8556,7 +8553,7 @@ fn universal_marker_propagation() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: The requested Python version 3.8 is not available; 3.12.[X] will be used to build dependencies instead.
|
warning: The requested Python version 3.8 is not available; 3.12.[X] will be used to build dependencies instead.
|
||||||
Resolved 19 packages in [TIME]
|
Resolved 23 packages in [TIME]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -334,10 +334,10 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available:
|
╰─▶ Because only the following versions of package-a are available:
|
||||||
package-a==1.0.0
|
package-a==1.0.0
|
||||||
package-a>2.0.0,<=3.0.0
|
package-a>2.0.0,<=3.0.0
|
||||||
we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1)
|
and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1)
|
||||||
|
|
||||||
Because only the following versions of package-c are available:
|
Because only the following versions of package-c are available:
|
||||||
package-c==1.0.0
|
package-c==1.0.0
|
||||||
|
@ -445,10 +445,10 @@ fn dependency_excludes_range_of_compatible_versions() {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available:
|
╰─▶ Because only the following versions of package-a are available:
|
||||||
package-a==1.0.0
|
package-a==1.0.0
|
||||||
package-a>2.0.0,<=3.0.0
|
package-a>2.0.0,<=3.0.0
|
||||||
we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1)
|
and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1)
|
||||||
|
|
||||||
Because only the following versions of package-c are available:
|
Because only the following versions of package-c are available:
|
||||||
package-c==1.0.0
|
package-c==1.0.0
|
||||||
|
@ -529,17 +529,17 @@ fn excluded_only_compatible_version() {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available:
|
╰─▶ Because only the following versions of package-a are available:
|
||||||
package-a==1.0.0
|
package-a==1.0.0
|
||||||
package-a==2.0.0
|
package-a==2.0.0
|
||||||
package-a==3.0.0
|
package-a==3.0.0
|
||||||
we can conclude that package-a<2.0.0 depends on package-b==1.0.0.
|
and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0.
|
||||||
And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all of:
|
And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all of:
|
||||||
package-a<2.0.0
|
package-a<2.0.0
|
||||||
package-a>2.0.0
|
package-a>2.0.0
|
||||||
depend on one of:
|
depend on one of:
|
||||||
package-b<=1.0.0
|
package-b==1.0.0
|
||||||
package-b>=3.0.0
|
package-b==3.0.0
|
||||||
|
|
||||||
And because you require one of:
|
And because you require one of:
|
||||||
package-a<2.0.0
|
package-a<2.0.0
|
||||||
|
@ -1276,8 +1276,10 @@ fn transitive_incompatible_with_transitive() {
|
||||||
/// │ └── python3.8
|
/// │ └── python3.8
|
||||||
/// ├── root
|
/// ├── root
|
||||||
/// │ └── requires a>=1.2.3
|
/// │ └── requires a>=1.2.3
|
||||||
|
/// │ ├── satisfied by a-1.2.3+bar
|
||||||
/// │ └── satisfied by a-1.2.3+foo
|
/// │ └── satisfied by a-1.2.3+foo
|
||||||
/// └── a
|
/// └── a
|
||||||
|
/// ├── a-1.2.3+bar
|
||||||
/// └── a-1.2.3+foo
|
/// └── a-1.2.3+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1354,8 +1356,10 @@ fn local_greater_than() {
|
||||||
/// │ └── python3.8
|
/// │ └── python3.8
|
||||||
/// ├── root
|
/// ├── root
|
||||||
/// │ └── requires a<=1.2.3
|
/// │ └── requires a<=1.2.3
|
||||||
|
/// │ ├── satisfied by a-1.2.3+bar
|
||||||
/// │ └── satisfied by a-1.2.3+foo
|
/// │ └── satisfied by a-1.2.3+foo
|
||||||
/// └── a
|
/// └── a
|
||||||
|
/// ├── a-1.2.3+bar
|
||||||
/// └── a-1.2.3+foo
|
/// └── a-1.2.3+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1369,19 +1373,22 @@ fn local_less_than_or_equal() {
|
||||||
uv_snapshot!(filters, command(&context)
|
uv_snapshot!(filters, command(&context)
|
||||||
.arg("local-less-than-or-equal-a<=1.2.3")
|
.arg("local-less-than-or-equal-a<=1.2.3")
|
||||||
, @r###"
|
, @r###"
|
||||||
success: false
|
success: true
|
||||||
exit_code: 1
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
Resolved 1 package in [TIME]
|
||||||
╰─▶ Because only package-a==1.2.3+foo is available and you require package-a<=1.2.3, we can conclude that your requirements are unsatisfiable.
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ package-a==1.2.3+foo
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
// The version '1.2.3+foo' satisfies the constraint '<=1.2.3'.
|
// The version '1.2.3+foo' satisfies the constraint '<=1.2.3'.
|
||||||
assert_not_installed(
|
assert_installed(
|
||||||
&context.venv,
|
&context.venv,
|
||||||
"local_less_than_or_equal_a",
|
"local_less_than_or_equal_a",
|
||||||
|
"1.2.3+foo",
|
||||||
&context.temp_dir,
|
&context.temp_dir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1500,14 +1507,14 @@ fn local_not_used_with_sdist() {
|
||||||
Resolved 1 package in [TIME]
|
Resolved 1 package in [TIME]
|
||||||
Prepared 1 package in [TIME]
|
Prepared 1 package in [TIME]
|
||||||
Installed 1 package in [TIME]
|
Installed 1 package in [TIME]
|
||||||
+ package-a==1.2.3
|
+ package-a==1.2.3+foo
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
// The version '1.2.3' with an sdist satisfies the constraint '==1.2.3'.
|
// The version '1.2.3' with an sdist satisfies the constraint '==1.2.3'.
|
||||||
assert_installed(
|
assert_installed(
|
||||||
&context.venv,
|
&context.venv,
|
||||||
"local_not_used_with_sdist_a",
|
"local_not_used_with_sdist_a",
|
||||||
"1.2.3",
|
"1.2.3+foo",
|
||||||
&context.temp_dir,
|
&context.temp_dir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1520,8 +1527,10 @@ fn local_not_used_with_sdist() {
|
||||||
/// │ └── python3.8
|
/// │ └── python3.8
|
||||||
/// ├── root
|
/// ├── root
|
||||||
/// │ └── requires a==1.2.3
|
/// │ └── requires a==1.2.3
|
||||||
|
/// │ ├── satisfied by a-1.2.3+bar
|
||||||
/// │ └── satisfied by a-1.2.3+foo
|
/// │ └── satisfied by a-1.2.3+foo
|
||||||
/// └── a
|
/// └── a
|
||||||
|
/// ├── a-1.2.3+bar
|
||||||
/// └── a-1.2.3+foo
|
/// └── a-1.2.3+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1535,17 +1544,24 @@ fn local_simple() {
|
||||||
uv_snapshot!(filters, command(&context)
|
uv_snapshot!(filters, command(&context)
|
||||||
.arg("local-simple-a==1.2.3")
|
.arg("local-simple-a==1.2.3")
|
||||||
, @r###"
|
, @r###"
|
||||||
success: false
|
success: true
|
||||||
exit_code: 1
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
Resolved 1 package in [TIME]
|
||||||
╰─▶ Because there is no version of package-a==1.2.3 and you require package-a==1.2.3, we can conclude that your requirements are unsatisfiable.
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ package-a==1.2.3+foo
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
// The version '1.2.3+foo' satisfies the constraint '==1.2.3'.
|
// The version '1.2.3+foo' satisfies the constraint '==1.2.3'.
|
||||||
assert_not_installed(&context.venv, "local_simple_a", &context.temp_dir);
|
assert_installed(
|
||||||
|
&context.venv,
|
||||||
|
"local_simple_a",
|
||||||
|
"1.2.3+foo",
|
||||||
|
&context.temp_dir,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dependency depends on a conflicting local version of a direct dependency, but we can backtrack to a compatible version.
|
/// A dependency depends on a conflicting local version of a direct dependency, but we can backtrack to a compatible version.
|
||||||
|
@ -1563,14 +1579,14 @@ fn local_simple() {
|
||||||
/// ├── a
|
/// ├── a
|
||||||
/// │ ├── a-1.0.0
|
/// │ ├── a-1.0.0
|
||||||
/// │ │ └── requires b==2.0.0
|
/// │ │ └── requires b==2.0.0
|
||||||
/// │ │ ├── satisfied by b-2.0.0+foo
|
/// │ │ ├── satisfied by b-2.0.0+bar
|
||||||
/// │ │ └── satisfied by b-2.0.0+bar
|
/// │ │ └── satisfied by b-2.0.0+foo
|
||||||
/// │ └── a-2.0.0
|
/// │ └── a-2.0.0
|
||||||
/// │ └── requires b==2.0.0+bar
|
/// │ └── requires b==2.0.0+bar
|
||||||
/// │ └── satisfied by b-2.0.0+bar
|
/// │ └── satisfied by b-2.0.0+bar
|
||||||
/// └── b
|
/// └── b
|
||||||
/// ├── b-2.0.0+foo
|
/// ├── b-2.0.0+bar
|
||||||
/// └── b-2.0.0+bar
|
/// └── b-2.0.0+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
fn local_transitive_backtrack() {
|
fn local_transitive_backtrack() {
|
||||||
|
@ -1627,8 +1643,8 @@ fn local_transitive_backtrack() {
|
||||||
/// │ └── requires b==2.0.0+bar
|
/// │ └── requires b==2.0.0+bar
|
||||||
/// │ └── satisfied by b-2.0.0+bar
|
/// │ └── satisfied by b-2.0.0+bar
|
||||||
/// └── b
|
/// └── b
|
||||||
/// ├── b-2.0.0+foo
|
/// ├── b-2.0.0+bar
|
||||||
/// └── b-2.0.0+bar
|
/// └── b-2.0.0+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
fn local_transitive_conflicting() {
|
fn local_transitive_conflicting() {
|
||||||
|
@ -1677,9 +1693,11 @@ fn local_transitive_conflicting() {
|
||||||
/// │ └── a-1.0.0
|
/// │ └── a-1.0.0
|
||||||
/// │ └── requires b==2.0.0
|
/// │ └── requires b==2.0.0
|
||||||
/// │ ├── satisfied by b-2.0.0
|
/// │ ├── satisfied by b-2.0.0
|
||||||
|
/// │ ├── satisfied by b-2.0.0+bar
|
||||||
/// │ └── satisfied by b-2.0.0+foo
|
/// │ └── satisfied by b-2.0.0+foo
|
||||||
/// └── b
|
/// └── b
|
||||||
/// ├── b-2.0.0
|
/// ├── b-2.0.0
|
||||||
|
/// ├── b-2.0.0+bar
|
||||||
/// └── b-2.0.0+foo
|
/// └── b-2.0.0+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1693,20 +1711,29 @@ fn local_transitive_confounding() {
|
||||||
uv_snapshot!(filters, command(&context)
|
uv_snapshot!(filters, command(&context)
|
||||||
.arg("local-transitive-confounding-a")
|
.arg("local-transitive-confounding-a")
|
||||||
, @r###"
|
, @r###"
|
||||||
success: false
|
success: true
|
||||||
exit_code: 1
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
Resolved 2 packages in [TIME]
|
||||||
╰─▶ Because package-b==2.0.0 has no wheels with a matching Python ABI tag and package-a==1.0.0 depends on package-b==2.0.0, we can conclude that package-a==1.0.0 cannot be used.
|
Prepared 2 packages in [TIME]
|
||||||
And because only package-a==1.0.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable.
|
Installed 2 packages in [TIME]
|
||||||
|
+ package-a==1.0.0
|
||||||
|
+ package-b==2.0.0+foo
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
// The version '2.0.0+foo' satisfies the constraint '==2.0.0'.
|
// The version '2.0.0+foo' satisfies the constraint '==2.0.0'.
|
||||||
assert_not_installed(
|
assert_installed(
|
||||||
&context.venv,
|
&context.venv,
|
||||||
"local_transitive_confounding_a",
|
"local_transitive_confounding_a",
|
||||||
|
"1.0.0",
|
||||||
|
&context.temp_dir,
|
||||||
|
);
|
||||||
|
assert_installed(
|
||||||
|
&context.venv,
|
||||||
|
"local_transitive_confounding_b",
|
||||||
|
"2.0.0+foo",
|
||||||
&context.temp_dir,
|
&context.temp_dir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1725,8 +1752,10 @@ fn local_transitive_confounding() {
|
||||||
/// ├── a
|
/// ├── a
|
||||||
/// │ └── a-1.0.0
|
/// │ └── a-1.0.0
|
||||||
/// │ └── requires b>=2.0.0
|
/// │ └── requires b>=2.0.0
|
||||||
|
/// │ ├── satisfied by b-2.0.0+bar
|
||||||
/// │ └── satisfied by b-2.0.0+foo
|
/// │ └── satisfied by b-2.0.0+foo
|
||||||
/// └── b
|
/// └── b
|
||||||
|
/// ├── b-2.0.0+bar
|
||||||
/// └── b-2.0.0+foo
|
/// └── b-2.0.0+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1784,6 +1813,7 @@ fn local_transitive_greater_than_or_equal() {
|
||||||
/// │ └── requires b>2.0.0
|
/// │ └── requires b>2.0.0
|
||||||
/// │ └── unsatisfied: no matching version
|
/// │ └── unsatisfied: no matching version
|
||||||
/// └── b
|
/// └── b
|
||||||
|
/// ├── b-2.0.0+bar
|
||||||
/// └── b-2.0.0+foo
|
/// └── b-2.0.0+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1834,8 +1864,10 @@ fn local_transitive_greater_than() {
|
||||||
/// ├── a
|
/// ├── a
|
||||||
/// │ └── a-1.0.0
|
/// │ └── a-1.0.0
|
||||||
/// │ └── requires b<=2.0.0
|
/// │ └── requires b<=2.0.0
|
||||||
|
/// │ ├── satisfied by b-2.0.0+bar
|
||||||
/// │ └── satisfied by b-2.0.0+foo
|
/// │ └── satisfied by b-2.0.0+foo
|
||||||
/// └── b
|
/// └── b
|
||||||
|
/// ├── b-2.0.0+bar
|
||||||
/// └── b-2.0.0+foo
|
/// └── b-2.0.0+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1893,6 +1925,7 @@ fn local_transitive_less_than_or_equal() {
|
||||||
/// │ └── requires b<2.0.0
|
/// │ └── requires b<2.0.0
|
||||||
/// │ └── unsatisfied: no matching version
|
/// │ └── unsatisfied: no matching version
|
||||||
/// └── b
|
/// └── b
|
||||||
|
/// ├── b-2.0.0+bar
|
||||||
/// └── b-2.0.0+foo
|
/// └── b-2.0.0+foo
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1943,9 +1976,11 @@ fn local_transitive_less_than() {
|
||||||
/// ├── a
|
/// ├── a
|
||||||
/// │ └── a-1.0.0
|
/// │ └── a-1.0.0
|
||||||
/// │ └── requires b==2.0.0
|
/// │ └── requires b==2.0.0
|
||||||
/// │ └── satisfied by b-2.0.0+foo
|
/// │ ├── satisfied by b-2.0.0+foo
|
||||||
|
/// │ └── satisfied by b-2.0.0+bar
|
||||||
/// └── b
|
/// └── b
|
||||||
/// └── b-2.0.0+foo
|
/// ├── b-2.0.0+foo
|
||||||
|
/// └── b-2.0.0+bar
|
||||||
/// ```
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
fn local_transitive() {
|
fn local_transitive() {
|
||||||
|
@ -2011,19 +2046,22 @@ fn local_used_without_sdist() {
|
||||||
uv_snapshot!(filters, command(&context)
|
uv_snapshot!(filters, command(&context)
|
||||||
.arg("local-used-without-sdist-a==1.2.3")
|
.arg("local-used-without-sdist-a==1.2.3")
|
||||||
, @r###"
|
, @r###"
|
||||||
success: false
|
success: true
|
||||||
exit_code: 1
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
Resolved 1 package in [TIME]
|
||||||
╰─▶ Because package-a==1.2.3 has no wheels with a matching Python ABI tag and you require package-a==1.2.3, we can conclude that your requirements are unsatisfiable.
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ package-a==1.2.3+foo
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
// The version '1.2.3+foo' satisfies the constraint '==1.2.3'.
|
// The version '1.2.3+foo' satisfies the constraint '==1.2.3'.
|
||||||
assert_not_installed(
|
assert_installed(
|
||||||
&context.venv,
|
&context.venv,
|
||||||
"local_used_without_sdist_a",
|
"local_used_without_sdist_a",
|
||||||
|
"1.2.3+foo",
|
||||||
&context.temp_dir,
|
&context.temp_dir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
source: crates/uv/tests/ecosystem.rs
|
source: crates/uv/tests/it/ecosystem.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
success: true
|
success: true
|
||||||
|
@ -7,4 +7,4 @@ exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Resolved 296 packages in [TIME]
|
Resolved 281 packages in [TIME]
|
||||||
|
|
|
@ -152,18 +152,6 @@ def main(scenarios: list[Path], snapshot_update: bool = True):
|
||||||
else:
|
else:
|
||||||
scenario["python_patch"] = False
|
scenario["python_patch"] = False
|
||||||
|
|
||||||
# We don't yet support local versions that aren't expressed as direct dependencies.
|
|
||||||
for scenario in data["scenarios"]:
|
|
||||||
expected = scenario["expected"]
|
|
||||||
|
|
||||||
if scenario["name"] in (
|
|
||||||
"local-less-than-or-equal",
|
|
||||||
"local-simple",
|
|
||||||
"local-transitive-confounding",
|
|
||||||
"local-used-without-sdist",
|
|
||||||
):
|
|
||||||
expected["satisfiable"] = False
|
|
||||||
|
|
||||||
# Split scenarios into `install`, `compile` and `lock` cases
|
# Split scenarios into `install`, `compile` and `lock` cases
|
||||||
install_scenarios = []
|
install_scenarios = []
|
||||||
compile_scenarios = []
|
compile_scenarios = []
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue