From ef9a332364197adbf3fca061f978acef6900256d Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 25 Aug 2025 09:40:20 -0400 Subject: [PATCH] Cache `WHEEL` and `METADATA` reads in installed distributions (#15489) ## Summary Uses interior mutability to cache the reads. This follows the pattern we use for reading the platform tags in `Interpreter::tags`. --- crates/uv-distribution-types/src/installed.rs | 319 +++++++++++------- .../src/distribution_database.rs | 2 +- crates/uv-installer/src/satisfies.rs | 19 +- crates/uv-installer/src/site_packages.rs | 12 +- crates/uv-installer/src/uninstall.rs | 12 +- crates/uv-resolver/src/preferences.rs | 4 +- crates/uv/src/commands/pip/freeze.rs | 14 +- crates/uv/src/commands/pip/operations.rs | 3 + crates/uv/src/commands/pip/show.rs | 14 +- crates/uv/src/commands/pip/tree.rs | 2 +- 10 files changed, 240 insertions(+), 161 deletions(-) diff --git a/crates/uv-distribution-types/src/installed.rs b/crates/uv-distribution-types/src/installed.rs index 74bc7bb71..4c291013c 100644 --- a/crates/uv-distribution-types/src/installed.rs +++ b/crates/uv-distribution-types/src/installed.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::io::BufReader; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::sync::OnceLock; use fs_err as fs; use thiserror::Error; @@ -68,9 +69,42 @@ pub enum InstalledDistError { }, } +#[derive(Debug, Clone)] +pub struct InstalledDist { + pub kind: InstalledDistKind, + // Cache data that must be read from the `.dist-info` directory. These are safe to cache as + // the `InstalledDist` is immutable after creation. + metadata_cache: OnceLock, + tags_cache: OnceLock>, +} + +impl From for InstalledDist { + fn from(kind: InstalledDistKind) -> Self { + Self { + kind, + metadata_cache: OnceLock::new(), + tags_cache: OnceLock::new(), + } + } +} + +impl std::hash::Hash for InstalledDist { + fn hash(&self, state: &mut H) { + self.kind.hash(state); + } +} + +impl PartialEq for InstalledDist { + fn eq(&self, other: &Self) -> bool { + self.kind == other.kind + } +} + +impl Eq for InstalledDist {} + /// A built distribution (wheel) that is installed in a virtual environment. #[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub enum InstalledDist { +pub enum InstalledDistKind { /// The distribution was derived from a registry, like `PyPI`. Registry(InstalledRegistryDist), /// The distribution was derived from an arbitrary URL. @@ -152,35 +186,41 @@ impl InstalledDist { return if let Some(direct_url) = Self::read_direct_url(path)? { match Url::try_from(&direct_url) { - Ok(url) => Ok(Some(Self::Url(InstalledDirectUrlDist { - name, - version, - editable: matches!(&direct_url, DirectUrl::LocalDirectory { dir_info, .. } if dir_info.editable == Some(true)), - direct_url: Box::new(direct_url), - url: DisplaySafeUrl::from(url), - path: path.to_path_buf().into_boxed_path(), - cache_info, - build_info, - }))), - Err(err) => { - warn!("Failed to parse direct URL: {err}"); - Ok(Some(Self::Registry(InstalledRegistryDist { + Ok(url) => Ok(Some(Self::from(InstalledDistKind::Url( + InstalledDirectUrlDist { name, version, + editable: matches!(&direct_url, DirectUrl::LocalDirectory { dir_info, .. } if dir_info.editable == Some(true)), + direct_url: Box::new(direct_url), + url: DisplaySafeUrl::from(url), path: path.to_path_buf().into_boxed_path(), cache_info, build_info, - }))) + }, + )))), + Err(err) => { + warn!("Failed to parse direct URL: {err}"); + Ok(Some(Self::from(InstalledDistKind::Registry( + InstalledRegistryDist { + name, + version, + path: path.to_path_buf().into_boxed_path(), + cache_info, + build_info, + }, + )))) } } } else { - Ok(Some(Self::Registry(InstalledRegistryDist { - name, - version, - path: path.to_path_buf().into_boxed_path(), - cache_info, - build_info, - }))) + Ok(Some(Self::from(InstalledDistKind::Registry( + InstalledRegistryDist { + name, + version, + path: path.to_path_buf().into_boxed_path(), + cache_info, + build_info, + }, + )))) }; } @@ -204,19 +244,23 @@ impl InstalledDist { if let Some(version) = file_name.version { if metadata.is_dir() { - return Ok(Some(Self::EggInfoDirectory(InstalledEggInfoDirectory { - name: file_name.name, - version, - path: path.to_path_buf().into_boxed_path(), - }))); + return Ok(Some(Self::from(InstalledDistKind::EggInfoDirectory( + InstalledEggInfoDirectory { + name: file_name.name, + version, + path: path.to_path_buf().into_boxed_path(), + }, + )))); } if metadata.is_file() { - return Ok(Some(Self::EggInfoFile(InstalledEggInfoFile { - name: file_name.name, - version, - path: path.to_path_buf().into_boxed_path(), - }))); + return Ok(Some(Self::from(InstalledDistKind::EggInfoFile( + InstalledEggInfoFile { + name: file_name.name, + version, + path: path.to_path_buf().into_boxed_path(), + }, + )))); } } @@ -224,22 +268,26 @@ impl InstalledDist { let Some(egg_metadata) = read_metadata(&path.join("PKG-INFO")) else { return Ok(None); }; - return Ok(Some(Self::EggInfoDirectory(InstalledEggInfoDirectory { - name: file_name.name, - version: Version::from_str(&egg_metadata.version)?, - path: path.to_path_buf().into_boxed_path(), - }))); + return Ok(Some(Self::from(InstalledDistKind::EggInfoDirectory( + InstalledEggInfoDirectory { + name: file_name.name, + version: Version::from_str(&egg_metadata.version)?, + path: path.to_path_buf().into_boxed_path(), + }, + )))); } if metadata.is_file() { let Some(egg_metadata) = read_metadata(path) else { return Ok(None); }; - return Ok(Some(Self::EggInfoDirectory(InstalledEggInfoDirectory { - name: file_name.name, - version: Version::from_str(&egg_metadata.version)?, - path: path.to_path_buf().into_boxed_path(), - }))); + return Ok(Some(Self::from(InstalledDistKind::EggInfoDirectory( + InstalledEggInfoDirectory { + name: file_name.name, + version: Version::from_str(&egg_metadata.version)?, + path: path.to_path_buf().into_boxed_path(), + }, + )))); } } @@ -283,14 +331,16 @@ impl InstalledDist { return Ok(None); }; - return Ok(Some(Self::LegacyEditable(InstalledLegacyEditable { - name: egg_metadata.name, - version: Version::from_str(&egg_metadata.version)?, - egg_link: path.to_path_buf().into_boxed_path(), - target: target.into_boxed_path(), - target_url: DisplaySafeUrl::from(url), - egg_info: egg_info.into_boxed_path(), - }))); + return Ok(Some(Self::from(InstalledDistKind::LegacyEditable( + InstalledLegacyEditable { + name: egg_metadata.name, + version: Version::from_str(&egg_metadata.version)?, + egg_link: path.to_path_buf().into_boxed_path(), + target: target.into_boxed_path(), + target_url: DisplaySafeUrl::from(url), + egg_info: egg_info.into_boxed_path(), + }, + )))); } Ok(None) @@ -298,45 +348,45 @@ impl InstalledDist { /// Return the [`Path`] at which the distribution is stored on-disk. pub fn install_path(&self) -> &Path { - match self { - Self::Registry(dist) => &dist.path, - Self::Url(dist) => &dist.path, - Self::EggInfoDirectory(dist) => &dist.path, - Self::EggInfoFile(dist) => &dist.path, - Self::LegacyEditable(dist) => &dist.egg_info, + match &self.kind { + InstalledDistKind::Registry(dist) => &dist.path, + InstalledDistKind::Url(dist) => &dist.path, + InstalledDistKind::EggInfoDirectory(dist) => &dist.path, + InstalledDistKind::EggInfoFile(dist) => &dist.path, + InstalledDistKind::LegacyEditable(dist) => &dist.egg_info, } } /// Return the [`Version`] of the distribution. pub fn version(&self) -> &Version { - match self { - Self::Registry(dist) => &dist.version, - Self::Url(dist) => &dist.version, - Self::EggInfoDirectory(dist) => &dist.version, - Self::EggInfoFile(dist) => &dist.version, - Self::LegacyEditable(dist) => &dist.version, + match &self.kind { + InstalledDistKind::Registry(dist) => &dist.version, + InstalledDistKind::Url(dist) => &dist.version, + InstalledDistKind::EggInfoDirectory(dist) => &dist.version, + InstalledDistKind::EggInfoFile(dist) => &dist.version, + InstalledDistKind::LegacyEditable(dist) => &dist.version, } } /// Return the [`CacheInfo`] of the distribution, if any. pub fn cache_info(&self) -> Option<&CacheInfo> { - match self { - Self::Registry(dist) => dist.cache_info.as_ref(), - Self::Url(dist) => dist.cache_info.as_ref(), - Self::EggInfoDirectory(..) => None, - Self::EggInfoFile(..) => None, - Self::LegacyEditable(..) => None, + match &self.kind { + InstalledDistKind::Registry(dist) => dist.cache_info.as_ref(), + InstalledDistKind::Url(dist) => dist.cache_info.as_ref(), + InstalledDistKind::EggInfoDirectory(..) => None, + InstalledDistKind::EggInfoFile(..) => None, + InstalledDistKind::LegacyEditable(..) => None, } } /// Return the [`BuildInfo`] of the distribution, if any. pub fn build_info(&self) -> Option<&BuildInfo> { - match self { - Self::Registry(dist) => dist.build_info.as_ref(), - Self::Url(dist) => dist.build_info.as_ref(), - Self::EggInfoDirectory(..) => None, - Self::EggInfoFile(..) => None, - Self::LegacyEditable(..) => None, + match &self.kind { + InstalledDistKind::Registry(dist) => dist.build_info.as_ref(), + InstalledDistKind::Url(dist) => dist.build_info.as_ref(), + InstalledDistKind::EggInfoDirectory(..) => None, + InstalledDistKind::EggInfoFile(..) => None, + InstalledDistKind::LegacyEditable(..) => None, } } @@ -380,9 +430,13 @@ impl InstalledDist { } /// Read the `METADATA` file from a `.dist-info` directory. - pub fn read_metadata(&self) -> Result { - match self { - Self::Registry(_) | Self::Url(_) => { + pub fn read_metadata(&self) -> Result<&uv_pypi_types::ResolutionMetadata, InstalledDistError> { + if let Some(metadata) = self.metadata_cache.get() { + return Ok(metadata); + } + + let metadata = match &self.kind { + InstalledDistKind::Registry(_) | InstalledDistKind::Url(_) => { let path = self.install_path().join("METADATA"); let contents = fs::read(&path)?; // TODO(zanieb): Update this to use thiserror so we can unpack parse errors downstream @@ -391,13 +445,19 @@ impl InstalledDist { path: path.clone(), err: Box::new(err), } - }) + })? } - Self::EggInfoFile(_) | Self::EggInfoDirectory(_) | Self::LegacyEditable(_) => { - let path = match self { - Self::EggInfoFile(dist) => Cow::Borrowed(&*dist.path), - Self::EggInfoDirectory(dist) => Cow::Owned(dist.path.join("PKG-INFO")), - Self::LegacyEditable(dist) => Cow::Owned(dist.egg_info.join("PKG-INFO")), + InstalledDistKind::EggInfoFile(_) + | InstalledDistKind::EggInfoDirectory(_) + | InstalledDistKind::LegacyEditable(_) => { + let path = match &self.kind { + InstalledDistKind::EggInfoFile(dist) => Cow::Borrowed(&*dist.path), + InstalledDistKind::EggInfoDirectory(dist) => { + Cow::Owned(dist.path.join("PKG-INFO")) + } + InstalledDistKind::LegacyEditable(dist) => { + Cow::Owned(dist.egg_info.join("PKG-INFO")) + } _ => unreachable!(), }; let contents = fs::read(path.as_ref())?; @@ -406,9 +466,12 @@ impl InstalledDist { path: path.to_path_buf(), err: Box::new(err), } - }) + })? } - } + }; + + let _ = self.metadata_cache.set(metadata); + Ok(self.metadata_cache.get().expect("metadata should be set")) } /// Return the `INSTALLER` of the distribution. @@ -422,56 +485,64 @@ impl InstalledDist { } /// Return the supported wheel tags for the distribution from the `WHEEL` file, if available. - pub fn read_tags(&self) -> Result, InstalledDistError> { - // TODO(charlie): Cache this result. - let path = match self { - Self::Registry(InstalledRegistryDist { path, .. }) => path, - Self::Url(InstalledDirectUrlDist { path, .. }) => path, - Self::EggInfoFile(_) => return Ok(None), - Self::EggInfoDirectory(_) => return Ok(None), - Self::LegacyEditable(_) => return Ok(None), + pub fn read_tags(&self) -> Result, InstalledDistError> { + if let Some(tags) = self.tags_cache.get() { + return Ok(tags.as_ref()); + } + + let path = match &self.kind { + InstalledDistKind::Registry(dist) => &dist.path, + InstalledDistKind::Url(dist) => &dist.path, + InstalledDistKind::EggInfoFile(_) => return Ok(None), + InstalledDistKind::EggInfoDirectory(_) => return Ok(None), + InstalledDistKind::LegacyEditable(_) => return Ok(None), }; // Read the `WHEEL` file. let contents = fs_err::read_to_string(path.join("WHEEL"))?; let wheel_file = WheelFile::parse(&contents)?; - let Some(tags) = wheel_file.tags() else { - return Ok(None); - }; // Parse the tags. - let tags = ExpandedTags::parse(tags.iter().map(String::as_str))?; + let tags = if let Some(tags) = wheel_file.tags() { + Some(ExpandedTags::parse(tags.iter().map(String::as_str))?) + } else { + None + }; - Ok(Some(tags)) + let _ = self.tags_cache.set(tags); + Ok(self.tags_cache.get().expect("tags should be set").as_ref()) } /// Return true if the distribution is editable. pub fn is_editable(&self) -> bool { matches!( - self, - Self::LegacyEditable(_) | Self::Url(InstalledDirectUrlDist { editable: true, .. }) + &self.kind, + InstalledDistKind::LegacyEditable(_) + | InstalledDistKind::Url(InstalledDirectUrlDist { editable: true, .. }) ) } /// Return the [`Url`] of the distribution, if it is editable. pub fn as_editable(&self) -> Option<&Url> { - match self { - Self::Registry(_) => None, - Self::Url(dist) => dist.editable.then_some(&dist.url), - Self::EggInfoFile(_) => None, - Self::EggInfoDirectory(_) => None, - Self::LegacyEditable(dist) => Some(&dist.target_url), + match &self.kind { + InstalledDistKind::Registry(_) => None, + InstalledDistKind::Url(dist) => dist.editable.then_some(&dist.url), + InstalledDistKind::EggInfoFile(_) => None, + InstalledDistKind::EggInfoDirectory(_) => None, + InstalledDistKind::LegacyEditable(dist) => Some(&dist.target_url), } } /// Return true if the distribution refers to a local file or directory. pub fn is_local(&self) -> bool { - match self { - Self::Registry(_) => false, - Self::Url(dist) => matches!(&*dist.direct_url, DirectUrl::LocalDirectory { .. }), - Self::EggInfoFile(_) => false, - Self::EggInfoDirectory(_) => false, - Self::LegacyEditable(_) => true, + match &self.kind { + InstalledDistKind::Registry(_) => false, + InstalledDistKind::Url(dist) => { + matches!(&*dist.direct_url, DirectUrl::LocalDirectory { .. }) + } + InstalledDistKind::EggInfoFile(_) => false, + InstalledDistKind::EggInfoDirectory(_) => false, + InstalledDistKind::LegacyEditable(_) => true, } } } @@ -514,12 +585,12 @@ impl Name for InstalledLegacyEditable { impl Name for InstalledDist { fn name(&self) -> &PackageName { - match self { - Self::Registry(dist) => dist.name(), - Self::Url(dist) => dist.name(), - Self::EggInfoDirectory(dist) => dist.name(), - Self::EggInfoFile(dist) => dist.name(), - Self::LegacyEditable(dist) => dist.name(), + match &self.kind { + InstalledDistKind::Registry(dist) => dist.name(), + InstalledDistKind::Url(dist) => dist.name(), + InstalledDistKind::EggInfoDirectory(dist) => dist.name(), + InstalledDistKind::EggInfoFile(dist) => dist.name(), + InstalledDistKind::LegacyEditable(dist) => dist.name(), } } } @@ -556,12 +627,12 @@ impl InstalledMetadata for InstalledLegacyEditable { impl InstalledMetadata for InstalledDist { fn installed_version(&self) -> InstalledVersion<'_> { - match self { - Self::Registry(dist) => dist.installed_version(), - Self::Url(dist) => dist.installed_version(), - Self::EggInfoFile(dist) => dist.installed_version(), - Self::EggInfoDirectory(dist) => dist.installed_version(), - Self::LegacyEditable(dist) => dist.installed_version(), + match &self.kind { + InstalledDistKind::Registry(dist) => dist.installed_version(), + InstalledDistKind::Url(dist) => dist.installed_version(), + InstalledDistKind::EggInfoFile(dist) => dist.installed_version(), + InstalledDistKind::EggInfoDirectory(dist) => dist.installed_version(), + InstalledDistKind::LegacyEditable(dist) => dist.installed_version(), } } } diff --git a/crates/uv-distribution/src/distribution_database.rs b/crates/uv-distribution/src/distribution_database.rs index 2700fb2d6..df04b3845 100644 --- a/crates/uv-distribution/src/distribution_database.rs +++ b/crates/uv-distribution/src/distribution_database.rs @@ -144,7 +144,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> { .read_metadata() .map_err(|err| Error::ReadInstalled(Box::new(dist.clone()), err))?; - Ok(ArchiveMetadata::from_metadata23(metadata)) + Ok(ArchiveMetadata::from_metadata23(metadata.clone())) } /// Either fetch the only wheel metadata (directly from the index or with range requests) or diff --git a/crates/uv-installer/src/satisfies.rs b/crates/uv-installer/src/satisfies.rs index 448755825..9c3cc00cd 100644 --- a/crates/uv-installer/src/satisfies.rs +++ b/crates/uv-installer/src/satisfies.rs @@ -9,8 +9,8 @@ use uv_cache_info::CacheInfo; use uv_cache_key::{CanonicalUrl, RepositoryUrl}; use uv_distribution_types::{ BuildInfo, BuildVariables, ConfigSettings, ExtraBuildRequirement, ExtraBuildRequires, - ExtraBuildVariables, InstalledDirectUrlDist, InstalledDist, PackageConfigSettings, - RequirementSource, + ExtraBuildVariables, InstalledDirectUrlDist, InstalledDist, InstalledDistKind, + PackageConfigSettings, RequirementSource, }; use uv_git_types::GitOid; use uv_normalize::PackageName; @@ -78,12 +78,12 @@ impl RequirementSatisfaction { ext: _, url: _, } => { - let InstalledDist::Url(InstalledDirectUrlDist { + let InstalledDistKind::Url(InstalledDirectUrlDist { direct_url, editable, cache_info, .. - }) = &distribution + }) = &distribution.kind else { return Self::Mismatch; }; @@ -137,7 +137,8 @@ impl RequirementSatisfaction { git: requested_git, subdirectory: requested_subdirectory, } => { - let InstalledDist::Url(InstalledDirectUrlDist { direct_url, .. }) = &distribution + let InstalledDistKind::Url(InstalledDirectUrlDist { direct_url, .. }) = + &distribution.kind else { return Self::Mismatch; }; @@ -192,11 +193,11 @@ impl RequirementSatisfaction { ext: _, url: _, } => { - let InstalledDist::Url(InstalledDirectUrlDist { + let InstalledDistKind::Url(InstalledDirectUrlDist { direct_url, cache_info, .. - }) = &distribution + }) = &distribution.kind else { return Self::Mismatch; }; @@ -247,11 +248,11 @@ impl RequirementSatisfaction { r#virtual: _, url: _, } => { - let InstalledDist::Url(InstalledDirectUrlDist { + let InstalledDistKind::Url(InstalledDirectUrlDist { direct_url, cache_info, .. - }) = &distribution + }) = &distribution.kind else { return Self::Mismatch; }; diff --git a/crates/uv-installer/src/site_packages.rs b/crates/uv-installer/src/site_packages.rs index fcc9d7861..2c2b64eac 100644 --- a/crates/uv-installer/src/site_packages.rs +++ b/crates/uv-installer/src/site_packages.rs @@ -8,9 +8,9 @@ use fs_err as fs; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use uv_distribution_types::{ - ConfigSettings, Diagnostic, ExtraBuildRequires, ExtraBuildVariables, InstalledDist, Name, - NameRequirementSpecification, PackageConfigSettings, Requirement, UnresolvedRequirement, - UnresolvedRequirementSpecification, + ConfigSettings, Diagnostic, ExtraBuildRequires, ExtraBuildVariables, InstalledDist, + InstalledDistKind, Name, NameRequirementSpecification, PackageConfigSettings, Requirement, + UnresolvedRequirement, UnresolvedRequirementSpecification, }; use uv_fs::Simplified; use uv_normalize::PackageName; @@ -126,7 +126,7 @@ impl SitePackages { .push(idx); // Index the distribution by URL. - if let InstalledDist::Url(dist) = &dist_info { + if let InstalledDistKind::Url(dist) = &dist_info.kind { by_url.entry(dist.url.clone()).or_default().push(idx); } @@ -528,8 +528,8 @@ impl SitePackages { .with_context(|| format!("Failed to read metadata for: {distribution}"))?; // Add the dependencies to the queue. - for dependency in metadata.requires_dist { - let dependency = Requirement::from(dependency); + for dependency in &metadata.requires_dist { + let dependency = Requirement::from(dependency.clone()); if let Some(r#overrides) = overrides.get(&dependency.name) { for dependency in r#overrides { if dependency.evaluate_markers(Some(markers), &requirement.extras) { diff --git a/crates/uv-installer/src/uninstall.rs b/crates/uv-installer/src/uninstall.rs index 726d1273f..cfbea6830 100644 --- a/crates/uv-installer/src/uninstall.rs +++ b/crates/uv-installer/src/uninstall.rs @@ -1,4 +1,4 @@ -use uv_distribution_types::{InstalledDist, InstalledEggInfoFile}; +use uv_distribution_types::{InstalledDist, InstalledDistKind, InstalledEggInfoFile}; /// Uninstall a package from the specified Python environment. pub async fn uninstall( @@ -6,17 +6,17 @@ pub async fn uninstall( ) -> Result { let uninstall = tokio::task::spawn_blocking({ let dist = dist.clone(); - move || match dist { - InstalledDist::Registry(_) | InstalledDist::Url(_) => { + move || match dist.kind { + InstalledDistKind::Registry(_) | InstalledDistKind::Url(_) => { Ok(uv_install_wheel::uninstall_wheel(dist.install_path())?) } - InstalledDist::EggInfoDirectory(_) => { + InstalledDistKind::EggInfoDirectory(_) => { Ok(uv_install_wheel::uninstall_egg(dist.install_path())?) } - InstalledDist::LegacyEditable(dist) => { + InstalledDistKind::LegacyEditable(dist) => { Ok(uv_install_wheel::uninstall_legacy_editable(&dist.egg_link)?) } - InstalledDist::EggInfoFile(dist) => Err(UninstallError::Distutils(dist)), + InstalledDistKind::EggInfoFile(dist) => Err(UninstallError::Distutils(dist)), } }) .await??; diff --git a/crates/uv-resolver/src/preferences.rs b/crates/uv-resolver/src/preferences.rs index 51e325d68..5683e8a17 100644 --- a/crates/uv-resolver/src/preferences.rs +++ b/crates/uv-resolver/src/preferences.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use rustc_hash::FxHashMap; use tracing::trace; -use uv_distribution_types::{IndexUrl, InstalledDist}; +use uv_distribution_types::{IndexUrl, InstalledDist, InstalledDistKind}; use uv_normalize::PackageName; use uv_pep440::{Operator, Version}; use uv_pep508::{MarkerTree, VerbatimUrl, VersionOrUrl}; @@ -122,7 +122,7 @@ impl Preference { /// Create a [`Preference`] from an installed distribution. pub fn from_installed(dist: &InstalledDist) -> Option { - let InstalledDist::Registry(dist) = dist else { + let InstalledDistKind::Registry(dist) = &dist.kind else { return None; }; Some(Self { diff --git a/crates/uv/src/commands/pip/freeze.rs b/crates/uv/src/commands/pip/freeze.rs index 56deb6f44..09735a88a 100644 --- a/crates/uv/src/commands/pip/freeze.rs +++ b/crates/uv/src/commands/pip/freeze.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use owo_colors::OwoColorize; use uv_cache::Cache; -use uv_distribution_types::{Diagnostic, InstalledDist, Name}; +use uv_distribution_types::{Diagnostic, InstalledDistKind, Name}; use uv_installer::SitePackages; use uv_preview::Preview; use uv_python::PythonPreference; @@ -61,24 +61,24 @@ pub(crate) fn pip_freeze( .flat_map(uv_installer::SitePackages::iter) .filter(|dist| !(exclude_editable && dist.is_editable())) .sorted_unstable_by(|a, b| a.name().cmp(b.name()).then(a.version().cmp(b.version()))) - .map(|dist| match dist { - InstalledDist::Registry(dist) => { + .map(|dist| match &dist.kind { + InstalledDistKind::Registry(dist) => { format!("{}=={}", dist.name().bold(), dist.version) } - InstalledDist::Url(dist) => { + InstalledDistKind::Url(dist) => { if dist.editable { format!("-e {}", dist.url) } else { format!("{} @ {}", dist.name().bold(), dist.url) } } - InstalledDist::EggInfoFile(dist) => { + InstalledDistKind::EggInfoFile(dist) => { format!("{}=={}", dist.name().bold(), dist.version) } - InstalledDist::EggInfoDirectory(dist) => { + InstalledDistKind::EggInfoDirectory(dist) => { format!("{}=={}", dist.name().bold(), dist.version) } - InstalledDist::LegacyEditable(dist) => { + InstalledDistKind::LegacyEditable(dist) => { format!("-e {}", dist.target.display()) } }) diff --git a/crates/uv/src/commands/pip/operations.rs b/crates/uv/src/commands/pip/operations.rs index c343ee321..de079d1ae 100644 --- a/crates/uv/src/commands/pip/operations.rs +++ b/crates/uv/src/commands/pip/operations.rs @@ -389,6 +389,9 @@ pub(crate) struct Changelog { impl Changelog { /// Create a [`Changelog`] from a list of installed and uninstalled distributions. pub(crate) fn new(installed: Vec, uninstalled: Vec) -> Self { + // SAFETY: This is allowed because `LocalDist` implements `Hash` and `Eq` based solely on + // the inner `kind`, and omits the types that rely on internal mutability. + #[allow(clippy::mutable_key_type)] let mut uninstalled: HashSet<_> = uninstalled.into_iter().map(LocalDist::from).collect(); let (reinstalled, installed): (HashSet<_>, HashSet<_>) = installed diff --git a/crates/uv/src/commands/pip/show.rs b/crates/uv/src/commands/pip/show.rs index 5f99536b3..5a906547f 100644 --- a/crates/uv/src/commands/pip/show.rs +++ b/crates/uv/src/commands/pip/show.rs @@ -102,9 +102,11 @@ pub(crate) fn pip_show( if let Ok(metadata) = dist.read_metadata() { requires_map.insert( dist.name(), - Box::into_iter(metadata.requires_dist) + metadata + .requires_dist + .iter() .filter(|req| req.evaluate_markers(&markers, &[])) - .map(|req| req.name) + .map(|req| &req.name) .sorted_unstable() .dedup() .collect_vec(), @@ -118,9 +120,11 @@ pub(crate) fn pip_show( continue; } if let Ok(metadata) = installed.read_metadata() { - let requires = Box::into_iter(metadata.requires_dist) + let requires = metadata + .requires_dist + .iter() .filter(|req| req.evaluate_markers(&markers, &[])) - .map(|req| req.name) + .map(|req| &req.name) .collect_vec(); if !requires.is_empty() { requires_map.insert(installed.name(), requires); @@ -172,7 +176,7 @@ pub(crate) fn pip_show( .iter() .filter(|(name, pkgs)| { **name != distribution.name() - && pkgs.iter().any(|pkg| pkg == distribution.name()) + && pkgs.iter().any(|pkg| *pkg == distribution.name()) }) .map(|(name, _)| name) .sorted_unstable() diff --git a/crates/uv/src/commands/pip/tree.rs b/crates/uv/src/commands/pip/tree.rs index a8c42a17b..fc8bec9c5 100644 --- a/crates/uv/src/commands/pip/tree.rs +++ b/crates/uv/src/commands/pip/tree.rs @@ -225,7 +225,7 @@ impl<'env> DisplayDependencyGraph<'env> { invert: bool, show_version_specifiers: bool, markers: &ResolverMarkerEnvironment, - packages: &'env FxHashMap<&PackageName, Vec>, + packages: &'env FxHashMap<&PackageName, Vec<&ResolutionMetadata>>, latest: &'env FxHashMap<&PackageName, Version>, ) -> Self { // Create a graph.