mirror of https://github.com/astral-sh/uv
Move WHEEL file parsing into a struct (#15483)
## Summary No functional changes, but I need to add more behavior here for https://github.com/astral-sh/uv/issues/15035, so seems nice to do this separately.
This commit is contained in:
parent
f16760e10a
commit
6d874b1a25
|
|
@ -14,7 +14,7 @@ use uv_pypi_types::{DirectUrl, Metadata10};
|
|||
|
||||
use crate::linker::{LinkMode, Locks};
|
||||
use crate::wheel::{
|
||||
LibKind, dist_info_metadata, find_dist_info, install_data, parse_scripts, parse_wheel_file,
|
||||
LibKind, WheelFile, dist_info_metadata, find_dist_info, install_data, parse_scripts,
|
||||
read_record_file, write_installer_metadata, write_script_entrypoints,
|
||||
};
|
||||
use crate::{Error, Layout};
|
||||
|
|
@ -66,7 +66,7 @@ pub fn install_wheel<Cache: serde::Serialize, Build: serde::Serialize>(
|
|||
.as_ref()
|
||||
.join(format!("{dist_info_prefix}.dist-info/WHEEL"));
|
||||
let wheel_text = fs::read_to_string(wheel_file_path)?;
|
||||
let lib_kind = parse_wheel_file(&wheel_text)?;
|
||||
let lib_kind = WheelFile::parse(&wheel_text)?.lib_kind();
|
||||
|
||||
// > 1.c If Root-Is-Purelib == ‘true’, unpack archive into purelib (site-packages).
|
||||
// > 1.d Else unpack archive into platlib (site-packages).
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use uv_pypi_types::Scheme;
|
|||
pub use install::install_wheel;
|
||||
pub use linker::{LinkMode, Locks};
|
||||
pub use uninstall::{Uninstall, uninstall_egg, uninstall_legacy_editable, uninstall_wheel};
|
||||
pub use wheel::{LibKind, parse_wheel_file, read_record_file};
|
||||
pub use wheel::{LibKind, WheelFile, read_record_file};
|
||||
|
||||
mod install;
|
||||
mod linker;
|
||||
|
|
|
|||
|
|
@ -257,6 +257,76 @@ pub(crate) fn write_script_entrypoints(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// A parsed `WHEEL` file.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct WheelFile(FxHashMap<String, Vec<String>>);
|
||||
|
||||
impl WheelFile {
|
||||
/// Parse `WHEEL` file.
|
||||
///
|
||||
/// > {distribution}-{version}.dist-info/WHEEL is metadata about the archive itself in the same
|
||||
/// > email message format:
|
||||
pub fn parse(wheel_text: &str) -> Result<Self, Error> {
|
||||
// {distribution}-{version}.dist-info/WHEEL is metadata about the archive itself in the same email message format:
|
||||
let data = parse_email_message_file(&mut wheel_text.as_bytes(), "WHEEL")?;
|
||||
|
||||
// mkl_fft-1.3.6-58-cp310-cp310-manylinux2014_x86_64.whl has multiple Wheel-Version entries, we have to ignore that
|
||||
// like pip
|
||||
let wheel_version = data
|
||||
.get("Wheel-Version")
|
||||
.and_then(|wheel_versions| wheel_versions.first());
|
||||
let wheel_version = wheel_version
|
||||
.and_then(|wheel_version| wheel_version.split_once('.'))
|
||||
.ok_or_else(|| {
|
||||
Error::InvalidWheel(format!(
|
||||
"Invalid Wheel-Version in WHEEL file: {wheel_version:?}"
|
||||
))
|
||||
})?;
|
||||
// pip has some test wheels that use that ancient version,
|
||||
// and technically we only need to check that the version is not higher
|
||||
if wheel_version == ("0", "1") {
|
||||
warn!("Ancient wheel version 0.1 (expected is 1.0)");
|
||||
return Ok(Self(data));
|
||||
}
|
||||
// Check that installer is compatible with Wheel-Version. Warn if minor version is greater, abort if major version is greater.
|
||||
// Wheel-Version: 1.0
|
||||
if wheel_version.0 != "1" {
|
||||
return Err(Error::InvalidWheel(format!(
|
||||
"Unsupported wheel major version (expected {}, got {})",
|
||||
1, wheel_version.0
|
||||
)));
|
||||
}
|
||||
if wheel_version.1 > "0" {
|
||||
warn!(
|
||||
"Warning: Unsupported wheel minor version (expected {}, got {})",
|
||||
0, wheel_version.1
|
||||
);
|
||||
}
|
||||
Ok(Self(data))
|
||||
}
|
||||
|
||||
/// Whether the wheel should be installed into the `purelib` or `platlib` directory.
|
||||
pub fn lib_kind(&self) -> LibKind {
|
||||
// Determine whether Root-Is-Purelib == ‘true’.
|
||||
// If it is, the wheel is pure, and should be installed into purelib.
|
||||
let root_is_purelib = self
|
||||
.0
|
||||
.get("Root-Is-Purelib")
|
||||
.and_then(|root_is_purelib| root_is_purelib.first())
|
||||
.is_some_and(|root_is_purelib| root_is_purelib == "true");
|
||||
if root_is_purelib {
|
||||
LibKind::Pure
|
||||
} else {
|
||||
LibKind::Plat
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the list of wheel tags.
|
||||
pub fn tags(&self) -> Option<&[String]> {
|
||||
self.0.get("Tag").map(Vec::as_slice)
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the wheel should be installed into the `purelib` or `platlib` directory.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LibKind {
|
||||
|
|
@ -266,61 +336,6 @@ pub enum LibKind {
|
|||
Plat,
|
||||
}
|
||||
|
||||
/// Parse WHEEL file.
|
||||
///
|
||||
/// > {distribution}-{version}.dist-info/WHEEL is metadata about the archive itself in the same
|
||||
/// > email message format:
|
||||
pub fn parse_wheel_file(wheel_text: &str) -> Result<LibKind, Error> {
|
||||
// {distribution}-{version}.dist-info/WHEEL is metadata about the archive itself in the same email message format:
|
||||
let data = parse_email_message_file(&mut wheel_text.as_bytes(), "WHEEL")?;
|
||||
|
||||
// Determine whether Root-Is-Purelib == ‘true’.
|
||||
// If it is, the wheel is pure, and should be installed into purelib.
|
||||
let root_is_purelib = data
|
||||
.get("Root-Is-Purelib")
|
||||
.and_then(|root_is_purelib| root_is_purelib.first())
|
||||
.is_some_and(|root_is_purelib| root_is_purelib == "true");
|
||||
let lib_kind = if root_is_purelib {
|
||||
LibKind::Pure
|
||||
} else {
|
||||
LibKind::Plat
|
||||
};
|
||||
|
||||
// mkl_fft-1.3.6-58-cp310-cp310-manylinux2014_x86_64.whl has multiple Wheel-Version entries, we have to ignore that
|
||||
// like pip
|
||||
let wheel_version = data
|
||||
.get("Wheel-Version")
|
||||
.and_then(|wheel_versions| wheel_versions.first());
|
||||
let wheel_version = wheel_version
|
||||
.and_then(|wheel_version| wheel_version.split_once('.'))
|
||||
.ok_or_else(|| {
|
||||
Error::InvalidWheel(format!(
|
||||
"Invalid Wheel-Version in WHEEL file: {wheel_version:?}"
|
||||
))
|
||||
})?;
|
||||
// pip has some test wheels that use that ancient version,
|
||||
// and technically we only need to check that the version is not higher
|
||||
if wheel_version == ("0", "1") {
|
||||
warn!("Ancient wheel version 0.1 (expected is 1.0)");
|
||||
return Ok(lib_kind);
|
||||
}
|
||||
// Check that installer is compatible with Wheel-Version. Warn if minor version is greater, abort if major version is greater.
|
||||
// Wheel-Version: 1.0
|
||||
if wheel_version.0 != "1" {
|
||||
return Err(Error::InvalidWheel(format!(
|
||||
"Unsupported wheel major version (expected {}, got {})",
|
||||
1, wheel_version.0
|
||||
)));
|
||||
}
|
||||
if wheel_version.1 > "0" {
|
||||
warn!(
|
||||
"Warning: Unsupported wheel minor version (expected {}, got {})",
|
||||
0, wheel_version.1
|
||||
);
|
||||
}
|
||||
Ok(lib_kind)
|
||||
}
|
||||
|
||||
/// Moves the files and folders in src to dest, updating the RECORD in the process
|
||||
pub(crate) fn move_folder_recorded(
|
||||
src_dir: &Path,
|
||||
|
|
@ -938,7 +953,7 @@ mod test {
|
|||
use crate::wheel::format_shebang;
|
||||
|
||||
use super::{
|
||||
RecordEntry, Script, get_script_executable, parse_email_message_file, parse_wheel_file,
|
||||
RecordEntry, Script, WheelFile, get_script_executable, parse_email_message_file,
|
||||
read_record_file, write_installer_metadata,
|
||||
};
|
||||
|
||||
|
|
@ -1013,8 +1028,8 @@ mod test {
|
|||
version
|
||||
}
|
||||
}
|
||||
parse_wheel_file(&wheel_with_version("1.0")).unwrap();
|
||||
parse_wheel_file(&wheel_with_version("2.0")).unwrap_err();
|
||||
WheelFile::parse(&wheel_with_version("1.0")).unwrap();
|
||||
WheelFile::parse(&wheel_with_version("2.0")).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Reference in New Issue