From c8987269ffe714ccffa11fa57f6070cf994a0bb1 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Tue, 2 Jul 2024 14:31:30 -0400 Subject: [PATCH] Lock the toolchains directory during toolchain operations (#4733) --- crates/uv-toolchain/src/managed.rs | 12 +++++++++++- crates/uv-toolchain/src/toolchain.rs | 1 + crates/uv/src/commands/toolchain/install.rs | 1 + crates/uv/src/commands/toolchain/uninstall.rs | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/uv-toolchain/src/managed.rs b/crates/uv-toolchain/src/managed.rs index b923a3275..1e30e1391 100644 --- a/crates/uv-toolchain/src/managed.rs +++ b/crates/uv-toolchain/src/managed.rs @@ -19,7 +19,7 @@ use crate::platform::{Arch, Libc, Os}; use crate::python_version::PythonVersion; use crate::toolchain::{self, ToolchainKey}; use crate::ToolchainRequest; -use uv_fs::Simplified; +use uv_fs::{LockedFile, Simplified}; #[derive(Error, Debug)] pub enum Error { @@ -65,6 +65,14 @@ impl InstalledToolchains { Self { root: root.into() } } + /// Lock the toolchains directory. + pub fn acquire_lock(&self) -> Result { + Ok(LockedFile::acquire( + self.root.join(".lock"), + self.root.user_display(), + )?) + } + /// Prefer, in order: /// 1. The specific toolchain directory specified by the user, i.e., `UV_TOOLCHAIN_DIR` /// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/toolchains` @@ -224,6 +232,7 @@ impl InstalledToolchain { Ok(Self { path, key }) } + /// The path to this toolchain's Python executable. pub fn executable(&self) -> PathBuf { if cfg!(windows) { self.path.join("install").join("python.exe") @@ -234,6 +243,7 @@ impl InstalledToolchain { } } + /// The [`PythonVersion`] of the toolchain. pub fn version(&self) -> PythonVersion { self.key.version() } diff --git a/crates/uv-toolchain/src/toolchain.rs b/crates/uv-toolchain/src/toolchain.rs index 411d6e194..ef2f42bed 100644 --- a/crates/uv-toolchain/src/toolchain.rs +++ b/crates/uv-toolchain/src/toolchain.rs @@ -122,6 +122,7 @@ impl Toolchain { ) -> Result { let toolchains = InstalledToolchains::from_settings()?.init()?; let toolchain_dir = toolchains.root(); + let _lock = toolchains.acquire_lock()?; let download = PythonDownload::from_request(&request)?; let client = client_builder.build(); diff --git a/crates/uv/src/commands/toolchain/install.rs b/crates/uv/src/commands/toolchain/install.rs index 6631445a7..369ce60ec 100644 --- a/crates/uv/src/commands/toolchain/install.rs +++ b/crates/uv/src/commands/toolchain/install.rs @@ -31,6 +31,7 @@ pub(crate) async fn install( let toolchains = InstalledToolchains::from_settings()?.init()?; let toolchain_dir = toolchains.root(); + let _lock = toolchains.acquire_lock()?; let requests: Vec<_> = if targets.is_empty() { if let Some(requests) = requests_from_version_file().await? { diff --git a/crates/uv/src/commands/toolchain/uninstall.rs b/crates/uv/src/commands/toolchain/uninstall.rs index cf6633209..0cdb3f2a3 100644 --- a/crates/uv/src/commands/toolchain/uninstall.rs +++ b/crates/uv/src/commands/toolchain/uninstall.rs @@ -23,6 +23,7 @@ pub(crate) async fn uninstall( } let toolchains = InstalledToolchains::from_settings()?.init()?; + let _lock = toolchains.acquire_lock()?; let requests = targets .iter()