mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-11 16:34:33 +00:00
Store unsupported tags in wheel filename (#10665)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on linux (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86_64 (push) Blocked by required conditions
CI / check system | python3.10 on windows (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on opensuse (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | conda3.11 on linux (push) Blocked by required conditions
CI / check system | conda3.8 on linux (push) Blocked by required conditions
CI / check system | conda3.11 on macos (push) Blocked by required conditions
CI / check system | conda3.8 on macos (push) Blocked by required conditions
CI / check system | conda3.11 on windows (push) Blocked by required conditions
CI / check system | conda3.8 on windows (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on linux (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86_64 (push) Blocked by required conditions
CI / check system | python3.10 on windows (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on opensuse (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | conda3.11 on linux (push) Blocked by required conditions
CI / check system | conda3.8 on linux (push) Blocked by required conditions
CI / check system | conda3.11 on macos (push) Blocked by required conditions
CI / check system | conda3.8 on macos (push) Blocked by required conditions
CI / check system | conda3.11 on windows (push) Blocked by required conditions
CI / check system | conda3.8 on windows (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
## Summary We can retain the small-size advantage of our new tags by moving the "unknown tag" case into `WheelTagLarge`. This ensures that we can still represent unknown tags, but avoid paying the cost for them.
This commit is contained in:
parent
80bdb3a997
commit
dce7b9da13
12 changed files with 273 additions and 305 deletions
|
|
@ -7,7 +7,7 @@ pub use build_tag::{BuildTag, BuildTagError};
|
||||||
pub use egg::{EggInfoFilename, EggInfoFilenameError};
|
pub use egg::{EggInfoFilename, EggInfoFilenameError};
|
||||||
pub use extension::{DistExtension, ExtensionError, SourceDistExtension};
|
pub use extension::{DistExtension, ExtensionError, SourceDistExtension};
|
||||||
pub use source_dist::{SourceDistFilename, SourceDistFilenameError};
|
pub use source_dist::{SourceDistFilename, SourceDistFilenameError};
|
||||||
pub use wheel::{TagSet, WheelFilename, WheelFilenameError};
|
pub use wheel::{WheelFilename, WheelFilenameError};
|
||||||
|
|
||||||
mod build_tag;
|
mod build_tag;
|
||||||
mod egg;
|
mod egg;
|
||||||
|
|
@ -15,6 +15,7 @@ mod extension;
|
||||||
mod source_dist;
|
mod source_dist;
|
||||||
mod splitter;
|
mod splitter;
|
||||||
mod wheel;
|
mod wheel;
|
||||||
|
mod wheel_tag;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum DistFilename {
|
pub enum DistFilename {
|
||||||
|
|
@ -97,13 +98,9 @@ impl Display for DistFilename {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::WheelFilename;
|
use crate::WheelFilename;
|
||||||
use uv_platform_tags::{AbiTag, LanguageTag, PlatformTag};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wheel_filename_size() {
|
fn wheel_filename_size() {
|
||||||
assert_eq!(size_of::<WheelFilename>(), 72);
|
assert_eq!(size_of::<WheelFilename>(), 48);
|
||||||
assert_eq!(size_of::<LanguageTag>(), 16);
|
|
||||||
assert_eq!(size_of::<AbiTag>(), 16);
|
|
||||||
assert_eq!(size_of::<PlatformTag>(), 16);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ Ok(
|
||||||
platform_tag: [
|
platform_tag: [
|
||||||
Any,
|
Any,
|
||||||
],
|
],
|
||||||
|
repr: "202206090410-py3-none-any",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ Ok(
|
||||||
arch: X86_64,
|
arch: X86_64,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
repr: "cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use uv_platform_tags::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::splitter::MemchrSplitter;
|
use crate::splitter::MemchrSplitter;
|
||||||
|
use crate::wheel_tag::{WheelTag, WheelTagLarge, WheelTagSmall};
|
||||||
use crate::{BuildTag, BuildTagError};
|
use crate::{BuildTag, BuildTagError};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
@ -177,7 +178,7 @@ impl WheelFilename {
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let (name, version, build_tag, python_tag, abi_tag, platform_tag, is_large) =
|
let (name, version, build_tag, python_tag, abi_tag, platform_tag, is_small) =
|
||||||
if let Some(platform_tag) = splitter.next() {
|
if let Some(platform_tag) = splitter.next() {
|
||||||
if splitter.next().is_some() {
|
if splitter.next().is_some() {
|
||||||
return Err(WheelFilenameError::InvalidWheelFileName(
|
return Err(WheelFilenameError::InvalidWheelFileName(
|
||||||
|
|
@ -193,7 +194,7 @@ impl WheelFilename {
|
||||||
&stem[abi_tag_or_platform_tag + 1..platform_tag],
|
&stem[abi_tag_or_platform_tag + 1..platform_tag],
|
||||||
&stem[platform_tag + 1..],
|
&stem[platform_tag + 1..],
|
||||||
// Always take the slow path if a build tag is present.
|
// Always take the slow path if a build tag is present.
|
||||||
true,
|
false,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
|
|
@ -206,7 +207,7 @@ impl WheelFilename {
|
||||||
// Determine whether any of the tag types contain a period, which would indicate
|
// Determine whether any of the tag types contain a period, which would indicate
|
||||||
// that at least one of the tag types includes multiple tags (which in turn
|
// that at least one of the tag types includes multiple tags (which in turn
|
||||||
// necessitates taking the slow path).
|
// necessitates taking the slow path).
|
||||||
memchr(b'.', stem[build_tag_or_python_tag..].as_bytes()).is_some(),
|
memchr(b'.', stem[build_tag_or_python_tag..].as_bytes()).is_none(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -221,44 +222,38 @@ impl WheelFilename {
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
let tags = if is_large {
|
let tags = if let Some(small) = is_small
|
||||||
|
.then(|| {
|
||||||
|
Some(WheelTagSmall {
|
||||||
|
python_tag: LanguageTag::from_str(python_tag).ok()?,
|
||||||
|
abi_tag: AbiTag::from_str(abi_tag).ok()?,
|
||||||
|
platform_tag: PlatformTag::from_str(platform_tag).ok()?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
WheelTag::Small { small }
|
||||||
|
} else {
|
||||||
|
// Store the plaintext representation of the tags.
|
||||||
|
let repr = &stem[build_tag_or_python_tag + 1..];
|
||||||
WheelTag::Large {
|
WheelTag::Large {
|
||||||
large: Box::new(WheelTagLarge {
|
large: Box::new(WheelTagLarge {
|
||||||
build_tag,
|
build_tag,
|
||||||
python_tag: MemchrSplitter::split(python_tag, b'.')
|
python_tag: MemchrSplitter::split(python_tag, b'.')
|
||||||
.map(LanguageTag::from_str)
|
.map(LanguageTag::from_str)
|
||||||
.collect::<Result<_, _>>()
|
.filter_map(Result::ok)
|
||||||
.map_err(|err| {
|
.collect(),
|
||||||
WheelFilenameError::InvalidLanguageTag(filename.to_string(), err)
|
|
||||||
})?,
|
|
||||||
abi_tag: MemchrSplitter::split(abi_tag, b'.')
|
abi_tag: MemchrSplitter::split(abi_tag, b'.')
|
||||||
.map(AbiTag::from_str)
|
.map(AbiTag::from_str)
|
||||||
.collect::<Result<_, _>>()
|
.filter_map(Result::ok)
|
||||||
.map_err(|err| {
|
.collect(),
|
||||||
WheelFilenameError::InvalidAbiTag(filename.to_string(), err)
|
|
||||||
})?,
|
|
||||||
platform_tag: MemchrSplitter::split(platform_tag, b'.')
|
platform_tag: MemchrSplitter::split(platform_tag, b'.')
|
||||||
.map(PlatformTag::from_str)
|
.map(PlatformTag::from_str)
|
||||||
.collect::<Result<_, _>>()
|
.filter_map(Result::ok)
|
||||||
.map_err(|err| {
|
.collect(),
|
||||||
WheelFilenameError::InvalidPlatformTag(filename.to_string(), err)
|
repr: repr.into(),
|
||||||
})?,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
WheelTag::Small {
|
|
||||||
small: WheelTagSmall {
|
|
||||||
python_tag: LanguageTag::from_str(python_tag).map_err(|err| {
|
|
||||||
WheelFilenameError::InvalidLanguageTag(filename.to_string(), err)
|
|
||||||
})?,
|
|
||||||
abi_tag: AbiTag::from_str(abi_tag).map_err(|err| {
|
|
||||||
WheelFilenameError::InvalidAbiTag(filename.to_string(), err)
|
|
||||||
})?,
|
|
||||||
platform_tag: PlatformTag::from_str(platform_tag).map_err(|err| {
|
|
||||||
WheelFilenameError::InvalidPlatformTag(filename.to_string(), err)
|
|
||||||
})?,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -311,124 +306,6 @@ impl Serialize for WheelFilename {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`SmallVec`] type for storing tags.
|
|
||||||
///
|
|
||||||
/// Wheels tend to include a single language, ABI, and platform tag, so we use a [`SmallVec`] with a
|
|
||||||
/// capacity of 1 to optimize for this common case.
|
|
||||||
pub type TagSet<T> = smallvec::SmallVec<[T; 3]>;
|
|
||||||
|
|
||||||
/// The portion of the wheel filename following the name and version: the optional build tag, along
|
|
||||||
/// with the Python tag(s), ABI tag(s), and platform tag(s).
|
|
||||||
///
|
|
||||||
/// Most wheels consist of a single Python, ABI, and platform tag (and no build tag). We represent
|
|
||||||
/// such wheels with [`WheelTagSmall`], a variant with a smaller memory footprint and (generally)
|
|
||||||
/// zero allocations. The [`WheelTagLarge`] variant is used for wheels with multiple tags and/or a
|
|
||||||
/// build tag.
|
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
Clone,
|
|
||||||
Eq,
|
|
||||||
PartialEq,
|
|
||||||
Ord,
|
|
||||||
PartialOrd,
|
|
||||||
Hash,
|
|
||||||
rkyv::Archive,
|
|
||||||
rkyv::Deserialize,
|
|
||||||
rkyv::Serialize,
|
|
||||||
)]
|
|
||||||
#[rkyv(derive(Debug))]
|
|
||||||
enum WheelTag {
|
|
||||||
Small { small: WheelTagSmall },
|
|
||||||
Large { large: Box<WheelTagLarge> },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for WheelTag {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Small { small } => write!(f, "{small}"),
|
|
||||||
Self::Large { large } => write!(f, "{large}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
Clone,
|
|
||||||
Eq,
|
|
||||||
PartialEq,
|
|
||||||
Ord,
|
|
||||||
PartialOrd,
|
|
||||||
Hash,
|
|
||||||
rkyv::Archive,
|
|
||||||
rkyv::Deserialize,
|
|
||||||
rkyv::Serialize,
|
|
||||||
)]
|
|
||||||
#[rkyv(derive(Debug))]
|
|
||||||
#[allow(clippy::struct_field_names)]
|
|
||||||
struct WheelTagSmall {
|
|
||||||
python_tag: LanguageTag,
|
|
||||||
abi_tag: AbiTag,
|
|
||||||
platform_tag: PlatformTag,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for WheelTagSmall {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}-{}-{}",
|
|
||||||
self.python_tag, self.abi_tag, self.platform_tag
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
Clone,
|
|
||||||
Eq,
|
|
||||||
PartialEq,
|
|
||||||
Ord,
|
|
||||||
PartialOrd,
|
|
||||||
Hash,
|
|
||||||
rkyv::Archive,
|
|
||||||
rkyv::Deserialize,
|
|
||||||
rkyv::Serialize,
|
|
||||||
)]
|
|
||||||
#[rkyv(derive(Debug))]
|
|
||||||
#[allow(clippy::struct_field_names)]
|
|
||||||
pub struct WheelTagLarge {
|
|
||||||
build_tag: Option<BuildTag>,
|
|
||||||
python_tag: TagSet<LanguageTag>,
|
|
||||||
abi_tag: TagSet<AbiTag>,
|
|
||||||
platform_tag: TagSet<PlatformTag>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for WheelTagLarge {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if let Some(build_tag) = &self.build_tag {
|
|
||||||
write!(f, "{build_tag}-")?;
|
|
||||||
}
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}-{}-{}",
|
|
||||||
self.python_tag
|
|
||||||
.iter()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("."),
|
|
||||||
self.abi_tag
|
|
||||||
.iter()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("."),
|
|
||||||
self.platform_tag
|
|
||||||
.iter()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("."),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum WheelFilenameError {
|
pub enum WheelFilenameError {
|
||||||
#[error("The wheel filename \"{0}\" is invalid: {1}")]
|
#[error("The wheel filename \"{0}\" is invalid: {1}")]
|
||||||
|
|
|
||||||
115
crates/uv-distribution-filename/src/wheel_tag.rs
Normal file
115
crates/uv-distribution-filename/src/wheel_tag.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
use crate::BuildTag;
|
||||||
|
use uv_platform_tags::{AbiTag, LanguageTag, PlatformTag};
|
||||||
|
use uv_small_str::SmallString;
|
||||||
|
|
||||||
|
/// A [`SmallVec`] type for storing tags.
|
||||||
|
///
|
||||||
|
/// Wheels tend to include a single language, ABI, and platform tag, so we use a [`SmallVec`] with a
|
||||||
|
/// capacity of 1 to optimize for this common case.
|
||||||
|
pub(crate) type TagSet<T> = smallvec::SmallVec<[T; 3]>;
|
||||||
|
|
||||||
|
/// The portion of the wheel filename following the name and version: the optional build tag, along
|
||||||
|
/// with the Python tag(s), ABI tag(s), and platform tag(s).
|
||||||
|
///
|
||||||
|
/// Most wheels consist of a single Python, ABI, and platform tag (and no build tag). We represent
|
||||||
|
/// such wheels with [`WheelTagSmall`], a variant with a smaller memory footprint and (generally)
|
||||||
|
/// zero allocations. The [`WheelTagLarge`] variant is used for wheels with multiple tags, a build
|
||||||
|
/// tag, or an unsupported tag (i.e., a tag that can't be represented by [`LanguageTag`],
|
||||||
|
/// [`AbiTag`], or [`PlatformTag`]). (Unsupported tags are filtered out, but retained in the display
|
||||||
|
/// representation of [`WheelTagLarge`].)
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Hash,
|
||||||
|
rkyv::Archive,
|
||||||
|
rkyv::Deserialize,
|
||||||
|
rkyv::Serialize,
|
||||||
|
)]
|
||||||
|
#[rkyv(derive(Debug))]
|
||||||
|
pub(crate) enum WheelTag {
|
||||||
|
Small { small: WheelTagSmall },
|
||||||
|
Large { large: Box<WheelTagLarge> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for WheelTag {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Small { small } => write!(f, "{small}"),
|
||||||
|
Self::Large { large } => write!(f, "{large}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Hash,
|
||||||
|
rkyv::Archive,
|
||||||
|
rkyv::Deserialize,
|
||||||
|
rkyv::Serialize,
|
||||||
|
)]
|
||||||
|
#[rkyv(derive(Debug))]
|
||||||
|
#[allow(clippy::struct_field_names)]
|
||||||
|
pub(crate) struct WheelTagSmall {
|
||||||
|
/// The Python tag, e.g., `py3` in `1.2.3-py3-none-any`.
|
||||||
|
pub(crate) python_tag: LanguageTag,
|
||||||
|
/// The ABI tag, e.g., `none` in `1.2.3-py3-none-any`.
|
||||||
|
pub(crate) abi_tag: AbiTag,
|
||||||
|
/// The platform tag, e.g., `none` in `1.2.3-py3-none-any`.
|
||||||
|
pub(crate) platform_tag: PlatformTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for WheelTagSmall {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}-{}-{}",
|
||||||
|
self.python_tag, self.abi_tag, self.platform_tag
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Hash,
|
||||||
|
rkyv::Archive,
|
||||||
|
rkyv::Deserialize,
|
||||||
|
rkyv::Serialize,
|
||||||
|
)]
|
||||||
|
#[rkyv(derive(Debug))]
|
||||||
|
#[allow(clippy::struct_field_names)]
|
||||||
|
pub(crate) struct WheelTagLarge {
|
||||||
|
/// The optional build tag, e.g., `73` in `1.2.3-73-py3-none-any`.
|
||||||
|
pub(crate) build_tag: Option<BuildTag>,
|
||||||
|
/// The Python tag(s), e.g., `py3` in `1.2.3-73-py3-none-any`.
|
||||||
|
pub(crate) python_tag: TagSet<LanguageTag>,
|
||||||
|
/// The ABI tag(s), e.g., `none` in `1.2.3-73-py3-none-any`.
|
||||||
|
pub(crate) abi_tag: TagSet<AbiTag>,
|
||||||
|
/// The platform tag(s), e.g., `none` in `1.2.3-73-py3-none-any`.
|
||||||
|
pub(crate) platform_tag: TagSet<PlatformTag>,
|
||||||
|
/// The string representation of the tag.
|
||||||
|
///
|
||||||
|
/// Preserves any unsupported tags that were filtered out when parsing the wheel filename.
|
||||||
|
pub(crate) repr: SmallString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for WheelTagLarge {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.repr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1343,8 +1343,8 @@ mod test {
|
||||||
/// Ensure that we don't accidentally grow the `Dist` sizes.
|
/// Ensure that we don't accidentally grow the `Dist` sizes.
|
||||||
#[test]
|
#[test]
|
||||||
fn dist_size() {
|
fn dist_size() {
|
||||||
assert!(size_of::<Dist>() <= 208, "{}", size_of::<Dist>());
|
assert!(size_of::<Dist>() <= 200, "{}", size_of::<Dist>());
|
||||||
assert!(size_of::<BuiltDist>() <= 208, "{}", size_of::<BuiltDist>());
|
assert!(size_of::<BuiltDist>() <= 200, "{}", size_of::<BuiltDist>());
|
||||||
assert!(
|
assert!(
|
||||||
size_of::<SourceDist>() <= 176,
|
size_of::<SourceDist>() <= 176,
|
||||||
"{}",
|
"{}",
|
||||||
|
|
|
||||||
|
|
@ -523,20 +523,20 @@ impl PrioritizedDist {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the set of all Python tags for the distribution.
|
/// Returns the set of all Python tags for the distribution.
|
||||||
pub fn python_tags(&self) -> BTreeSet<&LanguageTag> {
|
pub fn python_tags(&self) -> BTreeSet<LanguageTag> {
|
||||||
self.0
|
self.0
|
||||||
.wheels
|
.wheels
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(wheel, _)| wheel.filename.python_tags().iter())
|
.flat_map(|(wheel, _)| wheel.filename.python_tags().iter().copied())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the set of all ABI tags for the distribution.
|
/// Returns the set of all ABI tags for the distribution.
|
||||||
pub fn abi_tags(&self) -> BTreeSet<&AbiTag> {
|
pub fn abi_tags(&self) -> BTreeSet<AbiTag> {
|
||||||
self.0
|
self.0
|
||||||
.wheels
|
.wheels
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(wheel, _)| wheel.filename.abi_tags().iter())
|
.flat_map(|(wheel, _)| wheel.filename.abi_tags().iter().copied())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -547,7 +547,7 @@ impl PrioritizedDist {
|
||||||
for (wheel, _) in &self.0.wheels {
|
for (wheel, _) in &self.0.wheels {
|
||||||
for wheel_py in wheel.filename.python_tags() {
|
for wheel_py in wheel.filename.python_tags() {
|
||||||
for wheel_abi in wheel.filename.abi_tags() {
|
for wheel_abi in wheel.filename.abi_tags() {
|
||||||
if tags.is_compatible_abi(wheel_py, wheel_abi) {
|
if tags.is_compatible_abi(*wheel_py, *wheel_abi) {
|
||||||
candidates.extend(wheel.filename.platform_tags().iter());
|
candidates.extend(wheel.filename.platform_tags().iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use uv_small_str::SmallString;
|
|
||||||
|
|
||||||
/// A tag to represent the ABI compatibility of a Python distribution.
|
/// A tag to represent the ABI compatibility of a Python distribution.
|
||||||
///
|
///
|
||||||
/// This is the second segment in the wheel filename, following the language tag. For example,
|
/// This is the second segment in the wheel filename, following the language tag. For example,
|
||||||
/// in `cp39-none-manylinux_2_24_x86_64.whl`, the ABI tag is `none`.
|
/// in `cp39-none-manylinux_2_24_x86_64.whl`, the ABI tag is `none`.
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
|
Copy,
|
||||||
Clone,
|
Clone,
|
||||||
Eq,
|
Eq,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
|
@ -42,13 +41,11 @@ pub enum AbiTag {
|
||||||
},
|
},
|
||||||
/// Ex) `pyston_23_x86_64_linux_gnu`
|
/// Ex) `pyston_23_x86_64_linux_gnu`
|
||||||
Pyston { implementation_version: (u8, u8) },
|
Pyston { implementation_version: (u8, u8) },
|
||||||
/// Ex) `pypy_73`
|
|
||||||
Unknown { tag: SmallString },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbiTag {
|
impl AbiTag {
|
||||||
/// Return a pretty string representation of the ABI tag.
|
/// Return a pretty string representation of the ABI tag.
|
||||||
pub fn pretty(&self) -> Option<String> {
|
pub fn pretty(self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
AbiTag::None => None,
|
AbiTag::None => None,
|
||||||
AbiTag::Abi3 => None,
|
AbiTag::Abi3 => None,
|
||||||
|
|
@ -70,7 +67,6 @@ impl AbiTag {
|
||||||
implementation_version.0, implementation_version.1
|
implementation_version.0, implementation_version.1
|
||||||
)),
|
)),
|
||||||
AbiTag::Pyston { .. } => Some("Pyston".to_string()),
|
AbiTag::Pyston { .. } => Some("Pyston".to_string()),
|
||||||
AbiTag::Unknown { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,14 +117,16 @@ impl std::fmt::Display for AbiTag {
|
||||||
} => {
|
} => {
|
||||||
write!(f, "pyston_{impl_major}{impl_minor}_x86_64_linux_gnu")
|
write!(f, "pyston_{impl_major}{impl_minor}_x86_64_linux_gnu")
|
||||||
}
|
}
|
||||||
Self::Unknown { tag } => write!(f, "{tag}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbiTag {
|
impl FromStr for AbiTag {
|
||||||
|
type Err = ParseAbiTagError;
|
||||||
|
|
||||||
/// Parse an [`AbiTag`] from a string.
|
/// Parse an [`AbiTag`] from a string.
|
||||||
fn parse(s: &str) -> Result<Self, ParseAbiTagError> {
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
/// Parse a Python version from a string (e.g., convert `39` into `(3, 9)`).
|
/// Parse a Python version from a string (e.g., convert `39` into `(3, 9)`).
|
||||||
fn parse_python_version(
|
fn parse_python_version(
|
||||||
version_str: &str,
|
version_str: &str,
|
||||||
|
|
@ -290,14 +288,6 @@ impl AbiTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for AbiTag {
|
|
||||||
type Err = ParseAbiTagError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(AbiTag::parse(s).unwrap_or_else(|_| AbiTag::Unknown { tag: s.into() }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
pub enum ParseAbiTagError {
|
pub enum ParseAbiTagError {
|
||||||
#[error("Unknown ABI tag format: {0}")]
|
#[error("Unknown ABI tag format: {0}")]
|
||||||
|
|
@ -351,17 +341,19 @@ pub enum ParseAbiTagError {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::abi_tag::{AbiTag, ParseAbiTagError};
|
use crate::abi_tag::{AbiTag, ParseAbiTagError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn none_abi() {
|
fn none_abi() {
|
||||||
assert_eq!(AbiTag::parse("none"), Ok(AbiTag::None));
|
assert_eq!(AbiTag::from_str("none"), Ok(AbiTag::None));
|
||||||
assert_eq!(AbiTag::None.to_string(), "none");
|
assert_eq!(AbiTag::None.to_string(), "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn abi3() {
|
fn abi3() {
|
||||||
assert_eq!(AbiTag::parse("abi3"), Ok(AbiTag::Abi3));
|
assert_eq!(AbiTag::from_str("abi3"), Ok(AbiTag::Abi3));
|
||||||
assert_eq!(AbiTag::Abi3.to_string(), "abi3");
|
assert_eq!(AbiTag::Abi3.to_string(), "abi3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,25 +363,25 @@ mod tests {
|
||||||
gil_disabled: false,
|
gil_disabled: false,
|
||||||
python_version: (3, 9),
|
python_version: (3, 9),
|
||||||
};
|
};
|
||||||
assert_eq!(AbiTag::parse("cp39").as_ref(), Ok(&tag));
|
assert_eq!(AbiTag::from_str("cp39"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "cp39");
|
assert_eq!(tag.to_string(), "cp39");
|
||||||
|
|
||||||
let tag = AbiTag::CPython {
|
let tag = AbiTag::CPython {
|
||||||
gil_disabled: false,
|
gil_disabled: false,
|
||||||
python_version: (3, 7),
|
python_version: (3, 7),
|
||||||
};
|
};
|
||||||
assert_eq!(AbiTag::parse("cp37m").as_ref(), Ok(&tag));
|
assert_eq!(AbiTag::from_str("cp37m"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "cp37m");
|
assert_eq!(tag.to_string(), "cp37m");
|
||||||
|
|
||||||
let tag = AbiTag::CPython {
|
let tag = AbiTag::CPython {
|
||||||
gil_disabled: true,
|
gil_disabled: true,
|
||||||
python_version: (3, 13),
|
python_version: (3, 13),
|
||||||
};
|
};
|
||||||
assert_eq!(AbiTag::parse("cp313t").as_ref(), Ok(&tag));
|
assert_eq!(AbiTag::from_str("cp313t"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "cp313t");
|
assert_eq!(tag.to_string(), "cp313t");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("cpXY"),
|
AbiTag::from_str("cpXY"),
|
||||||
Err(ParseAbiTagError::MissingMajorVersion {
|
Err(ParseAbiTagError::MissingMajorVersion {
|
||||||
implementation: "CPython",
|
implementation: "CPython",
|
||||||
tag: "cpXY".to_string()
|
tag: "cpXY".to_string()
|
||||||
|
|
@ -403,32 +395,32 @@ mod tests {
|
||||||
python_version: Some((3, 9)),
|
python_version: Some((3, 9)),
|
||||||
implementation_version: (7, 3),
|
implementation_version: (7, 3),
|
||||||
};
|
};
|
||||||
assert_eq!(AbiTag::parse("pypy39_pp73").as_ref(), Ok(&tag));
|
assert_eq!(AbiTag::from_str("pypy39_pp73"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "pypy39_pp73");
|
assert_eq!(tag.to_string(), "pypy39_pp73");
|
||||||
|
|
||||||
let tag = AbiTag::PyPy {
|
let tag = AbiTag::PyPy {
|
||||||
python_version: None,
|
python_version: None,
|
||||||
implementation_version: (7, 3),
|
implementation_version: (7, 3),
|
||||||
};
|
};
|
||||||
assert_eq!(AbiTag::parse("pypy_73").as_ref(), Ok(&tag));
|
assert_eq!(AbiTag::from_str("pypy_73").as_ref(), Ok(&tag));
|
||||||
assert_eq!(tag.to_string(), "pypy_73");
|
assert_eq!(tag.to_string(), "pypy_73");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("pypy39"),
|
AbiTag::from_str("pypy39"),
|
||||||
Err(ParseAbiTagError::InvalidFormat {
|
Err(ParseAbiTagError::InvalidFormat {
|
||||||
implementation: "PyPy",
|
implementation: "PyPy",
|
||||||
tag: "pypy39".to_string()
|
tag: "pypy39".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("pypy39_73"),
|
AbiTag::from_str("pypy39_73"),
|
||||||
Err(ParseAbiTagError::InvalidFormat {
|
Err(ParseAbiTagError::InvalidFormat {
|
||||||
implementation: "PyPy",
|
implementation: "PyPy",
|
||||||
tag: "pypy39_73".to_string()
|
tag: "pypy39_73".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("pypy39_ppXY"),
|
AbiTag::from_str("pypy39_ppXY"),
|
||||||
Err(ParseAbiTagError::InvalidImplMajorVersion {
|
Err(ParseAbiTagError::InvalidImplMajorVersion {
|
||||||
implementation: "PyPy",
|
implementation: "PyPy",
|
||||||
tag: "pypy39_ppXY".to_string()
|
tag: "pypy39_ppXY".to_string()
|
||||||
|
|
@ -443,27 +435,27 @@ mod tests {
|
||||||
implementation_version: (2, 40),
|
implementation_version: (2, 40),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("graalpy310_graalpy240_310_native").as_ref(),
|
AbiTag::from_str("graalpy310_graalpy240_310_native"),
|
||||||
Ok(&tag)
|
Ok(tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "graalpy310_graalpy240_310_native");
|
assert_eq!(tag.to_string(), "graalpy310_graalpy240_310_native");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("graalpy310"),
|
AbiTag::from_str("graalpy310"),
|
||||||
Err(ParseAbiTagError::InvalidFormat {
|
Err(ParseAbiTagError::InvalidFormat {
|
||||||
implementation: "GraalPy",
|
implementation: "GraalPy",
|
||||||
tag: "graalpy310".to_string()
|
tag: "graalpy310".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("graalpy310_240"),
|
AbiTag::from_str("graalpy310_240"),
|
||||||
Err(ParseAbiTagError::InvalidFormat {
|
Err(ParseAbiTagError::InvalidFormat {
|
||||||
implementation: "GraalPy",
|
implementation: "GraalPy",
|
||||||
tag: "graalpy310_240".to_string()
|
tag: "graalpy310_240".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("graalpy310_graalpyXY"),
|
AbiTag::from_str("graalpy310_graalpyXY"),
|
||||||
Err(ParseAbiTagError::InvalidFormat {
|
Err(ParseAbiTagError::InvalidFormat {
|
||||||
implementation: "GraalPy",
|
implementation: "GraalPy",
|
||||||
tag: "graalpy310_graalpyXY".to_string()
|
tag: "graalpy310_graalpyXY".to_string()
|
||||||
|
|
@ -476,21 +468,18 @@ mod tests {
|
||||||
let tag = AbiTag::Pyston {
|
let tag = AbiTag::Pyston {
|
||||||
implementation_version: (2, 3),
|
implementation_version: (2, 3),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(AbiTag::from_str("pyston_23_x86_64_linux_gnu"), Ok(tag));
|
||||||
AbiTag::parse("pyston_23_x86_64_linux_gnu").as_ref(),
|
|
||||||
Ok(&tag)
|
|
||||||
);
|
|
||||||
assert_eq!(tag.to_string(), "pyston_23_x86_64_linux_gnu");
|
assert_eq!(tag.to_string(), "pyston_23_x86_64_linux_gnu");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("pyston23_x86_64_linux_gnu"),
|
AbiTag::from_str("pyston23_x86_64_linux_gnu"),
|
||||||
Err(ParseAbiTagError::InvalidFormat {
|
Err(ParseAbiTagError::InvalidFormat {
|
||||||
implementation: "Pyston",
|
implementation: "Pyston",
|
||||||
tag: "pyston23_x86_64_linux_gnu".to_string()
|
tag: "pyston23_x86_64_linux_gnu".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("pyston_XY_x86_64_linux_gnu"),
|
AbiTag::from_str("pyston_XY_x86_64_linux_gnu"),
|
||||||
Err(ParseAbiTagError::InvalidImplMajorVersion {
|
Err(ParseAbiTagError::InvalidImplMajorVersion {
|
||||||
implementation: "Pyston",
|
implementation: "Pyston",
|
||||||
tag: "pyston_XY_x86_64_linux_gnu".to_string()
|
tag: "pyston_XY_x86_64_linux_gnu".to_string()
|
||||||
|
|
@ -501,11 +490,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_abi() {
|
fn unknown_abi() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse("unknown"),
|
AbiTag::from_str("unknown"),
|
||||||
Err(ParseAbiTagError::UnknownFormat("unknown".to_string()))
|
Err(ParseAbiTagError::UnknownFormat("unknown".to_string()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AbiTag::parse(""),
|
AbiTag::from_str(""),
|
||||||
Err(ParseAbiTagError::UnknownFormat(String::new()))
|
Err(ParseAbiTagError::UnknownFormat(String::new()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use uv_small_str::SmallString;
|
|
||||||
|
|
||||||
/// A tag to represent the language and implementation of the Python interpreter.
|
/// A tag to represent the language and implementation of the Python interpreter.
|
||||||
///
|
///
|
||||||
/// This is the first segment in the wheel filename. For example, in `cp39-none-manylinux_2_24_x86_64.whl`,
|
/// This is the first segment in the wheel filename. For example, in `cp39-none-manylinux_2_24_x86_64.whl`,
|
||||||
/// the language tag is `cp39`.
|
/// the language tag is `cp39`.
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
|
Copy,
|
||||||
Clone,
|
Clone,
|
||||||
Eq,
|
Eq,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
|
@ -33,13 +32,11 @@ pub enum LanguageTag {
|
||||||
GraalPy { python_version: (u8, u8) },
|
GraalPy { python_version: (u8, u8) },
|
||||||
/// Ex) `pyston38`
|
/// Ex) `pyston38`
|
||||||
Pyston { python_version: (u8, u8) },
|
Pyston { python_version: (u8, u8) },
|
||||||
/// Ex) `ironpython27`
|
|
||||||
Unknown { tag: SmallString },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageTag {
|
impl LanguageTag {
|
||||||
/// Return a pretty string representation of the language tag.
|
/// Return a pretty string representation of the language tag.
|
||||||
pub fn pretty(&self) -> Option<String> {
|
pub fn pretty(self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
Self::None => None,
|
Self::None => None,
|
||||||
Self::Python { major, minor } => {
|
Self::Python { major, minor } => {
|
||||||
|
|
@ -61,7 +58,6 @@ impl LanguageTag {
|
||||||
Self::Pyston {
|
Self::Pyston {
|
||||||
python_version: (major, minor),
|
python_version: (major, minor),
|
||||||
} => Some(format!("Pyston {major}.{minor}")),
|
} => Some(format!("Pyston {major}.{minor}")),
|
||||||
Self::Unknown { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,15 +94,16 @@ impl std::fmt::Display for LanguageTag {
|
||||||
} => {
|
} => {
|
||||||
write!(f, "pyston{major}{minor}")
|
write!(f, "pyston{major}{minor}")
|
||||||
}
|
}
|
||||||
Self::Unknown { tag } => write!(f, "{tag}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageTag {
|
impl FromStr for LanguageTag {
|
||||||
|
type Err = ParseLanguageTagError;
|
||||||
|
|
||||||
/// Parse a [`LanguageTag`] from a string.
|
/// Parse a [`LanguageTag`] from a string.
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
fn parse(s: &str) -> Result<Self, ParseLanguageTagError> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
/// Parse a Python version from a string (e.g., convert `39` into `(3, 9)`).
|
/// Parse a Python version from a string (e.g., convert `39` into `(3, 9)`).
|
||||||
fn parse_python_version(
|
fn parse_python_version(
|
||||||
version_str: &str,
|
version_str: &str,
|
||||||
|
|
@ -209,14 +206,6 @@ impl LanguageTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for LanguageTag {
|
|
||||||
type Err = ParseLanguageTagError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(LanguageTag::parse(s).unwrap_or_else(|_| LanguageTag::Unknown { tag: s.into() }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
pub enum ParseLanguageTagError {
|
pub enum ParseLanguageTagError {
|
||||||
#[error("Unknown language tag format: {0}")]
|
#[error("Unknown language tag format: {0}")]
|
||||||
|
|
@ -245,13 +234,14 @@ pub enum ParseLanguageTagError {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::language_tag::ParseLanguageTagError;
|
use crate::language_tag::ParseLanguageTagError;
|
||||||
use crate::LanguageTag;
|
use crate::LanguageTag;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn none() {
|
fn none() {
|
||||||
assert_eq!(LanguageTag::parse("none"), Ok(LanguageTag::None));
|
assert_eq!(LanguageTag::from_str("none"), Ok(LanguageTag::None));
|
||||||
assert_eq!(LanguageTag::None.to_string(), "none");
|
assert_eq!(LanguageTag::None.to_string(), "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,32 +251,32 @@ mod tests {
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
};
|
};
|
||||||
assert_eq!(LanguageTag::parse("py3").as_ref(), Ok(&tag));
|
assert_eq!(LanguageTag::from_str("py3"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "py3");
|
assert_eq!(tag.to_string(), "py3");
|
||||||
|
|
||||||
let tag = LanguageTag::Python {
|
let tag = LanguageTag::Python {
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: Some(9),
|
minor: Some(9),
|
||||||
};
|
};
|
||||||
assert_eq!(LanguageTag::parse("py39").as_ref(), Ok(&tag));
|
assert_eq!(LanguageTag::from_str("py39"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "py39");
|
assert_eq!(tag.to_string(), "py39");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("py"),
|
LanguageTag::from_str("py"),
|
||||||
Err(ParseLanguageTagError::MissingMajorVersion {
|
Err(ParseLanguageTagError::MissingMajorVersion {
|
||||||
implementation: "Python",
|
implementation: "Python",
|
||||||
tag: "py".to_string()
|
tag: "py".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("pyX"),
|
LanguageTag::from_str("pyX"),
|
||||||
Err(ParseLanguageTagError::InvalidMajorVersion {
|
Err(ParseLanguageTagError::InvalidMajorVersion {
|
||||||
implementation: "Python",
|
implementation: "Python",
|
||||||
tag: "pyX".to_string()
|
tag: "pyX".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("py3X"),
|
LanguageTag::from_str("py3X"),
|
||||||
Err(ParseLanguageTagError::InvalidMinorVersion {
|
Err(ParseLanguageTagError::InvalidMinorVersion {
|
||||||
implementation: "Python",
|
implementation: "Python",
|
||||||
tag: "py3X".to_string()
|
tag: "py3X".to_string()
|
||||||
|
|
@ -299,25 +289,25 @@ mod tests {
|
||||||
let tag = LanguageTag::CPython {
|
let tag = LanguageTag::CPython {
|
||||||
python_version: (3, 9),
|
python_version: (3, 9),
|
||||||
};
|
};
|
||||||
assert_eq!(LanguageTag::parse("cp39").as_ref(), Ok(&tag));
|
assert_eq!(LanguageTag::from_str("cp39"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "cp39");
|
assert_eq!(tag.to_string(), "cp39");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("cp"),
|
LanguageTag::from_str("cp"),
|
||||||
Err(ParseLanguageTagError::MissingMajorVersion {
|
Err(ParseLanguageTagError::MissingMajorVersion {
|
||||||
implementation: "CPython",
|
implementation: "CPython",
|
||||||
tag: "cp".to_string()
|
tag: "cp".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("cpX"),
|
LanguageTag::from_str("cpX"),
|
||||||
Err(ParseLanguageTagError::InvalidMajorVersion {
|
Err(ParseLanguageTagError::InvalidMajorVersion {
|
||||||
implementation: "CPython",
|
implementation: "CPython",
|
||||||
tag: "cpX".to_string()
|
tag: "cpX".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("cp3X"),
|
LanguageTag::from_str("cp3X"),
|
||||||
Err(ParseLanguageTagError::InvalidMinorVersion {
|
Err(ParseLanguageTagError::InvalidMinorVersion {
|
||||||
implementation: "CPython",
|
implementation: "CPython",
|
||||||
tag: "cp3X".to_string()
|
tag: "cp3X".to_string()
|
||||||
|
|
@ -330,25 +320,25 @@ mod tests {
|
||||||
let tag = LanguageTag::PyPy {
|
let tag = LanguageTag::PyPy {
|
||||||
python_version: (3, 9),
|
python_version: (3, 9),
|
||||||
};
|
};
|
||||||
assert_eq!(LanguageTag::parse("pp39").as_ref(), Ok(&tag));
|
assert_eq!(LanguageTag::from_str("pp39"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "pp39");
|
assert_eq!(tag.to_string(), "pp39");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("pp"),
|
LanguageTag::from_str("pp"),
|
||||||
Err(ParseLanguageTagError::MissingMajorVersion {
|
Err(ParseLanguageTagError::MissingMajorVersion {
|
||||||
implementation: "PyPy",
|
implementation: "PyPy",
|
||||||
tag: "pp".to_string()
|
tag: "pp".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("ppX"),
|
LanguageTag::from_str("ppX"),
|
||||||
Err(ParseLanguageTagError::InvalidMajorVersion {
|
Err(ParseLanguageTagError::InvalidMajorVersion {
|
||||||
implementation: "PyPy",
|
implementation: "PyPy",
|
||||||
tag: "ppX".to_string()
|
tag: "ppX".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("pp3X"),
|
LanguageTag::from_str("pp3X"),
|
||||||
Err(ParseLanguageTagError::InvalidMinorVersion {
|
Err(ParseLanguageTagError::InvalidMinorVersion {
|
||||||
implementation: "PyPy",
|
implementation: "PyPy",
|
||||||
tag: "pp3X".to_string()
|
tag: "pp3X".to_string()
|
||||||
|
|
@ -361,25 +351,25 @@ mod tests {
|
||||||
let tag = LanguageTag::GraalPy {
|
let tag = LanguageTag::GraalPy {
|
||||||
python_version: (3, 10),
|
python_version: (3, 10),
|
||||||
};
|
};
|
||||||
assert_eq!(LanguageTag::parse("graalpy310").as_ref(), Ok(&tag));
|
assert_eq!(LanguageTag::from_str("graalpy310"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "graalpy310");
|
assert_eq!(tag.to_string(), "graalpy310");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("graalpy"),
|
LanguageTag::from_str("graalpy"),
|
||||||
Err(ParseLanguageTagError::MissingMajorVersion {
|
Err(ParseLanguageTagError::MissingMajorVersion {
|
||||||
implementation: "GraalPy",
|
implementation: "GraalPy",
|
||||||
tag: "graalpy".to_string()
|
tag: "graalpy".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("graalpyX"),
|
LanguageTag::from_str("graalpyX"),
|
||||||
Err(ParseLanguageTagError::InvalidMajorVersion {
|
Err(ParseLanguageTagError::InvalidMajorVersion {
|
||||||
implementation: "GraalPy",
|
implementation: "GraalPy",
|
||||||
tag: "graalpyX".to_string()
|
tag: "graalpyX".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("graalpy3X"),
|
LanguageTag::from_str("graalpy3X"),
|
||||||
Err(ParseLanguageTagError::InvalidMinorVersion {
|
Err(ParseLanguageTagError::InvalidMinorVersion {
|
||||||
implementation: "GraalPy",
|
implementation: "GraalPy",
|
||||||
tag: "graalpy3X".to_string()
|
tag: "graalpy3X".to_string()
|
||||||
|
|
@ -392,25 +382,25 @@ mod tests {
|
||||||
let tag = LanguageTag::Pyston {
|
let tag = LanguageTag::Pyston {
|
||||||
python_version: (3, 8),
|
python_version: (3, 8),
|
||||||
};
|
};
|
||||||
assert_eq!(LanguageTag::parse("pyston38").as_ref(), Ok(&tag));
|
assert_eq!(LanguageTag::from_str("pyston38"), Ok(tag));
|
||||||
assert_eq!(tag.to_string(), "pyston38");
|
assert_eq!(tag.to_string(), "pyston38");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("pyston"),
|
LanguageTag::from_str("pyston"),
|
||||||
Err(ParseLanguageTagError::MissingMajorVersion {
|
Err(ParseLanguageTagError::MissingMajorVersion {
|
||||||
implementation: "Pyston",
|
implementation: "Pyston",
|
||||||
tag: "pyston".to_string()
|
tag: "pyston".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("pystonX"),
|
LanguageTag::from_str("pystonX"),
|
||||||
Err(ParseLanguageTagError::InvalidMajorVersion {
|
Err(ParseLanguageTagError::InvalidMajorVersion {
|
||||||
implementation: "Pyston",
|
implementation: "Pyston",
|
||||||
tag: "pystonX".to_string()
|
tag: "pystonX".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("pyston3X"),
|
LanguageTag::from_str("pyston3X"),
|
||||||
Err(ParseLanguageTagError::InvalidMinorVersion {
|
Err(ParseLanguageTagError::InvalidMinorVersion {
|
||||||
implementation: "Pyston",
|
implementation: "Pyston",
|
||||||
tag: "pyston3X".to_string()
|
tag: "pyston3X".to_string()
|
||||||
|
|
@ -421,11 +411,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_language() {
|
fn unknown_language() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse("unknown"),
|
LanguageTag::from_str("unknown"),
|
||||||
Err(ParseLanguageTagError::UnknownFormat("unknown".to_string()))
|
Err(ParseLanguageTagError::UnknownFormat("unknown".to_string()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LanguageTag::parse(""),
|
LanguageTag::from_str(""),
|
||||||
Err(ParseLanguageTagError::UnknownFormat(String::new()))
|
Err(ParseLanguageTagError::UnknownFormat(String::new()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,6 @@ pub enum PlatformTag {
|
||||||
Illumos { release_arch: SmallString },
|
Illumos { release_arch: SmallString },
|
||||||
/// Ex) `solaris_11_4_x86_64`
|
/// Ex) `solaris_11_4_x86_64`
|
||||||
Solaris { release_arch: SmallString },
|
Solaris { release_arch: SmallString },
|
||||||
/// Ex) `win_ia64`
|
|
||||||
Unknown { tag: SmallString },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformTag {
|
impl PlatformTag {
|
||||||
|
|
@ -99,7 +97,6 @@ impl PlatformTag {
|
||||||
PlatformTag::Haiku { .. } => Some("Haiku"),
|
PlatformTag::Haiku { .. } => Some("Haiku"),
|
||||||
PlatformTag::Illumos { .. } => Some("Illumos"),
|
PlatformTag::Illumos { .. } => Some("Illumos"),
|
||||||
PlatformTag::Solaris { .. } => Some("Solaris"),
|
PlatformTag::Solaris { .. } => Some("Solaris"),
|
||||||
PlatformTag::Unknown { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,14 +262,15 @@ impl std::fmt::Display for PlatformTag {
|
||||||
Self::Haiku { release_arch } => write!(f, "haiku_{release_arch}"),
|
Self::Haiku { release_arch } => write!(f, "haiku_{release_arch}"),
|
||||||
Self::Illumos { release_arch } => write!(f, "illumos_{release_arch}"),
|
Self::Illumos { release_arch } => write!(f, "illumos_{release_arch}"),
|
||||||
Self::Solaris { release_arch } => write!(f, "solaris_{release_arch}_64bit"),
|
Self::Solaris { release_arch } => write!(f, "solaris_{release_arch}_64bit"),
|
||||||
Self::Unknown { tag } => write!(f, "{tag}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformTag {
|
impl FromStr for PlatformTag {
|
||||||
|
type Err = ParsePlatformTagError;
|
||||||
|
|
||||||
/// Parse a [`PlatformTag`] from a string.
|
/// Parse a [`PlatformTag`] from a string.
|
||||||
fn parse(s: &str) -> Result<Self, ParsePlatformTagError> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
// Match against any static variants.
|
// Match against any static variants.
|
||||||
match s {
|
match s {
|
||||||
"any" => return Ok(Self::Any),
|
"any" => return Ok(Self::Any),
|
||||||
|
|
@ -622,14 +620,6 @@ impl PlatformTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for PlatformTag {
|
|
||||||
type Err = ParsePlatformTagError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(PlatformTag::parse(s).unwrap_or_else(|_| PlatformTag::Unknown { tag: s.into() }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
pub enum ParsePlatformTagError {
|
pub enum ParsePlatformTagError {
|
||||||
#[error("Unknown platform tag format: {0}")]
|
#[error("Unknown platform tag format: {0}")]
|
||||||
|
|
@ -648,13 +638,14 @@ pub enum ParsePlatformTagError {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::platform_tag::{ParsePlatformTagError, PlatformTag};
|
use crate::platform_tag::{ParsePlatformTagError, PlatformTag};
|
||||||
use crate::{Arch, BinaryFormat};
|
use crate::{Arch, BinaryFormat};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn any_platform() {
|
fn any_platform() {
|
||||||
assert_eq!(PlatformTag::parse("any"), Ok(PlatformTag::Any));
|
assert_eq!(PlatformTag::from_str("any"), Ok(PlatformTag::Any));
|
||||||
assert_eq!(PlatformTag::Any.to_string(), "any");
|
assert_eq!(PlatformTag::Any.to_string(), "any");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -666,13 +657,13 @@ mod tests {
|
||||||
arch: Arch::X86_64,
|
arch: Arch::X86_64,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux_2_24_x86_64").as_ref(),
|
PlatformTag::from_str("manylinux_2_24_x86_64").as_ref(),
|
||||||
Ok(&tag)
|
Ok(&tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "manylinux_2_24_x86_64");
|
assert_eq!(tag.to_string(), "manylinux_2_24_x86_64");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux_x_24_x86_64"),
|
PlatformTag::from_str("manylinux_x_24_x86_64"),
|
||||||
Err(ParsePlatformTagError::InvalidMajorVersion {
|
Err(ParsePlatformTagError::InvalidMajorVersion {
|
||||||
platform: "manylinux",
|
platform: "manylinux",
|
||||||
tag: "manylinux_x_24_x86_64".to_string()
|
tag: "manylinux_x_24_x86_64".to_string()
|
||||||
|
|
@ -680,7 +671,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux_2_x_x86_64"),
|
PlatformTag::from_str("manylinux_2_x_x86_64"),
|
||||||
Err(ParsePlatformTagError::InvalidMinorVersion {
|
Err(ParsePlatformTagError::InvalidMinorVersion {
|
||||||
platform: "manylinux",
|
platform: "manylinux",
|
||||||
tag: "manylinux_2_x_x86_64".to_string()
|
tag: "manylinux_2_x_x86_64".to_string()
|
||||||
|
|
@ -688,7 +679,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux_2_24_invalid"),
|
PlatformTag::from_str("manylinux_2_24_invalid"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "manylinux",
|
platform: "manylinux",
|
||||||
tag: "manylinux_2_24_invalid".to_string()
|
tag: "manylinux_2_24_invalid".to_string()
|
||||||
|
|
@ -699,11 +690,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn manylinux1_platform() {
|
fn manylinux1_platform() {
|
||||||
let tag = PlatformTag::Manylinux1 { arch: Arch::X86_64 };
|
let tag = PlatformTag::Manylinux1 { arch: Arch::X86_64 };
|
||||||
assert_eq!(PlatformTag::parse("manylinux1_x86_64").as_ref(), Ok(&tag));
|
assert_eq!(
|
||||||
|
PlatformTag::from_str("manylinux1_x86_64").as_ref(),
|
||||||
|
Ok(&tag)
|
||||||
|
);
|
||||||
assert_eq!(tag.to_string(), "manylinux1_x86_64");
|
assert_eq!(tag.to_string(), "manylinux1_x86_64");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux1_invalid"),
|
PlatformTag::from_str("manylinux1_invalid"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "manylinux1",
|
platform: "manylinux1",
|
||||||
tag: "manylinux1_invalid".to_string()
|
tag: "manylinux1_invalid".to_string()
|
||||||
|
|
@ -715,13 +709,13 @@ mod tests {
|
||||||
fn manylinux2010_platform() {
|
fn manylinux2010_platform() {
|
||||||
let tag = PlatformTag::Manylinux2010 { arch: Arch::X86_64 };
|
let tag = PlatformTag::Manylinux2010 { arch: Arch::X86_64 };
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux2010_x86_64").as_ref(),
|
PlatformTag::from_str("manylinux2010_x86_64").as_ref(),
|
||||||
Ok(&tag)
|
Ok(&tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "manylinux2010_x86_64");
|
assert_eq!(tag.to_string(), "manylinux2010_x86_64");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux2010_invalid"),
|
PlatformTag::from_str("manylinux2010_invalid"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "manylinux2010",
|
platform: "manylinux2010",
|
||||||
tag: "manylinux2010_invalid".to_string()
|
tag: "manylinux2010_invalid".to_string()
|
||||||
|
|
@ -733,13 +727,13 @@ mod tests {
|
||||||
fn manylinux2014_platform() {
|
fn manylinux2014_platform() {
|
||||||
let tag = PlatformTag::Manylinux2014 { arch: Arch::X86_64 };
|
let tag = PlatformTag::Manylinux2014 { arch: Arch::X86_64 };
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux2014_x86_64").as_ref(),
|
PlatformTag::from_str("manylinux2014_x86_64").as_ref(),
|
||||||
Ok(&tag)
|
Ok(&tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "manylinux2014_x86_64");
|
assert_eq!(tag.to_string(), "manylinux2014_x86_64");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("manylinux2014_invalid"),
|
PlatformTag::from_str("manylinux2014_invalid"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "manylinux2014",
|
platform: "manylinux2014",
|
||||||
tag: "manylinux2014_invalid".to_string()
|
tag: "manylinux2014_invalid".to_string()
|
||||||
|
|
@ -750,11 +744,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn linux_platform() {
|
fn linux_platform() {
|
||||||
let tag = PlatformTag::Linux { arch: Arch::X86_64 };
|
let tag = PlatformTag::Linux { arch: Arch::X86_64 };
|
||||||
assert_eq!(PlatformTag::parse("linux_x86_64").as_ref(), Ok(&tag));
|
assert_eq!(PlatformTag::from_str("linux_x86_64").as_ref(), Ok(&tag));
|
||||||
assert_eq!(tag.to_string(), "linux_x86_64");
|
assert_eq!(tag.to_string(), "linux_x86_64");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("linux_invalid"),
|
PlatformTag::from_str("linux_invalid"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
tag: "linux_invalid".to_string()
|
tag: "linux_invalid".to_string()
|
||||||
|
|
@ -770,13 +764,13 @@ mod tests {
|
||||||
arch: Arch::X86_64,
|
arch: Arch::X86_64,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("musllinux_1_2_x86_64").as_ref(),
|
PlatformTag::from_str("musllinux_1_2_x86_64").as_ref(),
|
||||||
Ok(&tag)
|
Ok(&tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "musllinux_1_2_x86_64");
|
assert_eq!(tag.to_string(), "musllinux_1_2_x86_64");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("musllinux_x_2_x86_64"),
|
PlatformTag::from_str("musllinux_x_2_x86_64"),
|
||||||
Err(ParsePlatformTagError::InvalidMajorVersion {
|
Err(ParsePlatformTagError::InvalidMajorVersion {
|
||||||
platform: "musllinux",
|
platform: "musllinux",
|
||||||
tag: "musllinux_x_2_x86_64".to_string()
|
tag: "musllinux_x_2_x86_64".to_string()
|
||||||
|
|
@ -784,7 +778,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("musllinux_1_x_x86_64"),
|
PlatformTag::from_str("musllinux_1_x_x86_64"),
|
||||||
Err(ParsePlatformTagError::InvalidMinorVersion {
|
Err(ParsePlatformTagError::InvalidMinorVersion {
|
||||||
platform: "musllinux",
|
platform: "musllinux",
|
||||||
tag: "musllinux_1_x_x86_64".to_string()
|
tag: "musllinux_1_x_x86_64".to_string()
|
||||||
|
|
@ -792,7 +786,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("musllinux_1_2_invalid"),
|
PlatformTag::from_str("musllinux_1_2_invalid"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "musllinux",
|
platform: "musllinux",
|
||||||
tag: "musllinux_1_2_invalid".to_string()
|
tag: "musllinux_1_2_invalid".to_string()
|
||||||
|
|
@ -808,13 +802,13 @@ mod tests {
|
||||||
binary_format: BinaryFormat::Universal2,
|
binary_format: BinaryFormat::Universal2,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("macosx_11_0_universal2").as_ref(),
|
PlatformTag::from_str("macosx_11_0_universal2").as_ref(),
|
||||||
Ok(&tag)
|
Ok(&tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "macosx_11_0_universal2");
|
assert_eq!(tag.to_string(), "macosx_11_0_universal2");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("macosx_x_0_universal2"),
|
PlatformTag::from_str("macosx_x_0_universal2"),
|
||||||
Err(ParsePlatformTagError::InvalidMajorVersion {
|
Err(ParsePlatformTagError::InvalidMajorVersion {
|
||||||
platform: "macosx",
|
platform: "macosx",
|
||||||
tag: "macosx_x_0_universal2".to_string()
|
tag: "macosx_x_0_universal2".to_string()
|
||||||
|
|
@ -822,7 +816,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("macosx_11_x_universal2"),
|
PlatformTag::from_str("macosx_11_x_universal2"),
|
||||||
Err(ParsePlatformTagError::InvalidMinorVersion {
|
Err(ParsePlatformTagError::InvalidMinorVersion {
|
||||||
platform: "macosx",
|
platform: "macosx",
|
||||||
tag: "macosx_11_x_universal2".to_string()
|
tag: "macosx_11_x_universal2".to_string()
|
||||||
|
|
@ -830,7 +824,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("macosx_11_0_invalid"),
|
PlatformTag::from_str("macosx_11_0_invalid"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "macosx",
|
platform: "macosx",
|
||||||
tag: "macosx_11_0_invalid".to_string()
|
tag: "macosx_11_0_invalid".to_string()
|
||||||
|
|
@ -840,19 +834,25 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn win32_platform() {
|
fn win32_platform() {
|
||||||
assert_eq!(PlatformTag::parse("win32"), Ok(PlatformTag::Win32));
|
assert_eq!(PlatformTag::from_str("win32"), Ok(PlatformTag::Win32));
|
||||||
assert_eq!(PlatformTag::Win32.to_string(), "win32");
|
assert_eq!(PlatformTag::Win32.to_string(), "win32");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn win_amd64_platform() {
|
fn win_amd64_platform() {
|
||||||
assert_eq!(PlatformTag::parse("win_amd64"), Ok(PlatformTag::WinAmd64));
|
assert_eq!(
|
||||||
|
PlatformTag::from_str("win_amd64"),
|
||||||
|
Ok(PlatformTag::WinAmd64)
|
||||||
|
);
|
||||||
assert_eq!(PlatformTag::WinAmd64.to_string(), "win_amd64");
|
assert_eq!(PlatformTag::WinAmd64.to_string(), "win_amd64");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn win_arm64_platform() {
|
fn win_arm64_platform() {
|
||||||
assert_eq!(PlatformTag::parse("win_arm64"), Ok(PlatformTag::WinArm64));
|
assert_eq!(
|
||||||
|
PlatformTag::from_str("win_arm64"),
|
||||||
|
Ok(PlatformTag::WinArm64)
|
||||||
|
);
|
||||||
assert_eq!(PlatformTag::WinArm64.to_string(), "win_arm64");
|
assert_eq!(PlatformTag::WinArm64.to_string(), "win_arm64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -862,7 +862,7 @@ mod tests {
|
||||||
release_arch: "13_14_x86_64".into(),
|
release_arch: "13_14_x86_64".into(),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("freebsd_13_14_x86_64").as_ref(),
|
PlatformTag::from_str("freebsd_13_14_x86_64").as_ref(),
|
||||||
Ok(&tag)
|
Ok(&tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "freebsd_13_14_x86_64");
|
assert_eq!(tag.to_string(), "freebsd_13_14_x86_64");
|
||||||
|
|
@ -873,7 +873,10 @@ mod tests {
|
||||||
let tag = PlatformTag::Illumos {
|
let tag = PlatformTag::Illumos {
|
||||||
release_arch: "5_11_x86_64".into(),
|
release_arch: "5_11_x86_64".into(),
|
||||||
};
|
};
|
||||||
assert_eq!(PlatformTag::parse("illumos_5_11_x86_64").as_ref(), Ok(&tag));
|
assert_eq!(
|
||||||
|
PlatformTag::from_str("illumos_5_11_x86_64").as_ref(),
|
||||||
|
Ok(&tag)
|
||||||
|
);
|
||||||
assert_eq!(tag.to_string(), "illumos_5_11_x86_64");
|
assert_eq!(tag.to_string(), "illumos_5_11_x86_64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -883,13 +886,13 @@ mod tests {
|
||||||
release_arch: "11_4_x86_64".into(),
|
release_arch: "11_4_x86_64".into(),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("solaris_11_4_x86_64_64bit").as_ref(),
|
PlatformTag::from_str("solaris_11_4_x86_64_64bit").as_ref(),
|
||||||
Ok(&tag)
|
Ok(&tag)
|
||||||
);
|
);
|
||||||
assert_eq!(tag.to_string(), "solaris_11_4_x86_64_64bit");
|
assert_eq!(tag.to_string(), "solaris_11_4_x86_64_64bit");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("solaris_11_4_x86_64"),
|
PlatformTag::from_str("solaris_11_4_x86_64"),
|
||||||
Err(ParsePlatformTagError::InvalidArch {
|
Err(ParsePlatformTagError::InvalidArch {
|
||||||
platform: "solaris",
|
platform: "solaris",
|
||||||
tag: "solaris_11_4_x86_64".to_string()
|
tag: "solaris_11_4_x86_64".to_string()
|
||||||
|
|
@ -900,11 +903,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_platform() {
|
fn unknown_platform() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse("unknown"),
|
PlatformTag::from_str("unknown"),
|
||||||
Err(ParsePlatformTagError::UnknownFormat("unknown".to_string()))
|
Err(ParsePlatformTagError::UnknownFormat("unknown".to_string()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PlatformTag::parse(""),
|
PlatformTag::from_str(""),
|
||||||
Err(ParsePlatformTagError::UnknownFormat(String::new()))
|
Err(ParsePlatformTagError::UnknownFormat(String::new()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -298,13 +298,13 @@ impl Tags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the highest-priority Python tag for the [`Tags`].
|
/// Return the highest-priority Python tag for the [`Tags`].
|
||||||
pub fn python_tag(&self) -> Option<&LanguageTag> {
|
pub fn python_tag(&self) -> Option<LanguageTag> {
|
||||||
self.best.as_ref().map(|(python, _, _)| python)
|
self.best.as_ref().map(|(python, _, _)| *python)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the highest-priority ABI tag for the [`Tags`].
|
/// Return the highest-priority ABI tag for the [`Tags`].
|
||||||
pub fn abi_tag(&self) -> Option<&AbiTag> {
|
pub fn abi_tag(&self) -> Option<AbiTag> {
|
||||||
self.best.as_ref().map(|(_, abi, _)| abi)
|
self.best.as_ref().map(|(_, abi, _)| *abi)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the highest-priority platform tag for the [`Tags`].
|
/// Return the highest-priority platform tag for the [`Tags`].
|
||||||
|
|
@ -314,10 +314,10 @@ impl Tags {
|
||||||
|
|
||||||
/// Returns `true` if the given language and ABI tags are compatible with the current
|
/// Returns `true` if the given language and ABI tags are compatible with the current
|
||||||
/// environment.
|
/// environment.
|
||||||
pub fn is_compatible_abi(&self, python_tag: &LanguageTag, abi_tag: &AbiTag) -> bool {
|
pub fn is_compatible_abi(&self, python_tag: LanguageTag, abi_tag: AbiTag) -> bool {
|
||||||
self.map
|
self.map
|
||||||
.get(python_tag)
|
.get(&python_tag)
|
||||||
.map(|abis| abis.contains_key(abi_tag))
|
.map(|abis| abis.contains_key(&abi_tag))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -744,12 +744,8 @@ impl PubGrubReportFormatter<'_> {
|
||||||
match tag {
|
match tag {
|
||||||
IncompatibleTag::Invalid => None,
|
IncompatibleTag::Invalid => None,
|
||||||
IncompatibleTag::Python => {
|
IncompatibleTag::Python => {
|
||||||
let best = tags.and_then(Tags::python_tag).cloned();
|
let best = tags.and_then(Tags::python_tag);
|
||||||
let tags = prioritized
|
let tags = prioritized.python_tags();
|
||||||
.python_tags()
|
|
||||||
.into_iter()
|
|
||||||
.cloned()
|
|
||||||
.collect::<BTreeSet<_>>();
|
|
||||||
if tags.is_empty() {
|
if tags.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -762,7 +758,7 @@ impl PubGrubReportFormatter<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IncompatibleTag::Abi | IncompatibleTag::AbiPythonVersion => {
|
IncompatibleTag::Abi | IncompatibleTag::AbiPythonVersion => {
|
||||||
let best = tags.and_then(Tags::abi_tag).cloned();
|
let best = tags.and_then(Tags::abi_tag);
|
||||||
let tags = prioritized
|
let tags = prioritized
|
||||||
.abi_tags()
|
.abi_tags()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -774,8 +770,7 @@ impl PubGrubReportFormatter<'_> {
|
||||||
// In that case, the wheel isn't compatible, but when solving for Python 3.13,
|
// In that case, the wheel isn't compatible, but when solving for Python 3.13,
|
||||||
// the `cp312` Python tag _can_ be compatible (e.g., for `cp312-abi3-macosx_11_0_arm64.whl`),
|
// the `cp312` Python tag _can_ be compatible (e.g., for `cp312-abi3-macosx_11_0_arm64.whl`),
|
||||||
// so this is considered an ABI incompatibility rather than Python incompatibility.
|
// so this is considered an ABI incompatibility rather than Python incompatibility.
|
||||||
.filter(|tag| **tag != AbiTag::None)
|
.filter(|tag| *tag != AbiTag::None)
|
||||||
.cloned()
|
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
if tags.is_empty() {
|
if tags.is_empty() {
|
||||||
None
|
None
|
||||||
|
|
@ -1581,7 +1576,7 @@ impl std::fmt::Display for PubGrubHint {
|
||||||
tags,
|
tags,
|
||||||
best,
|
best,
|
||||||
} => {
|
} => {
|
||||||
if let Some(best) = best.as_ref() {
|
if let Some(best) = best {
|
||||||
let s = if tags.len() == 1 { "" } else { "s" };
|
let s = if tags.len() == 1 { "" } else { "s" };
|
||||||
let best = if let Some(pretty) = best.pretty() {
|
let best = if let Some(pretty) = best.pretty() {
|
||||||
format!("{} (`{}`)", pretty.cyan(), best.cyan())
|
format!("{} (`{}`)", pretty.cyan(), best.cyan())
|
||||||
|
|
@ -1621,7 +1616,7 @@ impl std::fmt::Display for PubGrubHint {
|
||||||
tags,
|
tags,
|
||||||
best,
|
best,
|
||||||
} => {
|
} => {
|
||||||
if let Some(best) = best.as_ref() {
|
if let Some(best) = best {
|
||||||
let s = if tags.len() == 1 { "" } else { "s" };
|
let s = if tags.len() == 1 { "" } else { "s" };
|
||||||
let best = if let Some(pretty) = best.pretty() {
|
let best = if let Some(pretty) = best.pretty() {
|
||||||
format!("{} (`{}`)", pretty.cyan(), best.cyan())
|
format!("{} (`{}`)", pretty.cyan(), best.cyan())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue