From ae7a8d7f3354fa7a68dc7e79d629636f242c613f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 12 Aug 2024 16:15:30 -0400 Subject: [PATCH] Colocate Python install cache with destination directory (#6043) ## Summary Closes https://github.com/astral-sh/uv/issues/6036. --- crates/uv-python/src/downloads.rs | 11 +++++------ crates/uv-python/src/installation.rs | 7 ++++--- crates/uv-python/src/managed.rs | 11 +++++++++++ crates/uv/src/commands/python/install.rs | 13 +++++++------ crates/uv/src/lib.rs | 4 ---- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/crates/uv-python/src/downloads.rs b/crates/uv-python/src/downloads.rs index eb6a0bff7..05b9a48b0 100644 --- a/crates/uv-python/src/downloads.rs +++ b/crates/uv-python/src/downloads.rs @@ -14,7 +14,6 @@ use tokio::io::{AsyncRead, ReadBuf}; use tokio_util::compat::FuturesAsyncReadCompatExt; use tracing::{debug, instrument}; use url::Url; -use uv_cache::Cache; use uv_client::WrappedReqwestError; use uv_extract::hash::Hasher; use uv_fs::{rename_with_retry, Simplified}; @@ -408,16 +407,16 @@ impl ManagedPythonDownload { } /// Download and extract - #[instrument(skip(client, parent_path, cache, reporter), fields(download = % self.key()))] + #[instrument(skip(client, installation_dir, cache_dir, reporter), fields(download = % self.key()))] pub async fn fetch( &self, client: &uv_client::BaseClient, - parent_path: &Path, - cache: &Cache, + installation_dir: &Path, + cache_dir: &Path, reporter: Option<&dyn Reporter>, ) -> Result { let url = self.download_url()?; - let path = parent_path.join(self.key().to_string()); + let path = installation_dir.join(self.key().to_string()); // If it already exists, return it if path.is_dir() { @@ -438,7 +437,7 @@ impl ManagedPythonDownload { .map(|reporter| (reporter, reporter.on_download_start(&self.key, size))); // Download and extract into a temporary directory. - let temp_dir = tempfile::tempdir_in(cache.root()).map_err(Error::DownloadDirError)?; + let temp_dir = tempfile::tempdir_in(cache_dir).map_err(Error::DownloadDirError)?; debug!( "Downloading {url} to temporary location: {}", diff --git a/crates/uv-python/src/installation.rs b/crates/uv-python/src/installation.rs index 3d4ffb3dc..ddff3f65e 100644 --- a/crates/uv-python/src/installation.rs +++ b/crates/uv-python/src/installation.rs @@ -1,11 +1,11 @@ use std::fmt; use std::str::FromStr; -use pep440_rs::Version; use tracing::{debug, info}; -use uv_client::BaseClientBuilder; +use pep440_rs::Version; use uv_cache::Cache; +use uv_client::BaseClientBuilder; use crate::discovery::{ find_best_python_installation, find_python_installation, EnvironmentPreference, PythonRequest, @@ -123,6 +123,7 @@ impl PythonInstallation { ) -> Result { let installations = ManagedPythonInstallations::from_settings()?.init()?; let installations_dir = installations.root(); + let cache_dir = installations.cache(); let _lock = installations.acquire_lock()?; let download = ManagedPythonDownload::from_request(&request)?; @@ -130,7 +131,7 @@ impl PythonInstallation { info!("Fetching requested Python..."); let result = download - .fetch(&client, installations_dir, cache, reporter) + .fetch(&client, installations_dir, &cache_dir, reporter) .await?; let path = match result { diff --git a/crates/uv-python/src/managed.rs b/crates/uv-python/src/managed.rs index 756bb5a40..ea7a376f3 100644 --- a/crates/uv-python/src/managed.rs +++ b/crates/uv-python/src/managed.rs @@ -95,6 +95,11 @@ impl ManagedPythonInstallations { )) } + /// Return the location of the cache directory for managed Python installations. + pub fn cache(&self) -> PathBuf { + self.root.join(".cache") + } + /// Initialize the Python installation directory. /// /// Ensures the directory is created. @@ -119,6 +124,10 @@ impl ManagedPythonInstallations { // Create the directory, if it doesn't exist. fs::create_dir_all(root)?; + // Create the cache directory, if it doesn't exist. + let cache = self.cache(); + fs::create_dir_all(&cache)?; + // Add a .gitignore. match fs::OpenOptions::new() .write(true) @@ -166,8 +175,10 @@ impl ManagedPythonInstallations { }) } }; + let cache = self.cache(); Ok(dirs .into_iter() + .filter(|path| *path != cache) .filter_map(|path| { ManagedPythonInstallation::new(path) .inspect_err(|err| { diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index d7f2b7db2..bad63ad79 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -1,14 +1,15 @@ +use std::collections::BTreeSet; +use std::fmt::Write; +use std::path::PathBuf; + use anyhow::Result; use fs_err as fs; use futures::stream::FuturesUnordered; use futures::StreamExt; use itertools::Itertools; use owo_colors::OwoColorize; -use std::collections::BTreeSet; -use std::fmt::Write; -use std::path::PathBuf; use tracing::debug; -use uv_cache::Cache; + use uv_client::Connectivity; use uv_configuration::PreviewMode; use uv_fs::CWD; @@ -34,7 +35,6 @@ pub(crate) async fn install( connectivity: Connectivity, preview: PreviewMode, no_config: bool, - cache: &Cache, printer: Printer, ) -> Result { if preview.is_disabled() { @@ -45,6 +45,7 @@ pub(crate) async fn install( let installations = ManagedPythonInstallations::from_settings()?.init()?; let installations_dir = installations.root(); + let cache_dir = installations.cache(); let _lock = installations.acquire_lock()?; let targets = targets.into_iter().collect::>(); @@ -161,7 +162,7 @@ pub(crate) async fn install( ( download.key(), download - .fetch(&client, installations_dir, cache, Some(&reporter)) + .fetch(&client, installations_dir, &cache_dir, Some(&reporter)) .await, ) }); diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 466b0943e..bf3c2db4d 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -910,9 +910,6 @@ async fn run(cli: Cli) -> Result { let args = settings::PythonInstallSettings::resolve(args, filesystem); show_settings!(args); - // Initialize the cache. - let cache = cache.init()?; - commands::python_install( args.targets, args.reinstall, @@ -921,7 +918,6 @@ async fn run(cli: Cli) -> Result { globals.connectivity, globals.preview, cli.no_config, - &cache, printer, ) .await