diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index 69049070a..895a6d328 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -86,8 +86,9 @@ impl InstalledTools { } /// Return the metadata for all installed tools. + /// + /// Note it is generally incorrect to use this without [`Self::acquire_lock`]. pub fn tools(&self) -> Result, Error> { - let _lock = self.acquire_lock(); let mut tools = Vec::new(); for directory in uv_fs::directories(self.root()) { let name = directory.file_name().unwrap().to_string_lossy().to_string(); @@ -109,6 +110,8 @@ impl InstalledTools { } /// Get the receipt for the given tool. + /// + /// Note it is generally incorrect to use this without [`Self::acquire_lock`]. pub fn get_tool_receipt(&self, name: &PackageName) -> Result, Error> { let path = self.root.join(name.to_string()).join("uv-receipt.toml"); match ToolReceipt::from_path(&path) { @@ -119,28 +122,19 @@ impl InstalledTools { } /// Lock the tools directory. - fn acquire_lock(&self) -> Result { + pub fn acquire_lock(&self) -> Result { Ok(LockedFile::acquire( self.root.join(".lock"), self.root.user_display(), )?) } - /// Lock a tool directory. - fn acquire_tool_lock(&self, name: &PackageName) -> Result { - let path = self.root.join(name.to_string()); - Ok(LockedFile::acquire( - path.join(".lock"), - path.user_display(), - )?) - } - /// Add a receipt for a tool. /// /// Any existing receipt will be replaced. + /// + /// Note it is generally incorrect to use this without [`Self::acquire_lock`]. pub fn add_tool_receipt(&self, name: &PackageName, tool: Tool) -> Result<(), Error> { - let _lock = self.acquire_tool_lock(name); - let tool_receipt = ToolReceipt::from(tool); let path = self.root.join(name.to_string()).join("uv-receipt.toml"); @@ -161,11 +155,12 @@ impl InstalledTools { /// /// Does not remove the tool's entrypoints. /// + /// Note it is generally incorrect to use this without [`Self::acquire_lock`]. + /// /// # Errors /// /// If no such environment exists for the tool. pub fn remove_environment(&self, name: &PackageName) -> Result<(), Error> { - let _lock = self.acquire_lock(); let environment_path = self.root.join(name.to_string()); debug!( @@ -182,12 +177,13 @@ impl InstalledTools { /// /// Returns `Ok(None)` if the environment does not exist or is linked to a non-existent /// interpreter. + /// + /// Note it is generally incorrect to use this without [`Self::acquire_lock`]. pub fn get_environment( &self, name: &PackageName, cache: &Cache, ) -> Result, Error> { - let _lock = self.acquire_lock(); let environment_path = self.root.join(name.to_string()); match PythonEnvironment::from_root(&environment_path, cache) { @@ -213,12 +209,13 @@ impl InstalledTools { } /// Create the [`PythonEnvironment`] for a given tool, removing any existing environments. + /// + /// Note it is generally incorrect to use this without [`Self::acquire_lock`]. pub fn create_environment( &self, name: &PackageName, interpreter: Interpreter, ) -> Result { - let _lock = self.acquire_lock(); let environment_path = self.root.join(name.to_string()); // Remove any existing environment. diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index 44438e157..aaa156535 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -154,7 +154,8 @@ pub(crate) async fn install( requirements }; - let installed_tools = InstalledTools::from_settings()?; + let installed_tools = InstalledTools::from_settings()?.init()?; + let _lock = installed_tools.acquire_lock()?; let existing_tool_receipt = installed_tools.get_tool_receipt(&from.name)?; let existing_environment = installed_tools @@ -361,7 +362,6 @@ pub(crate) async fn install( )?; debug!("Adding receipt for tool `{}`", from.name); - let installed_tools = installed_tools.init()?; let tool = Tool::new( requirements .into_iter() diff --git a/crates/uv/src/commands/tool/list.rs b/crates/uv/src/commands/tool/list.rs index 7ce4663de..ce5bab13a 100644 --- a/crates/uv/src/commands/tool/list.rs +++ b/crates/uv/src/commands/tool/list.rs @@ -22,6 +22,14 @@ pub(crate) async fn list( } let installed_tools = InstalledTools::from_settings()?; + let _lock = match installed_tools.acquire_lock() { + Ok(lock) => lock, + Err(uv_tool::Error::IO(err)) if err.kind() == std::io::ErrorKind::NotFound => { + writeln!(printer.stderr(), "No tools installed")?; + return Ok(ExitStatus::Success); + } + Err(err) => return Err(err.into()), + }; let mut tools = installed_tools.tools()?.into_iter().collect::>(); tools.sort_by_key(|(name, _)| name.clone()); diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 9557b0b21..f9582dd5e 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -218,7 +218,8 @@ async fn get_or_create_environment( // Check if the tool is already installed in a compatible environment. if !isolated { - let installed_tools = InstalledTools::from_settings()?; + let installed_tools = InstalledTools::from_settings()?.init()?; + let _lock = installed_tools.acquire_lock()?; let existing_environment = installed_tools diff --git a/crates/uv/src/commands/tool/uninstall.rs b/crates/uv/src/commands/tool/uninstall.rs index e77a51850..3fd35d979 100644 --- a/crates/uv/src/commands/tool/uninstall.rs +++ b/crates/uv/src/commands/tool/uninstall.rs @@ -24,7 +24,18 @@ pub(crate) async fn uninstall( warn_user_once!("`uv tool uninstall` is experimental and may change without warning."); } - let installed_tools = InstalledTools::from_settings()?; + let installed_tools = InstalledTools::from_settings()?.init()?; + let _lock = match installed_tools.acquire_lock() { + Ok(lock) => lock, + Err(uv_tool::Error::IO(err)) if err.kind() == std::io::ErrorKind::NotFound => { + if let Some(name) = name { + bail!("`{name}` is not installed"); + } + writeln!(printer.stderr(), "Nothing to uninstall")?; + return Ok(ExitStatus::Success); + } + Err(err) => return Err(err.into()), + }; let mut entrypoints = if let Some(name) = name { let Some(receipt) = installed_tools.get_tool_receipt(&name)? else {