Use a dedicated error type for `puffin-distribution` (#466)

This commit is contained in:
Charlie Marsh 2023-11-20 03:38:27 -08:00 committed by GitHub
parent 342fc628f0
commit 8decb29bad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 52 additions and 22 deletions

1
Cargo.lock generated
View File

@ -2541,6 +2541,7 @@ dependencies = [
"pypi-types", "pypi-types",
"rayon", "rayon",
"tempfile", "tempfile",
"thiserror",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing", "tracing",

View File

@ -65,6 +65,10 @@ pub enum Error {
Pep440, Pep440,
#[error("Invalid direct_url.json")] #[error("Invalid direct_url.json")]
DirectUrlJson(#[from] serde_json::Error), DirectUrlJson(#[from] serde_json::Error),
#[error("No .dist-info directory found")]
MissingDistInfo,
#[error("Multiple .dist-info directories found: {0}")]
MultipleDistInfo(String),
} }
impl Error { impl Error {
@ -84,7 +88,7 @@ impl Error {
pub fn find_dist_info<'a, T: Copy>( pub fn find_dist_info<'a, T: Copy>(
filename: &WheelFilename, filename: &WheelFilename,
files: impl Iterator<Item = (T, &'a str)>, files: impl Iterator<Item = (T, &'a str)>,
) -> Result<(T, &'a str), String> { ) -> Result<(T, &'a str), Error> {
let metadatas: Vec<_> = files let metadatas: Vec<_> = files
.filter_map(|(payload, path)| { .filter_map(|(payload, path)| {
let (dist_info_dir, file) = path.split_once('/')?; let (dist_info_dir, file) = path.split_once('/')?;
@ -102,17 +106,16 @@ pub fn find_dist_info<'a, T: Copy>(
.collect(); .collect();
let (payload, dist_info_dir) = match metadatas[..] { let (payload, dist_info_dir) = match metadatas[..] {
[] => { [] => {
return Err("no .dist-info directory".to_string()); return Err(Error::MissingDistInfo);
} }
[(payload, path)] => (payload, path), [(payload, path)] => (payload, path),
_ => { _ => {
return Err(format!( return Err(Error::MultipleDistInfo(
"multiple .dist-info directories: {}",
metadatas metadatas
.into_iter() .into_iter()
.map(|(_, dist_info_dir)| dist_info_dir.to_string()) .map(|(_, dist_info_dir)| dist_info_dir.to_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", "),
)); ));
} }
}; };

View File

@ -930,8 +930,7 @@ pub fn install_wheel(
ZipArchive::new(reader).map_err(|err| Error::from_zip_error("(index)".to_string(), err))?; ZipArchive::new(reader).map_err(|err| Error::from_zip_error("(index)".to_string(), err))?;
debug!(name = name.as_ref(), "Getting wheel metadata"); debug!(name = name.as_ref(), "Getting wheel metadata");
let dist_info_prefix = find_dist_info(filename, archive.file_names().map(|name| (name, name))) let dist_info_prefix = find_dist_info(filename, archive.file_names().map(|name| (name, name)))?
.map_err(Error::InvalidWheel)?
.1 .1
.to_string(); .to_string();
let (name, _version) = read_metadata(&dist_info_prefix, &mut archive)?; let (name, _version) = read_metadata(&dist_info_prefix, &mut archive)?;

View File

@ -301,8 +301,7 @@ impl RegistryClient {
.iter() .iter()
.enumerate() .enumerate()
.filter_map(|(idx, e)| Some((idx, e.entry().filename().as_str().ok()?))), .filter_map(|(idx, e)| Some((idx, e.entry().filename().as_str().ok()?))),
) )?;
.map_err(|err| Error::InvalidDistInfo(filename.clone(), err))?;
// Read the contents of the METADATA file // Read the contents of the METADATA file
let mut contents = Vec::new(); let mut contents = Vec::new();

View File

@ -13,6 +13,9 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
UrlParseError(#[from] url::ParseError), UrlParseError(#[from] url::ParseError),
#[error(transparent)]
InstallWheel(#[from] install_wheel_rs::Error),
#[error("{0} isn't available locally, but making network requests to registries was banned.")] #[error("{0} isn't available locally, but making network requests to registries was banned.")]
NoIndex(String), NoIndex(String),

View File

@ -76,8 +76,7 @@ pub(crate) async fn wheel_metadata_from_remote_zip(
.iter() .iter()
.enumerate() .enumerate()
.filter_map(|(idx, e)| Some(((idx, e), e.entry().filename().as_str().ok()?))), .filter_map(|(idx, e)| Some(((idx, e), e.entry().filename().as_str().ok()?))),
) )?;
.map_err(|err| Error::InvalidDistInfo(filename.clone(), err))?;
let offset = metadata_entry.header_offset(); let offset = metadata_entry.header_offset();
let size = metadata_entry.entry().compressed_size() let size = metadata_entry.entry().compressed_size()

View File

@ -24,6 +24,7 @@ bytesize = { workspace = true }
fs-err = { workspace = true } fs-err = { workspace = true }
rayon = { workspace = true } rayon = { workspace = true }
tempfile = { workspace = true } tempfile = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tokio-util = { workspace = true, features = ["compat"] } tokio-util = { workspace = true, features = ["compat"] }
tracing = { workspace = true } tracing = { workspace = true }

View File

@ -1,7 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use anyhow::{format_err, Context, Result}; use anyhow::{Context, Result};
use tempfile::TempDir; use tempfile::TempDir;
use zip::ZipArchive; use zip::ZipArchive;
@ -10,6 +10,8 @@ use distribution_types::{Dist, RemoteSource};
use install_wheel_rs::find_dist_info; use install_wheel_rs::find_dist_info;
use pypi_types::Metadata21; use pypi_types::Metadata21;
use crate::error::Error;
/// A downloaded wheel that's stored in-memory. /// A downloaded wheel that's stored in-memory.
#[derive(Debug)] #[derive(Debug)]
pub struct InMemoryWheel { pub struct InMemoryWheel {
@ -86,12 +88,14 @@ impl std::fmt::Display for SourceDistDownload {
impl InMemoryWheel { impl InMemoryWheel {
/// Read the [`Metadata21`] from a wheel. /// Read the [`Metadata21`] from a wheel.
pub fn read_dist_info(&self) -> Result<Metadata21> { pub fn read_dist_info(&self) -> Result<Metadata21, Error> {
let mut archive = ZipArchive::new(std::io::Cursor::new(&self.buffer))?; let mut archive = ZipArchive::new(std::io::Cursor::new(&self.buffer))?;
let filename = self.filename()?; let filename = self
.filename()
.map_err(|err| Error::FilenameParse(self.dist.to_string(), err))?;
let dist_info_dir = let dist_info_dir =
find_dist_info(&filename, archive.file_names().map(|name| (name, name))) find_dist_info(&filename, archive.file_names().map(|name| (name, name)))
.map_err(|err| format_err!("Invalid wheel {filename}: {err}"))? .map_err(|err| Error::DistInfo(self.dist.to_string(), err))?
.1; .1;
let dist_info = let dist_info =
std::io::read_to_string(archive.by_name(&format!("{dist_info_dir}/METADATA"))?)?; std::io::read_to_string(archive.by_name(&format!("{dist_info_dir}/METADATA"))?)?;
@ -101,12 +105,14 @@ impl InMemoryWheel {
impl DiskWheel { impl DiskWheel {
/// Read the [`Metadata21`] from a wheel. /// Read the [`Metadata21`] from a wheel.
pub fn read_dist_info(&self) -> Result<Metadata21> { pub fn read_dist_info(&self) -> Result<Metadata21, Error> {
let mut archive = ZipArchive::new(fs_err::File::open(&self.path)?)?; let mut archive = ZipArchive::new(fs_err::File::open(&self.path)?)?;
let filename = self.filename()?; let filename = self
.filename()
.map_err(|err| Error::FilenameParse(self.dist.to_string(), err))?;
let dist_info_dir = let dist_info_dir =
find_dist_info(&filename, archive.file_names().map(|name| (name, name))) find_dist_info(&filename, archive.file_names().map(|name| (name, name)))
.map_err(|err| format_err!("Invalid wheel {filename}: {err}"))? .map_err(|err| Error::DistInfo(self.dist.to_string(), err))?
.1; .1;
let dist_info = let dist_info =
std::io::read_to_string(archive.by_name(&format!("{dist_info_dir}/METADATA"))?)?; std::io::read_to_string(archive.by_name(&format!("{dist_info_dir}/METADATA"))?)?;
@ -116,7 +122,7 @@ impl DiskWheel {
impl WheelDownload { impl WheelDownload {
/// Read the [`Metadata21`] from a wheel. /// Read the [`Metadata21`] from a wheel.
pub fn read_dist_info(&self) -> Result<Metadata21> { pub fn read_dist_info(&self) -> Result<Metadata21, Error> {
match self { match self {
WheelDownload::InMemory(wheel) => wheel.read_dist_info(), WheelDownload::InMemory(wheel) => wheel.read_dist_info(),
WheelDownload::Disk(wheel) => wheel.read_dist_info(), WheelDownload::Disk(wheel) => wheel.read_dist_info(),

View File

@ -0,0 +1,13 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
IO(#[from] std::io::Error),
#[error(transparent)]
PypiTypes(#[from] pypi_types::Error),
#[error(transparent)]
Zip(#[from] zip::result::ZipError),
#[error("Unable to read .dist-info directory for: {0}")]
DistInfo(String, #[source] install_wheel_rs::Error),
#[error("Unable to parse wheel filename for: {0}")]
FilenameParse(String, #[source] anyhow::Error),
}

View File

@ -18,6 +18,7 @@ use puffin_git::{GitSource, GitUrl};
use puffin_traits::BuildContext; use puffin_traits::BuildContext;
use pypi_types::Metadata21; use pypi_types::Metadata21;
use crate::error::Error;
use crate::reporter::Facade; use crate::reporter::Facade;
use crate::{DiskWheel, Download, InMemoryWheel, Reporter, SourceDistDownload, WheelDownload}; use crate::{DiskWheel, Download, InMemoryWheel, Reporter, SourceDistDownload, WheelDownload};
@ -52,7 +53,7 @@ impl<'a> Fetcher<'a> {
} }
/// Return the [`Metadata21`] for a distribution, if it exists in the cache. /// Return the [`Metadata21`] for a distribution, if it exists in the cache.
pub fn find_metadata(&self, dist: &Dist, tags: &Tags) -> Result<Option<Metadata21>> { pub fn find_metadata(&self, dist: &Dist, tags: &Tags) -> Result<Option<Metadata21>, Error> {
self.find_in_cache(dist, tags) self.find_in_cache(dist, tags)
.map(|wheel| wheel.read_dist_info()) .map(|wheel| wheel.read_dist_info())
.transpose() .transpose()
@ -77,10 +78,14 @@ impl<'a> Fetcher<'a> {
// Fetch the distribution, then read the metadata (for built distributions), or build // Fetch the distribution, then read the metadata (for built distributions), or build
// the distribution and _then_ read the metadata (for source distributions). // the distribution and _then_ read the metadata (for source distributions).
dist => match self.fetch_dist(dist, client).await? { dist => match self.fetch_dist(dist, client).await? {
Download::Wheel(wheel) => wheel.read_dist_info(), Download::Wheel(wheel) => {
let metadata = wheel.read_dist_info()?;
Ok(metadata)
}
Download::SourceDist(sdist) => { Download::SourceDist(sdist) => {
let wheel = self.build_sdist(sdist, build_context).await?; let wheel = self.build_sdist(sdist, build_context).await?;
wheel.read_dist_info() let metadata = wheel.read_dist_info()?;
Ok(metadata)
} }
}, },
} }

View File

@ -4,6 +4,7 @@ pub use crate::reporter::Reporter;
pub use crate::unzip::Unzip; pub use crate::unzip::Unzip;
mod download; mod download;
mod error;
mod fetcher; mod fetcher;
mod reporter; mod reporter;
mod unzip; mod unzip;