mirror of https://github.com/astral-sh/uv
Reduce `WheelFilename` to 48 bytes (#10583)
## Summary Based on some advice from @konstin.
This commit is contained in:
parent
e3f6b9c5f3
commit
24a5920739
|
|
@ -10,7 +10,7 @@ use tracing::{debug, trace};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
use zip::{CompressionMethod, ZipWriter};
|
use zip::{CompressionMethod, ZipWriter};
|
||||||
|
|
||||||
use uv_distribution_filename::{TagSet, WheelFilename};
|
use uv_distribution_filename::WheelFilename;
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_globfilter::{parse_portable_glob, GlobDirFilter};
|
use uv_globfilter::{parse_portable_glob, GlobDirFilter};
|
||||||
use uv_platform_tags::{AbiTag, LanguageTag, PlatformTag};
|
use uv_platform_tags::{AbiTag, LanguageTag, PlatformTag};
|
||||||
|
|
@ -33,17 +33,16 @@ pub fn build_wheel(
|
||||||
}
|
}
|
||||||
crate::check_metadata_directory(source_tree, metadata_directory, &pyproject_toml)?;
|
crate::check_metadata_directory(source_tree, metadata_directory, &pyproject_toml)?;
|
||||||
|
|
||||||
let filename = WheelFilename {
|
let filename = WheelFilename::new(
|
||||||
name: pyproject_toml.name().clone(),
|
pyproject_toml.name().clone(),
|
||||||
version: pyproject_toml.version().clone(),
|
pyproject_toml.version().clone(),
|
||||||
build_tag: None,
|
LanguageTag::Python {
|
||||||
python_tag: TagSet::from_slice(&[LanguageTag::Python {
|
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
}]),
|
},
|
||||||
abi_tag: TagSet::from_buf([AbiTag::None]),
|
AbiTag::None,
|
||||||
platform_tag: TagSet::from_buf([PlatformTag::Any]),
|
PlatformTag::Any,
|
||||||
};
|
);
|
||||||
|
|
||||||
let wheel_path = wheel_dir.join(filename.to_string());
|
let wheel_path = wheel_dir.join(filename.to_string());
|
||||||
debug!("Writing wheel at {}", wheel_path.user_display());
|
debug!("Writing wheel at {}", wheel_path.user_display());
|
||||||
|
|
@ -71,17 +70,16 @@ pub fn list_wheel(
|
||||||
warn_user_once!("{warning}");
|
warn_user_once!("{warning}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = WheelFilename {
|
let filename = WheelFilename::new(
|
||||||
name: pyproject_toml.name().clone(),
|
pyproject_toml.name().clone(),
|
||||||
version: pyproject_toml.version().clone(),
|
pyproject_toml.version().clone(),
|
||||||
build_tag: None,
|
LanguageTag::Python {
|
||||||
python_tag: TagSet::from_slice(&[LanguageTag::Python {
|
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
}]),
|
},
|
||||||
abi_tag: TagSet::from_buf([AbiTag::None]),
|
AbiTag::None,
|
||||||
platform_tag: TagSet::from_buf([PlatformTag::Any]),
|
PlatformTag::Any,
|
||||||
};
|
);
|
||||||
|
|
||||||
let mut files = FileList::new();
|
let mut files = FileList::new();
|
||||||
let writer = ListWriter::new(&mut files);
|
let writer = ListWriter::new(&mut files);
|
||||||
|
|
@ -253,17 +251,16 @@ pub fn build_editable(
|
||||||
|
|
||||||
crate::check_metadata_directory(source_tree, metadata_directory, &pyproject_toml)?;
|
crate::check_metadata_directory(source_tree, metadata_directory, &pyproject_toml)?;
|
||||||
|
|
||||||
let filename = WheelFilename {
|
let filename = WheelFilename::new(
|
||||||
name: pyproject_toml.name().clone(),
|
pyproject_toml.name().clone(),
|
||||||
version: pyproject_toml.version().clone(),
|
pyproject_toml.version().clone(),
|
||||||
build_tag: None,
|
LanguageTag::Python {
|
||||||
python_tag: TagSet::from_slice(&[LanguageTag::Python {
|
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
}]),
|
},
|
||||||
abi_tag: TagSet::from_buf([AbiTag::None]),
|
AbiTag::None,
|
||||||
platform_tag: TagSet::from_buf([PlatformTag::Any]),
|
PlatformTag::Any,
|
||||||
};
|
);
|
||||||
|
|
||||||
let wheel_path = wheel_dir.join(filename.to_string());
|
let wheel_path = wheel_dir.join(filename.to_string());
|
||||||
debug!("Writing wheel at {}", wheel_path.user_display());
|
debug!("Writing wheel at {}", wheel_path.user_display());
|
||||||
|
|
@ -308,17 +305,16 @@ pub fn metadata(
|
||||||
warn_user_once!("{warning}");
|
warn_user_once!("{warning}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = WheelFilename {
|
let filename = WheelFilename::new(
|
||||||
name: pyproject_toml.name().clone(),
|
pyproject_toml.name().clone(),
|
||||||
version: pyproject_toml.version().clone(),
|
pyproject_toml.version().clone(),
|
||||||
build_tag: None,
|
LanguageTag::Python {
|
||||||
python_tag: TagSet::from_slice(&[LanguageTag::Python {
|
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
}]),
|
},
|
||||||
abi_tag: TagSet::from_buf([AbiTag::None]),
|
AbiTag::None,
|
||||||
platform_tag: TagSet::from_buf([PlatformTag::Any]),
|
PlatformTag::Any,
|
||||||
};
|
);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Writing metadata files to {}",
|
"Writing metadata files to {}",
|
||||||
|
|
@ -568,9 +564,9 @@ fn wheel_info(filename: &WheelFilename, uv_version: &str) -> String {
|
||||||
("Generator", format!("uv {uv_version}")),
|
("Generator", format!("uv {uv_version}")),
|
||||||
("Root-Is-Purelib", "true".to_string()),
|
("Root-Is-Purelib", "true".to_string()),
|
||||||
];
|
];
|
||||||
for python_tag in &filename.python_tag {
|
for python_tag in filename.python_tags() {
|
||||||
for abi_tag in &filename.abi_tag {
|
for abi_tag in filename.abi_tags() {
|
||||||
for platform_tag in &filename.platform_tag {
|
for platform_tag in filename.platform_tags() {
|
||||||
wheel_info.push(("Tag", format!("{python_tag}-{abi_tag}-{platform_tag}")));
|
wheel_info.push(("Tag", format!("{python_tag}-{abi_tag}-{platform_tag}")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -765,29 +761,21 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_wheel() {
|
fn test_wheel() {
|
||||||
let filename = WheelFilename {
|
let filename = WheelFilename::new(
|
||||||
name: PackageName::from_str("foo").unwrap(),
|
PackageName::from_str("foo").unwrap(),
|
||||||
version: Version::from_str("1.2.3").unwrap(),
|
Version::from_str("1.2.3").unwrap(),
|
||||||
build_tag: None,
|
LanguageTag::Python {
|
||||||
python_tag: TagSet::from_slice(&[
|
major: 3,
|
||||||
LanguageTag::Python {
|
minor: None,
|
||||||
major: 2,
|
},
|
||||||
minor: None,
|
AbiTag::None,
|
||||||
},
|
PlatformTag::Any,
|
||||||
LanguageTag::Python {
|
);
|
||||||
major: 3,
|
|
||||||
minor: None,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
abi_tag: TagSet::from_buf([AbiTag::None]),
|
|
||||||
platform_tag: TagSet::from_buf([PlatformTag::Any]),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_snapshot!(wheel_info(&filename, "1.0.0+test"), @r"
|
assert_snapshot!(wheel_info(&filename, "1.0.0+test"), @r"
|
||||||
Wheel-Version: 1.0
|
Wheel-Version: 1.0
|
||||||
Generator: uv 1.0.0+test
|
Generator: uv 1.0.0+test
|
||||||
Root-Is-Purelib: true
|
Root-Is-Purelib: true
|
||||||
Tag: py2-none-any
|
|
||||||
Tag: py3-none-any
|
Tag: py3-none-any
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wheel_filename_size() {
|
fn wheel_filename_size() {
|
||||||
assert_eq!(size_of::<WheelFilename>(), 128);
|
assert_eq!(size_of::<WheelFilename>(), 48);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,23 +8,27 @@ Ok(
|
||||||
"foo",
|
"foo",
|
||||||
),
|
),
|
||||||
version: "1.2.3",
|
version: "1.2.3",
|
||||||
build_tag: Some(
|
tags: Large {
|
||||||
BuildTag(
|
large: WheelTagLarge {
|
||||||
202206090410,
|
build_tag: Some(
|
||||||
None,
|
BuildTag(
|
||||||
),
|
202206090410,
|
||||||
),
|
None,
|
||||||
python_tag: [
|
),
|
||||||
Python {
|
),
|
||||||
major: 3,
|
python_tag: [
|
||||||
minor: None,
|
Python {
|
||||||
|
major: 3,
|
||||||
|
minor: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
abi_tag: [
|
||||||
|
None,
|
||||||
|
],
|
||||||
|
platform_tag: [
|
||||||
|
Any,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
abi_tag: [
|
|
||||||
None,
|
|
||||||
],
|
|
||||||
platform_tag: [
|
|
||||||
Any,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,33 +8,37 @@ Ok(
|
||||||
"foo",
|
"foo",
|
||||||
),
|
),
|
||||||
version: "1.2.3",
|
version: "1.2.3",
|
||||||
build_tag: None,
|
tags: Large {
|
||||||
python_tag: [
|
large: WheelTagLarge {
|
||||||
CPython {
|
build_tag: None,
|
||||||
python_version: (
|
python_tag: [
|
||||||
3,
|
CPython {
|
||||||
11,
|
python_version: (
|
||||||
),
|
3,
|
||||||
|
11,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
abi_tag: [
|
||||||
|
CPython {
|
||||||
|
gil_disabled: false,
|
||||||
|
python_version: (
|
||||||
|
3,
|
||||||
|
11,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
platform_tag: [
|
||||||
|
Manylinux {
|
||||||
|
major: 2,
|
||||||
|
minor: 17,
|
||||||
|
arch: X86_64,
|
||||||
|
},
|
||||||
|
Manylinux2014 {
|
||||||
|
arch: X86_64,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
abi_tag: [
|
|
||||||
CPython {
|
|
||||||
gil_disabled: false,
|
|
||||||
python_version: (
|
|
||||||
3,
|
|
||||||
11,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
platform_tag: [
|
|
||||||
Manylinux {
|
|
||||||
major: 2,
|
|
||||||
minor: 17,
|
|
||||||
arch: X86_64,
|
|
||||||
},
|
|
||||||
Manylinux2014 {
|
|
||||||
arch: X86_64,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,15 @@ Ok(
|
||||||
"foo",
|
"foo",
|
||||||
),
|
),
|
||||||
version: "1.2.3",
|
version: "1.2.3",
|
||||||
build_tag: None,
|
tags: Small {
|
||||||
python_tag: [
|
small: WheelTagSmall {
|
||||||
Python {
|
python_tag: Python {
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
|
},
|
||||||
|
abi_tag: None,
|
||||||
|
platform_tag: Any,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
abi_tag: [
|
|
||||||
None,
|
|
||||||
],
|
|
||||||
platform_tag: [
|
|
||||||
Any,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
@ -14,12 +14,6 @@ use uv_platform_tags::{
|
||||||
|
|
||||||
use crate::{BuildTag, BuildTagError};
|
use crate::{BuildTag, BuildTagError};
|
||||||
|
|
||||||
/// 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; 1]>;
|
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
|
|
@ -36,10 +30,7 @@ pub type TagSet<T> = smallvec::SmallVec<[T; 1]>;
|
||||||
pub struct WheelFilename {
|
pub struct WheelFilename {
|
||||||
pub name: PackageName,
|
pub name: PackageName,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub build_tag: Option<BuildTag>,
|
tags: WheelTag,
|
||||||
pub python_tag: TagSet<LanguageTag>,
|
|
||||||
pub abi_tag: TagSet<AbiTag>,
|
|
||||||
pub platform_tag: TagSet<PlatformTag>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for WheelFilename {
|
impl FromStr for WheelFilename {
|
||||||
|
|
@ -63,20 +54,41 @@ impl Display for WheelFilename {
|
||||||
"{}-{}-{}.whl",
|
"{}-{}-{}.whl",
|
||||||
self.name.as_dist_info_name(),
|
self.name.as_dist_info_name(),
|
||||||
self.version,
|
self.version,
|
||||||
self.get_tag()
|
self.tags,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WheelFilename {
|
impl WheelFilename {
|
||||||
|
/// Create a [`WheelFilename`] from its components.
|
||||||
|
pub fn new(
|
||||||
|
name: PackageName,
|
||||||
|
version: Version,
|
||||||
|
python_tag: LanguageTag,
|
||||||
|
abi_tag: AbiTag,
|
||||||
|
platform_tag: PlatformTag,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
tags: WheelTag::Small {
|
||||||
|
small: WheelTagSmall {
|
||||||
|
python_tag,
|
||||||
|
abi_tag,
|
||||||
|
platform_tag,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the wheel is compatible with the given tags.
|
/// Returns `true` if the wheel is compatible with the given tags.
|
||||||
pub fn is_compatible(&self, compatible_tags: &Tags) -> bool {
|
pub fn is_compatible(&self, compatible_tags: &Tags) -> bool {
|
||||||
compatible_tags.is_compatible(&self.python_tag, &self.abi_tag, &self.platform_tag)
|
compatible_tags.is_compatible(self.python_tags(), self.abi_tags(), self.platform_tags())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`TagCompatibility`] of the wheel with the given tags
|
/// Return the [`TagCompatibility`] of the wheel with the given tags
|
||||||
pub fn compatibility(&self, compatible_tags: &Tags) -> TagCompatibility {
|
pub fn compatibility(&self, compatible_tags: &Tags) -> TagCompatibility {
|
||||||
compatible_tags.compatibility(&self.python_tag, &self.abi_tag, &self.platform_tag)
|
compatible_tags.compatibility(self.python_tags(), self.abi_tags(), self.platform_tags())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The wheel filename without the extension.
|
/// The wheel filename without the extension.
|
||||||
|
|
@ -85,45 +97,47 @@ impl WheelFilename {
|
||||||
"{}-{}-{}",
|
"{}-{}-{}",
|
||||||
self.name.as_dist_info_name(),
|
self.name.as_dist_info_name(),
|
||||||
self.version,
|
self.version,
|
||||||
self.get_tag()
|
self.tags
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the wheel's Python tags.
|
||||||
|
pub fn python_tags(&self) -> &[LanguageTag] {
|
||||||
|
match &self.tags {
|
||||||
|
WheelTag::Small { small } => std::slice::from_ref(&small.python_tag),
|
||||||
|
WheelTag::Large { large } => large.python_tag.as_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the wheel's ABI tags.
|
||||||
|
pub fn abi_tags(&self) -> &[AbiTag] {
|
||||||
|
match &self.tags {
|
||||||
|
WheelTag::Small { small } => std::slice::from_ref(&small.abi_tag),
|
||||||
|
WheelTag::Large { large } => large.abi_tag.as_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the wheel's platform tags.
|
||||||
|
pub fn platform_tags(&self) -> &[PlatformTag] {
|
||||||
|
match &self.tags {
|
||||||
|
WheelTag::Small { small } => std::slice::from_ref(&small.platform_tag),
|
||||||
|
WheelTag::Large { large } => large.platform_tag.as_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the wheel's build tag, if present.
|
||||||
|
pub fn build_tag(&self) -> Option<&BuildTag> {
|
||||||
|
match &self.tags {
|
||||||
|
WheelTag::Small { .. } => None,
|
||||||
|
WheelTag::Large { large } => large.build_tag.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a wheel filename from the stem (e.g., `foo-1.2.3-py3-none-any`).
|
/// Parse a wheel filename from the stem (e.g., `foo-1.2.3-py3-none-any`).
|
||||||
pub fn from_stem(stem: &str) -> Result<Self, WheelFilenameError> {
|
pub fn from_stem(stem: &str) -> Result<Self, WheelFilenameError> {
|
||||||
Self::parse(stem, stem)
|
Self::parse(stem, stem)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the tag for this wheel.
|
|
||||||
fn get_tag(&self) -> String {
|
|
||||||
if let ([python_tag], [abi_tag], [platform_tag]) = (
|
|
||||||
self.python_tag.as_slice(),
|
|
||||||
self.abi_tag.as_slice(),
|
|
||||||
self.platform_tag.as_slice(),
|
|
||||||
) {
|
|
||||||
format!("{python_tag}-{abi_tag}-{platform_tag}",)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"{}-{}-{}",
|
|
||||||
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("."),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a wheel filename from the stem (e.g., `foo-1.2.3-py3-none-any`).
|
/// Parse a wheel filename from the stem (e.g., `foo-1.2.3-py3-none-any`).
|
||||||
///
|
///
|
||||||
/// The originating `filename` is used for high-fidelity error messages.
|
/// The originating `filename` is used for high-fidelity error messages.
|
||||||
|
|
@ -202,25 +216,58 @@ impl WheelFilename {
|
||||||
.map_err(|err| WheelFilenameError::InvalidBuildTag(filename.to_string(), err))
|
.map_err(|err| WheelFilenameError::InvalidBuildTag(filename.to_string(), err))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
|
let tags = if build_tag.is_some()
|
||||||
|
|| python_tag.contains('.')
|
||||||
|
|| abi_tag.contains('.')
|
||||||
|
|| platform_tag.contains('.')
|
||||||
|
{
|
||||||
|
WheelTag::Large {
|
||||||
|
large: Box::new(WheelTagLarge {
|
||||||
|
build_tag,
|
||||||
|
python_tag: python_tag
|
||||||
|
.split('.')
|
||||||
|
.map(LanguageTag::from_str)
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.map_err(|err| {
|
||||||
|
WheelFilenameError::InvalidLanguageTag(filename.to_string(), err)
|
||||||
|
})?,
|
||||||
|
abi_tag: abi_tag
|
||||||
|
.split('.')
|
||||||
|
.map(AbiTag::from_str)
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.map_err(|err| {
|
||||||
|
WheelFilenameError::InvalidAbiTag(filename.to_string(), err)
|
||||||
|
})?,
|
||||||
|
platform_tag: platform_tag
|
||||||
|
.split('.')
|
||||||
|
.map(PlatformTag::from_str)
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.map_err(|err| {
|
||||||
|
WheelFilenameError::InvalidPlatformTag(filename.to_string(), err)
|
||||||
|
})?,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
} 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 {
|
||||||
name,
|
name,
|
||||||
version,
|
version,
|
||||||
build_tag,
|
tags,
|
||||||
python_tag: python_tag
|
|
||||||
.split('.')
|
|
||||||
.map(LanguageTag::from_str)
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.map_err(|err| WheelFilenameError::InvalidLanguageTag(filename.to_string(), err))?,
|
|
||||||
abi_tag: abi_tag
|
|
||||||
.split('.')
|
|
||||||
.map(AbiTag::from_str)
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.map_err(|err| WheelFilenameError::InvalidAbiTag(filename.to_string(), err))?,
|
|
||||||
platform_tag: platform_tag
|
|
||||||
.split('.')
|
|
||||||
.map(PlatformTag::from_str)
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.map_err(|err| WheelFilenameError::InvalidPlatformTag(filename.to_string(), err))?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,6 +314,121 @@ 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 {
|
||||||
|
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}")]
|
||||||
|
|
@ -283,6 +445,12 @@ pub enum WheelFilenameError {
|
||||||
InvalidAbiTag(String, ParseAbiTagError),
|
InvalidAbiTag(String, ParseAbiTagError),
|
||||||
#[error("The wheel filename \"{0}\" has an invalid platform tag: {1}")]
|
#[error("The wheel filename \"{0}\" has an invalid platform tag: {1}")]
|
||||||
InvalidPlatformTag(String, ParsePlatformTagError),
|
InvalidPlatformTag(String, ParsePlatformTagError),
|
||||||
|
#[error("The wheel filename \"{0}\" is missing a language tag")]
|
||||||
|
MissingLanguageTag(String),
|
||||||
|
#[error("The wheel filename \"{0}\" is missing an ABI tag")]
|
||||||
|
MissingAbiTag(String),
|
||||||
|
#[error("The wheel filename \"{0}\" is missing a platform tag")]
|
||||||
|
MissingPlatformTag(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -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>() <= 272, "{}", size_of::<Dist>());
|
assert!(size_of::<Dist>() <= 248, "{}", size_of::<Dist>());
|
||||||
assert!(size_of::<BuiltDist>() <= 272, "{}", size_of::<BuiltDist>());
|
assert!(size_of::<BuiltDist>() <= 216, "{}", size_of::<BuiltDist>());
|
||||||
assert!(
|
assert!(
|
||||||
size_of::<SourceDist>() <= 248,
|
size_of::<SourceDist>() <= 248,
|
||||||
"{}",
|
"{}",
|
||||||
|
|
|
||||||
|
|
@ -527,7 +527,7 @@ impl PrioritizedDist {
|
||||||
self.0
|
self.0
|
||||||
.wheels
|
.wheels
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(wheel, _)| wheel.filename.python_tag.iter().copied())
|
.flat_map(|(wheel, _)| wheel.filename.python_tags().iter().copied())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -536,7 +536,7 @@ impl PrioritizedDist {
|
||||||
self.0
|
self.0
|
||||||
.wheels
|
.wheels
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(wheel, _)| wheel.filename.abi_tag.iter().copied())
|
.flat_map(|(wheel, _)| wheel.filename.abi_tags().iter().copied())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -545,10 +545,10 @@ impl PrioritizedDist {
|
||||||
pub fn platform_tags<'a>(&'a self, tags: &'a Tags) -> BTreeSet<&'a PlatformTag> {
|
pub fn platform_tags<'a>(&'a self, tags: &'a Tags) -> BTreeSet<&'a PlatformTag> {
|
||||||
let mut candidates = BTreeSet::new();
|
let mut candidates = BTreeSet::new();
|
||||||
for (wheel, _) in &self.0.wheels {
|
for (wheel, _) in &self.0.wheels {
|
||||||
for wheel_py in &wheel.filename.python_tag {
|
for wheel_py in wheel.filename.python_tags() {
|
||||||
for wheel_abi in &wheel.filename.abi_tag {
|
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_tag.iter());
|
candidates.extend(wheel.filename.platform_tags().iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -724,7 +724,7 @@ impl IncompatibleWheel {
|
||||||
/// supported platforms (rather than generating the supported tags from a given platform).
|
/// supported platforms (rather than generating the supported tags from a given platform).
|
||||||
pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
|
pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
|
||||||
let mut marker = MarkerTree::FALSE;
|
let mut marker = MarkerTree::FALSE;
|
||||||
for platform_tag in &filename.platform_tag {
|
for platform_tag in filename.platform_tags() {
|
||||||
match platform_tag {
|
match platform_tag {
|
||||||
PlatformTag::Any => {
|
PlatformTag::Any => {
|
||||||
return MarkerTree::TRUE;
|
return MarkerTree::TRUE;
|
||||||
|
|
|
||||||
|
|
@ -674,7 +674,7 @@ async fn form_metadata(
|
||||||
];
|
];
|
||||||
|
|
||||||
if let DistFilename::WheelFilename(wheel) = filename {
|
if let DistFilename::WheelFilename(wheel) = filename {
|
||||||
form_metadata.push(("pyversion", wheel.python_tag.iter().join(".")));
|
form_metadata.push(("pyversion", wheel.python_tags().iter().join(".")));
|
||||||
} else {
|
} else {
|
||||||
form_metadata.push(("pyversion", "source".to_string()));
|
form_metadata.push(("pyversion", "source".to_string()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ impl FlatIndex {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Break ties with the build tag.
|
// Break ties with the build tag.
|
||||||
let build_tag = filename.build_tag.clone();
|
let build_tag = filename.build_tag().cloned();
|
||||||
|
|
||||||
WheelCompatibility::Compatible(hash, priority, build_tag)
|
WheelCompatibility::Compatible(hash, priority, build_tag)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ impl Lock {
|
||||||
// `(A ∩ (B ∩ C) = ∅) => ((A ∩ B = ∅) or (A ∩ C = ∅))`
|
// `(A ∩ (B ∩ C) = ∅) => ((A ∩ B = ∅) or (A ∩ C = ∅))`
|
||||||
// a single disjointness check with the intersection is sufficient, so we have one
|
// a single disjointness check with the intersection is sufficient, so we have one
|
||||||
// constant per platform.
|
// constant per platform.
|
||||||
let platform_tags = &wheel.filename.platform_tag;
|
let platform_tags = wheel.filename.platform_tags();
|
||||||
if platform_tags
|
if platform_tags
|
||||||
.iter()
|
.iter()
|
||||||
.all(uv_platform_tags::PlatformTag::is_linux)
|
.all(uv_platform_tags::PlatformTag::is_linux)
|
||||||
|
|
@ -2275,7 +2275,7 @@ impl Package {
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let build_tag = wheel.filename.build_tag.as_ref();
|
let build_tag = wheel.filename.build_tag();
|
||||||
let wheel_priority = (tag_priority, build_tag);
|
let wheel_priority = (tag_priority, build_tag);
|
||||||
match best {
|
match best {
|
||||||
None => {
|
None => {
|
||||||
|
|
|
||||||
|
|
@ -66,19 +66,16 @@ Ok(
|
||||||
"anyio",
|
"anyio",
|
||||||
),
|
),
|
||||||
version: "4.3.0",
|
version: "4.3.0",
|
||||||
build_tag: None,
|
tags: Small {
|
||||||
python_tag: [
|
small: WheelTagSmall {
|
||||||
Python {
|
python_tag: Python {
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
|
},
|
||||||
|
abi_tag: None,
|
||||||
|
platform_tag: Any,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
abi_tag: [
|
|
||||||
None,
|
|
||||||
],
|
|
||||||
platform_tag: [
|
|
||||||
Any,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -73,19 +73,16 @@ Ok(
|
||||||
"anyio",
|
"anyio",
|
||||||
),
|
),
|
||||||
version: "4.3.0",
|
version: "4.3.0",
|
||||||
build_tag: None,
|
tags: Small {
|
||||||
python_tag: [
|
small: WheelTagSmall {
|
||||||
Python {
|
python_tag: Python {
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
|
},
|
||||||
|
abi_tag: None,
|
||||||
|
platform_tag: Any,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
abi_tag: [
|
|
||||||
None,
|
|
||||||
],
|
|
||||||
platform_tag: [
|
|
||||||
Any,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -69,19 +69,16 @@ Ok(
|
||||||
"anyio",
|
"anyio",
|
||||||
),
|
),
|
||||||
version: "4.3.0",
|
version: "4.3.0",
|
||||||
build_tag: None,
|
tags: Small {
|
||||||
python_tag: [
|
small: WheelTagSmall {
|
||||||
Python {
|
python_tag: Python {
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: None,
|
minor: None,
|
||||||
|
},
|
||||||
|
abi_tag: None,
|
||||||
|
platform_tag: Any,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
abi_tag: [
|
|
||||||
None,
|
|
||||||
],
|
|
||||||
platform_tag: [
|
|
||||||
Any,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -380,12 +380,12 @@ impl RequiresPython {
|
||||||
/// It is meant to filter out clearly unusable wheels with perfect specificity and acceptable
|
/// It is meant to filter out clearly unusable wheels with perfect specificity and acceptable
|
||||||
/// sensitivity, we return `true` if the tags are unknown.
|
/// sensitivity, we return `true` if the tags are unknown.
|
||||||
pub fn matches_wheel_tag(&self, wheel: &WheelFilename) -> bool {
|
pub fn matches_wheel_tag(&self, wheel: &WheelFilename) -> bool {
|
||||||
wheel.abi_tag.iter().any(|abi_tag| {
|
wheel.abi_tags().iter().any(|abi_tag| {
|
||||||
if *abi_tag == AbiTag::Abi3 {
|
if *abi_tag == AbiTag::Abi3 {
|
||||||
// Universal tags are allowed.
|
// Universal tags are allowed.
|
||||||
true
|
true
|
||||||
} else if *abi_tag == AbiTag::None {
|
} else if *abi_tag == AbiTag::None {
|
||||||
wheel.python_tag.iter().any(|python_tag| {
|
wheel.python_tags().iter().any(|python_tag| {
|
||||||
// Remove `py2-none-any` and `py27-none-any` and analogous `cp` and `pp` tags.
|
// Remove `py2-none-any` and `py27-none-any` and analogous `cp` and `pp` tags.
|
||||||
if matches!(
|
if matches!(
|
||||||
python_tag,
|
python_tag,
|
||||||
|
|
|
||||||
|
|
@ -555,7 +555,7 @@ impl VersionMapLazy {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Break ties with the build tag.
|
// Break ties with the build tag.
|
||||||
let build_tag = filename.build_tag.clone();
|
let build_tag = filename.build_tag().cloned();
|
||||||
|
|
||||||
WheelCompatibility::Compatible(hash, priority, build_tag)
|
WheelCompatibility::Compatible(hash, priority, build_tag)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue