mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Lock for the duration of tool commands (#4720)
Feedback from https://github.com/astral-sh/uv/pull/4501#discussion_r1655391958
This commit is contained in:
parent
067b3ee666
commit
f3c5d26417
5 changed files with 37 additions and 20 deletions
|
@ -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<Vec<(PackageName, Tool)>, 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<Option<Tool>, 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<LockedFile, Error> {
|
||||
pub fn acquire_lock(&self) -> Result<LockedFile, Error> {
|
||||
Ok(LockedFile::acquire(
|
||||
self.root.join(".lock"),
|
||||
self.root.user_display(),
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Lock a tool directory.
|
||||
fn acquire_tool_lock(&self, name: &PackageName) -> Result<LockedFile, Error> {
|
||||
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<Option<PythonEnvironment>, 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<PythonEnvironment, Error> {
|
||||
let _lock = self.acquire_lock();
|
||||
let environment_path = self.root.join(name.to_string());
|
||||
|
||||
// Remove any existing environment.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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::<Vec<_>>();
|
||||
tools.sort_by_key(|(name, _)| name.clone());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue