From 17a6789d1b14cdaefaa1be7b1099c4feddb0d835 Mon Sep 17 00:00:00 2001 From: eth3lbert Date: Tue, 26 Mar 2024 03:45:05 +0800 Subject: [PATCH] `Editable project location` and `Required-by` for `pip show` (#2589) ## Summary - Displays missing packages as single-line warnings. - Adds support for `Editable project location` and `Required-by` fields in `pip show`. Part of #2526. --- crates/uv-dev/src/resolve_cli.rs | 5 ++++- crates/uv-dispatch/src/lib.rs | 4 +++- crates/uv-installer/src/site_packages.rs | 21 ++++++++++++++++++ crates/uv-resolver/src/resolver/mod.rs | 27 ++++++++++++++++++++---- crates/uv-resolver/tests/resolver.rs | 5 ++++- crates/uv-traits/src/lib.rs | 26 ++++++++++++++++++++++- crates/uv/src/commands/pip_compile.rs | 5 ++++- crates/uv/src/commands/pip_install.rs | 1 + crates/uv/src/commands/pip_sync.rs | 6 +++--- 9 files changed, 88 insertions(+), 12 deletions(-) diff --git a/crates/uv-dev/src/resolve_cli.rs b/crates/uv-dev/src/resolve_cli.rs index eff8ea4ecba9..10b593284d1f 100644 --- a/crates/uv-dev/src/resolve_cli.rs +++ b/crates/uv-dev/src/resolve_cli.rs @@ -14,7 +14,7 @@ use pep508_rs::Requirement; use uv_cache::{Cache, CacheArgs}; use uv_client::{FlatIndex, FlatIndexClient, RegistryClientBuilder}; use uv_dispatch::BuildDispatch; -use uv_installer::NoBinary; +use uv_installer::{NoBinary, SitePackages}; use uv_interpreter::PythonEnvironment; use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver}; use uv_traits::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy}; @@ -88,6 +88,8 @@ pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> { &NoBinary::None, ); + let site_packages = SitePackages::from_executable(&venv)?; + // Copied from `BuildDispatch` let tags = venv.interpreter().tags()?; let resolver = Resolver::new( @@ -100,6 +102,7 @@ pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> { &flat_index, &index, &build_dispatch, + &site_packages, )?; let resolution_graph = resolver.resolve().await.with_context(|| { format!( diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 047b3cd414dd..56a2b858be55 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -21,7 +21,8 @@ use uv_installer::{Downloader, Installer, NoBinary, Plan, Planner, Reinstall, Si use uv_interpreter::{Interpreter, PythonEnvironment}; use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver}; use uv_traits::{ - BuildContext, BuildIsolation, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy, + BuildContext, BuildIsolation, BuildKind, ConfigSettings, EmptyInstalledPackages, InFlight, + NoBuild, SetupPyStrategy, }; /// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`] @@ -145,6 +146,7 @@ impl<'a> BuildContext for BuildDispatch<'a> { self.flat_index, self.index, self, + &EmptyInstalledPackages, )?; let graph = resolver.resolve().await.with_context(|| { format!( diff --git a/crates/uv-installer/src/site_packages.rs b/crates/uv-installer/src/site_packages.rs index 56069588c034..90a42afcc3c6 100644 --- a/crates/uv-installer/src/site_packages.rs +++ b/crates/uv-installer/src/site_packages.rs @@ -14,6 +14,7 @@ use requirements_txt::EditableRequirement; use uv_cache::{ArchiveTarget, ArchiveTimestamp}; use uv_interpreter::PythonEnvironment; use uv_normalize::PackageName; +use uv_traits::InstalledPackagesProvider; use crate::is_dynamic; @@ -577,3 +578,23 @@ impl Diagnostic { } } } + +impl InstalledPackagesProvider for SitePackages<'_> { + fn iter(&self) -> impl Iterator { + self.iter() + } + + fn get_packages(&self, name: &PackageName) -> Vec<&InstalledDist> { + self.get_packages(name) + } +} + +impl InstalledPackagesProvider for &SitePackages<'_> { + fn iter(&self) -> impl Iterator { + (*self).iter() + } + + fn get_packages(&self, name: &PackageName) -> Vec<&InstalledDist> { + (*self).get_packages(name) + } +} diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index ee829ccf7cba..b29b9c4860b8 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -31,7 +31,7 @@ use uv_client::{FlatIndex, RegistryClient}; use uv_distribution::DistributionDatabase; use uv_interpreter::Interpreter; use uv_normalize::PackageName; -use uv_traits::BuildContext; +use uv_traits::{BuildContext, InstalledPackagesProvider}; use crate::candidate_selector::{CandidateDist, CandidateSelector}; use crate::constraints::Constraints; @@ -89,7 +89,11 @@ enum ResolverVersion { Unavailable(Version, UnavailableVersion), } -pub struct Resolver<'a, Provider: ResolverProvider> { +pub struct Resolver< + 'a, + Provider: ResolverProvider, + InstalledPackages: InstalledPackagesProvider + Send + Sync, +> { project: Option, requirements: Vec, constraints: Constraints, @@ -103,6 +107,7 @@ pub struct Resolver<'a, Provider: ResolverProvider> { python_requirement: PythonRequirement, selector: CandidateSelector, index: &'a InMemoryIndex, + installed_packages: &'a InstalledPackages, /// Incompatibilities for packages that are entirely unavailable unavailable_packages: DashMap, /// The set of all registry-based packages visited during resolution. @@ -111,7 +116,12 @@ pub struct Resolver<'a, Provider: ResolverProvider> { provider: Provider, } -impl<'a, Context: BuildContext + Send + Sync> Resolver<'a, DefaultResolverProvider<'a, Context>> { +impl< + 'a, + Context: BuildContext + Send + Sync, + InstalledPackages: InstalledPackagesProvider + Send + Sync, + > Resolver<'a, DefaultResolverProvider<'a, Context>, InstalledPackages> +{ /// Initialize a new resolver using the default backend doing real requests. /// /// Reads the flat index entries. @@ -126,6 +136,7 @@ impl<'a, Context: BuildContext + Send + Sync> Resolver<'a, DefaultResolverProvid flat_index: &'a FlatIndex, index: &'a InMemoryIndex, build_context: &'a Context, + installed_packages: &'a InstalledPackages, ) -> Result { let provider = DefaultResolverProvider::new( client, @@ -145,11 +156,17 @@ impl<'a, Context: BuildContext + Send + Sync> Resolver<'a, DefaultResolverProvid PythonRequirement::new(interpreter, markers), index, provider, + installed_packages, ) } } -impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { +impl< + 'a, + Provider: ResolverProvider, + InstalledPackages: InstalledPackagesProvider + Send + Sync, + > Resolver<'a, Provider, InstalledPackages> +{ /// Initialize a new resolver using a user provided backend. pub fn new_custom_io( manifest: Manifest, @@ -158,6 +175,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { python_requirement: PythonRequirement, index: &'a InMemoryIndex, provider: Provider, + installed_packages: &'a InstalledPackages, ) -> Result { Ok(Self { index, @@ -177,6 +195,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { python_requirement, reporter: None, provider, + installed_packages, }) } diff --git a/crates/uv-resolver/tests/resolver.rs b/crates/uv-resolver/tests/resolver.rs index 939f4b0c18fe..f8004e60372f 100644 --- a/crates/uv-resolver/tests/resolver.rs +++ b/crates/uv-resolver/tests/resolver.rs @@ -21,7 +21,8 @@ use uv_resolver::{ Preference, ResolutionGraph, ResolutionMode, Resolver, }; use uv_traits::{ - BuildContext, BuildIsolation, BuildKind, NoBinary, NoBuild, SetupPyStrategy, SourceBuildTrait, + BuildContext, BuildIsolation, BuildKind, EmptyInstalledPackages, NoBinary, NoBuild, + SetupPyStrategy, SourceBuildTrait, }; // Exclude any packages uploaded after this date. @@ -124,6 +125,7 @@ async fn resolve( find_default_python(&Cache::temp().unwrap()).expect("Expected a python to be installed"); let interpreter = Interpreter::artificial(real_interpreter.platform().clone(), markers.clone()); let build_context = DummyContext::new(Cache::temp()?, interpreter.clone()); + let installed_packages = EmptyInstalledPackages; let resolver = Resolver::new( manifest, options, @@ -134,6 +136,7 @@ async fn resolve( &flat_index, &index, &build_context, + &installed_packages, )?; Ok(resolver.resolve().await?) } diff --git a/crates/uv-traits/src/lib.rs b/crates/uv-traits/src/lib.rs index 335195c93742..09b1cd32745c 100644 --- a/crates/uv-traits/src/lib.rs +++ b/crates/uv-traits/src/lib.rs @@ -9,7 +9,9 @@ use std::str::FromStr; use anyhow::Result; -use distribution_types::{CachedDist, DistributionId, IndexLocations, Resolution, SourceDist}; +use distribution_types::{ + CachedDist, DistributionId, IndexLocations, InstalledDist, Resolution, SourceDist, +}; use once_map::OnceMap; use pep508_rs::Requirement; use uv_cache::Cache; @@ -374,6 +376,28 @@ impl ConfigSettings { } } +/// A wrapper for [`uv_installer::SitePackages`] +pub trait InstalledPackagesProvider { + fn iter(&self) -> impl Iterator; + fn get_packages(&self, name: &PackageName) -> Vec<&InstalledDist>; +} + +/// An [`InstalledPackagesProvider`] with no packages in it. +pub struct EmptyInstalledPackages; + +impl InstalledPackagesProvider for EmptyInstalledPackages { + fn get_packages( + &self, + _name: &pep508_rs::PackageName, + ) -> Vec<&distribution_types::InstalledDist> { + Vec::new() + } + + fn iter(&self) -> impl Iterator { + std::iter::empty() + } +} + #[cfg(feature = "serde")] impl serde::Serialize for ConfigSettings { fn serialize(&self, serializer: S) -> Result { diff --git a/crates/uv/src/commands/pip_compile.rs b/crates/uv/src/commands/pip_compile.rs index 4608bdc6f524..835e3ba6f429 100644 --- a/crates/uv/src/commands/pip_compile.rs +++ b/crates/uv/src/commands/pip_compile.rs @@ -35,7 +35,9 @@ use uv_resolver::{ AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest, OptionsBuilder, PreReleaseMode, PythonRequirement, ResolutionMode, Resolver, }; -use uv_traits::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy}; +use uv_traits::{ + BuildIsolation, ConfigSettings, EmptyInstalledPackages, InFlight, NoBuild, SetupPyStrategy, +}; use uv_warnings::warn_user; use crate::commands::reporters::{DownloadReporter, ResolverReporter}; @@ -336,6 +338,7 @@ pub(crate) async fn pip_compile( &flat_index, &top_level_index, &build_dispatch, + &EmptyInstalledPackages, )? .with_reporter(ResolverReporter::from(printer)); diff --git a/crates/uv/src/commands/pip_install.rs b/crates/uv/src/commands/pip_install.rs index fca811a47e54..91cb353aeb8b 100644 --- a/crates/uv/src/commands/pip_install.rs +++ b/crates/uv/src/commands/pip_install.rs @@ -545,6 +545,7 @@ async fn resolve( flat_index, index, build_dispatch, + &site_packages, )? .with_reporter(ResolverReporter::from(printer)); let resolution = resolver.resolve().await?; diff --git a/crates/uv/src/commands/pip_sync.rs b/crates/uv/src/commands/pip_sync.rs index 517ed13026b0..5f6722494663 100644 --- a/crates/uv/src/commands/pip_sync.rs +++ b/crates/uv/src/commands/pip_sync.rs @@ -161,6 +161,9 @@ pub(crate) async fn pip_sync( BuildIsolation::Isolated }; + // Determine the set of installed packages. + let site_packages = SitePackages::from_executable(&venv)?; + // Prep the build context. let build_dispatch = BuildDispatch::new( &client, @@ -183,9 +186,6 @@ pub(crate) async fn pip_sync( .resolve(&build_dispatch, &client) .await?; - // Determine the set of installed packages. - let site_packages = SitePackages::from_executable(&venv)?; - // Resolve any editables. let resolved_editables = resolve_editables( editables,