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:
Charlie Marsh 2025-08-24 11:53:12 -04:00 committed by GitHub
parent f16760e10a
commit 6d874b1a25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 76 additions and 61 deletions

View File

@ -14,7 +14,7 @@ use uv_pypi_types::{DirectUrl, Metadata10};
use crate::linker::{LinkMode, Locks}; use crate::linker::{LinkMode, Locks};
use crate::wheel::{ 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, read_record_file, write_installer_metadata, write_script_entrypoints,
}; };
use crate::{Error, Layout}; use crate::{Error, Layout};
@ -66,7 +66,7 @@ pub fn install_wheel<Cache: serde::Serialize, Build: serde::Serialize>(
.as_ref() .as_ref()
.join(format!("{dist_info_prefix}.dist-info/WHEEL")); .join(format!("{dist_info_prefix}.dist-info/WHEEL"));
let wheel_text = fs::read_to_string(wheel_file_path)?; 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.c If Root-Is-Purelib == true, unpack archive into purelib (site-packages).
// > 1.d Else unpack archive into platlib (site-packages). // > 1.d Else unpack archive into platlib (site-packages).

View File

@ -13,7 +13,7 @@ use uv_pypi_types::Scheme;
pub use install::install_wheel; pub use install::install_wheel;
pub use linker::{LinkMode, Locks}; pub use linker::{LinkMode, Locks};
pub use uninstall::{Uninstall, uninstall_egg, uninstall_legacy_editable, uninstall_wheel}; 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 install;
mod linker; mod linker;

View File

@ -257,35 +257,19 @@ pub(crate) fn write_script_entrypoints(
Ok(()) Ok(())
} }
/// Whether the wheel should be installed into the `purelib` or `platlib` directory. /// A parsed `WHEEL` file.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum LibKind { pub struct WheelFile(FxHashMap<String, Vec<String>>);
/// Install into the `purelib` directory.
Pure,
/// Install into the `platlib` directory.
Plat,
}
/// Parse WHEEL file. impl WheelFile {
/// Parse `WHEEL` file.
/// ///
/// > {distribution}-{version}.dist-info/WHEEL is metadata about the archive itself in the same /// > {distribution}-{version}.dist-info/WHEEL is metadata about the archive itself in the same
/// > email message format: /// > email message format:
pub fn parse_wheel_file(wheel_text: &str) -> Result<LibKind, Error> { 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: // {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")?; 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 // mkl_fft-1.3.6-58-cp310-cp310-manylinux2014_x86_64.whl has multiple Wheel-Version entries, we have to ignore that
// like pip // like pip
let wheel_version = data let wheel_version = data
@ -302,7 +286,7 @@ pub fn parse_wheel_file(wheel_text: &str) -> Result<LibKind, Error> {
// and technically we only need to check that the version is not higher // and technically we only need to check that the version is not higher
if wheel_version == ("0", "1") { if wheel_version == ("0", "1") {
warn!("Ancient wheel version 0.1 (expected is 1.0)"); warn!("Ancient wheel version 0.1 (expected is 1.0)");
return Ok(lib_kind); return Ok(Self(data));
} }
// Check that installer is compatible with Wheel-Version. Warn if minor version is greater, abort if major version is greater. // Check that installer is compatible with Wheel-Version. Warn if minor version is greater, abort if major version is greater.
// Wheel-Version: 1.0 // Wheel-Version: 1.0
@ -318,7 +302,38 @@ pub fn parse_wheel_file(wheel_text: &str) -> Result<LibKind, Error> {
0, wheel_version.1 0, wheel_version.1
); );
} }
Ok(lib_kind) 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 {
/// Install into the `purelib` directory.
Pure,
/// Install into the `platlib` directory.
Plat,
} }
/// Moves the files and folders in src to dest, updating the RECORD in the process /// Moves the files and folders in src to dest, updating the RECORD in the process
@ -938,7 +953,7 @@ mod test {
use crate::wheel::format_shebang; use crate::wheel::format_shebang;
use super::{ 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, read_record_file, write_installer_metadata,
}; };
@ -1013,8 +1028,8 @@ mod test {
version version
} }
} }
parse_wheel_file(&wheel_with_version("1.0")).unwrap(); WheelFile::parse(&wheel_with_version("1.0")).unwrap();
parse_wheel_file(&wheel_with_version("2.0")).unwrap_err(); WheelFile::parse(&wheel_with_version("2.0")).unwrap_err();
} }
#[test] #[test]