mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Accept package names in the tool API (#4737)
## Summary It seems helpful that these _not_ accept arbitrary strings.
This commit is contained in:
parent
c8987269ff
commit
21187e1f36
5 changed files with 33 additions and 24 deletions
|
@ -2014,7 +2014,8 @@ pub struct ToolListArgs;
|
|||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct ToolUninstallArgs {
|
||||
pub name: String,
|
||||
/// The name of the tool to uninstall.
|
||||
pub name: PackageName,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
|
@ -96,8 +96,8 @@ impl InstalledTools {
|
|||
}
|
||||
|
||||
/// Get the receipt for the given tool.
|
||||
pub fn get_tool_receipt(&self, name: &str) -> Result<Option<Tool>, Error> {
|
||||
let path = self.root.join(name).join("uv-receipt.toml");
|
||||
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) {
|
||||
Ok(tool_receipt) => Ok(Some(tool_receipt.tool)),
|
||||
Err(Error::IO(err)) if err.kind() == io::ErrorKind::NotFound => Ok(None),
|
||||
|
@ -114,8 +114,8 @@ impl InstalledTools {
|
|||
}
|
||||
|
||||
/// Lock a tool directory.
|
||||
fn acquire_tool_lock(&self, name: &str) -> Result<LockedFile, Error> {
|
||||
let path = self.root.join(name);
|
||||
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(),
|
||||
|
@ -125,11 +125,11 @@ impl InstalledTools {
|
|||
/// Add a receipt for a tool.
|
||||
///
|
||||
/// Any existing receipt will be replaced.
|
||||
pub fn add_tool_receipt(&self, name: &str, tool: Tool) -> Result<(), Error> {
|
||||
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).join("uv-receipt.toml");
|
||||
let path = self.root.join(name.to_string()).join("uv-receipt.toml");
|
||||
|
||||
debug!(
|
||||
"Adding metadata entry for tool `{name}` at {}",
|
||||
|
@ -147,9 +147,9 @@ impl InstalledTools {
|
|||
/// Remove the environment for a tool.
|
||||
///
|
||||
/// Does not remove the tool's entrypoints.
|
||||
pub fn remove_environment(&self, name: &str) -> Result<(), Error> {
|
||||
pub fn remove_environment(&self, name: &PackageName) -> Result<(), Error> {
|
||||
let _lock = self.acquire_lock();
|
||||
let environment_path = self.root.join(name);
|
||||
let environment_path = self.root.join(name.to_string());
|
||||
|
||||
debug!(
|
||||
"Deleting environment for tool `{name}` at {}",
|
||||
|
@ -161,15 +161,16 @@ impl InstalledTools {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the [`PythonEnvironment`] for a given tool.
|
||||
pub fn environment(
|
||||
&self,
|
||||
name: &str,
|
||||
name: &PackageName,
|
||||
remove_existing: bool,
|
||||
interpreter: Interpreter,
|
||||
cache: &Cache,
|
||||
) -> Result<PythonEnvironment, Error> {
|
||||
let _lock = self.acquire_lock();
|
||||
let environment_path = self.root.join(name);
|
||||
let environment_path = self.root.join(name.to_string());
|
||||
|
||||
if !remove_existing && environment_path.exists() {
|
||||
debug!(
|
||||
|
|
|
@ -122,11 +122,9 @@ pub(crate) async fn install(
|
|||
.unwrap()
|
||||
};
|
||||
|
||||
let name = from.name.to_string();
|
||||
|
||||
let installed_tools = InstalledTools::from_settings()?;
|
||||
|
||||
let existing_tool_receipt = installed_tools.get_tool_receipt(&name)?;
|
||||
let existing_tool_receipt = installed_tools.get_tool_receipt(&from.name)?;
|
||||
// TODO(zanieb): Automatically replace an existing tool if the request differs
|
||||
let reinstall_entry_points = if existing_tool_receipt.is_some() {
|
||||
if force {
|
||||
|
@ -142,7 +140,11 @@ pub(crate) async fn install(
|
|||
Reinstall::Packages(ref packages) => packages.contains(&from.name),
|
||||
// If not reinstalling... then we're done
|
||||
Reinstall::None => {
|
||||
writeln!(printer.stderr(), "Tool `{name}` is already installed")?;
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Tool `{}` is already installed",
|
||||
from.name
|
||||
)?;
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +178,7 @@ pub(crate) async fn install(
|
|||
// TODO(zanieb): Build the environment in the cache directory then copy into the tool directory
|
||||
// This lets us confirm the environment is valid before removing an existing install
|
||||
let environment = installed_tools.environment(
|
||||
&name,
|
||||
&from.name,
|
||||
// Do not remove the existing environment if we're reinstalling a subset of packages
|
||||
!matches!(settings.reinstall, Reinstall::Packages(_)),
|
||||
interpreter,
|
||||
|
@ -208,7 +210,11 @@ pub(crate) async fn install(
|
|||
// Exit early if we're not supposed to be reinstalling entry points
|
||||
// e.g. `--reinstall-package` was used for some dependency
|
||||
if existing_tool_receipt.is_some() && !reinstall_entry_points {
|
||||
writeln!(printer.stderr(), "Updated environment for tool `{name}`")?;
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Updated environment for tool `{}`",
|
||||
from.name
|
||||
)?;
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
|
||||
|
@ -246,9 +252,9 @@ pub(crate) async fn install(
|
|||
|
||||
if target_entry_points.is_empty() {
|
||||
// Clean up the environment we just created
|
||||
installed_tools.remove_environment(&name)?;
|
||||
installed_tools.remove_environment(&from.name)?;
|
||||
|
||||
bail!("No entry points found for tool `{name}`");
|
||||
bail!("No entry points found for tool `{}`", from.name);
|
||||
}
|
||||
|
||||
// Check if they exist, before installing
|
||||
|
@ -266,7 +272,7 @@ pub(crate) async fn install(
|
|||
}
|
||||
} else if existing_entry_points.peek().is_some() {
|
||||
// Clean up the environment we just created
|
||||
installed_tools.remove_environment(&name)?;
|
||||
installed_tools.remove_environment(&from.name)?;
|
||||
|
||||
let existing_entry_points = existing_entry_points
|
||||
// SAFETY: We know the target has a filename because we just constructed it above
|
||||
|
@ -300,7 +306,7 @@ pub(crate) async fn install(
|
|||
.join(", ")
|
||||
)?;
|
||||
|
||||
debug!("Adding receipt for tool `{name}`");
|
||||
debug!("Adding receipt for tool `{}`", from.name);
|
||||
let installed_tools = installed_tools.init()?;
|
||||
let tool = Tool::new(
|
||||
requirements
|
||||
|
@ -312,7 +318,7 @@ pub(crate) async fn install(
|
|||
.into_iter()
|
||||
.map(|(name, _, target_path)| ToolEntrypoint::new(name, target_path)),
|
||||
);
|
||||
installed_tools.add_tool_receipt(&name, tool)?;
|
||||
installed_tools.add_tool_receipt(&from.name, tool)?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use itertools::Itertools;
|
|||
use tracing::debug;
|
||||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_tool::InstalledTools;
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
|
@ -14,7 +15,7 @@ use crate::printer::Printer;
|
|||
|
||||
/// Uninstall a tool.
|
||||
pub(crate) async fn uninstall(
|
||||
name: String,
|
||||
name: PackageName,
|
||||
preview: PreviewMode,
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
|
|
|
@ -300,7 +300,7 @@ impl ToolListSettings {
|
|||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ToolUninstallSettings {
|
||||
pub(crate) name: String,
|
||||
pub(crate) name: PackageName,
|
||||
}
|
||||
|
||||
impl ToolUninstallSettings {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue