Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 44 additions & 50 deletions crates/uv-client/src/registry_client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::BTreeMap;
use std::fmt::{self, Debug, Formatter};
use std::path::PathBuf;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;

Expand All @@ -26,7 +27,7 @@ use uv_distribution_types::{
};
use uv_extract::hash::Hasher;
use uv_git::{GIT_LFS, GitError, GitHttpSettings, GitResolver, Reporter};
use uv_metadata::{read_metadata_async_seek, read_metadata_async_stream};
use uv_metadata::{read_archive_metadata, read_metadata_async_stream};
use uv_normalize::PackageName;
use uv_pep440::{Version, VersionSpecifiers};
use uv_pep508::MarkerEnvironment;
Expand Down Expand Up @@ -903,22 +904,8 @@ impl RegistryClient {

match location {
WheelLocation::Path(path) => {
let file = fs_err::tokio::File::open(&path)
.await
.map_err(ErrorKind::Io)?;
let reader = tokio::io::BufReader::new(file);
let contents = read_metadata_async_seek(&wheel.filename, reader)
.await
.map_err(|err| {
ErrorKind::Metadata(path.to_string_lossy().to_string(), err)
})?;
ResolutionMetadata::parse_metadata(&contents).map_err(|err| {
ErrorKind::MetadataParseError(
wheel.filename.clone(),
built_dist.to_string(),
Box::new(err),
)
})?
Self::wheel_metadata_local(&path, &path, &wheel.filename, built_dist)
.await?
}
WheelLocation::Url(url) => {
self.wheel_metadata_registry(wheel, &url, capabilities)
Expand All @@ -937,22 +924,13 @@ impl RegistryClient {
.await?
}
BuiltDist::Path(wheel) => {
let file = fs_err::tokio::File::open(wheel.install_path.as_ref())
.await
.map_err(ErrorKind::Io)?;
let reader = tokio::io::BufReader::new(file);
let contents = read_metadata_async_seek(&wheel.filename, reader)
.await
.map_err(|err| {
ErrorKind::Metadata(wheel.install_path.to_string_lossy().to_string(), err)
})?;
ResolutionMetadata::parse_metadata(&contents).map_err(|err| {
ErrorKind::MetadataParseError(
wheel.filename.clone(),
built_dist.to_string(),
Box::new(err),
)
})?
Self::wheel_metadata_local(
&wheel.install_path,
&wheel.install_path,
&wheel.filename,
built_dist,
)
.await?
}
BuiltDist::GitPath(wheel) => {
// Fetch the Git repository.
Expand Down Expand Up @@ -982,22 +960,13 @@ impl RegistryClient {
}

// Read the metadata.
let file = fs_err::tokio::File::open(fetch.path().join(&wheel.install_path))
.await
.map_err(ErrorKind::Io)?;
let reader = tokio::io::BufReader::new(file);
let contents = read_metadata_async_seek(&wheel.filename, reader)
.await
.map_err(|err| {
ErrorKind::Metadata(wheel.install_path.to_string_lossy().to_string(), err)
})?;
ResolutionMetadata::parse_metadata(&contents).map_err(|err| {
ErrorKind::MetadataParseError(
wheel.filename.clone(),
built_dist.to_string(),
Box::new(err),
)
})?
Self::wheel_metadata_local(
&fetch.path().join(&wheel.install_path),
&wheel.install_path,
&wheel.filename,
built_dist,
)
.await?
}
};

Expand All @@ -1011,6 +980,31 @@ impl RegistryClient {
Ok(metadata)
}

/// Read and parse local wheel metadata in one blocking task.
///
/// `metadata_path` identifies the wheel in diagnostics and may be relative to a Git checkout.
async fn wheel_metadata_local(
path: &Path,
metadata_path: &Path,
filename: &WheelFilename,
built_dist: &BuiltDist,
) -> Result<ResolutionMetadata, Error> {
let path = path.to_path_buf();
let metadata_path = metadata_path.to_string_lossy().into_owned();
let filename = filename.clone();
let built_dist = built_dist.to_string();
tokio::task::spawn_blocking(move || {
let file = fs_err::File::open(path).map_err(ErrorKind::Io)?;
let contents = read_archive_metadata(&filename, BufReader::new(file))
.map_err(|err| ErrorKind::Metadata(metadata_path, err))?;
ResolutionMetadata::parse_metadata(&contents).map_err(|err| {
ErrorKind::MetadataParseError(filename, built_dist, Box::new(err)).into()
})
})
.await
.map_err(|err| ErrorKind::Io(err.into()))?
}

/// Fetch the metadata from a wheel file.
async fn wheel_metadata_registry(
&self,
Expand Down
33 changes: 2 additions & 31 deletions crates/uv-metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::io;
use std::path::Path;
use thiserror::Error;
use tokio::io::AsyncReadExt;
use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};
use tokio_util::compat::FuturesAsyncReadCompatExt;
use uv_distribution_filename::WheelFilename;
use uv_normalize::{DistInfoName, InvalidNameError};
use uv_pypi_types::ResolutionMetadata;
Expand Down Expand Up @@ -226,36 +226,7 @@ fn read_dist_info_metadata(
fs_err::read(metadata_file).map_err(Error::Io)
}

/// Read a wheel's `METADATA` file from a zip file.
pub async fn read_metadata_async_seek(
filename: &WheelFilename,
reader: impl tokio::io::AsyncRead + tokio::io::AsyncSeek + Unpin,
) -> Result<Vec<u8>, Error> {
let reader = futures::io::BufReader::new(reader.compat());
let mut zip_reader = async_zip::base::read::seek::ZipFileReader::new(reader).await?;

let (metadata_idx, _dist_info_prefix) = find_archive_dist_info(
filename,
zip_reader
.file()
.entries()
.iter()
.enumerate()
.filter_map(|(index, entry)| Some((index, entry.filename().as_str().ok()?))),
)?;

// Read the contents of the `METADATA` file.
let mut contents = Vec::new();
zip_reader
.reader_with_entry(metadata_idx)
.await?
.read_to_end_checked(&mut contents)
.await?;

Ok(contents)
}

/// Like [`read_metadata_async_seek`], but doesn't use seek.
/// Read and parse a wheel's `METADATA` file from a zip stream without seeking.
pub async fn read_metadata_async_stream<R: futures::AsyncRead + Unpin>(
filename: &WheelFilename,
debug_path: &str,
Expand Down
13 changes: 10 additions & 3 deletions crates/uv-publish/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use uv_distribution_filename::{DistFilename, SourceDistExtension, SourceDistFile
use uv_distribution_types::{IndexCapabilities, IndexUrl};
use uv_extract::hash::Hasher;
use uv_fs::{ProgressReader, Simplified};
use uv_metadata::read_metadata_async_seek;
use uv_metadata::read_archive_metadata;
use uv_preview::PreviewFeature;
use uv_pypi_types::{HashAlgorithm, HashDigest, Metadata23, MetadataError};
use uv_redacted::{DisplaySafeUrl, DisplaySafeUrlError};
Expand Down Expand Up @@ -1099,8 +1099,15 @@ async fn metadata(file: &Path, filename: &DistFilename) -> Result<Metadata23, Pu
source_dist_pkg_info(file).await?
}
DistFilename::WheelFilename(wheel) => {
let reader = BufReader::new(File::open(&file).await?);
read_metadata_async_seek(wheel, reader).await?
let file = file.to_path_buf();
let wheel = wheel.clone();
return tokio::task::spawn_blocking(move || {
let reader = io::BufReader::new(fs_err::File::open(file)?);
let contents = read_archive_metadata(&wheel, reader)?;
Ok(Metadata23::parse(&contents)?)
})
.await
.map_err(io::Error::from)?;
}
};
Ok(Metadata23::parse(&contents)?)
Expand Down
Loading