mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Add uv toolchain uninstall
(#4646)
This commit is contained in:
parent
ad5151cda8
commit
8dabc29d80
9 changed files with 167 additions and 6 deletions
|
@ -2022,15 +2022,18 @@ pub enum ToolchainCommand {
|
|||
/// List the available toolchains.
|
||||
List(ToolchainListArgs),
|
||||
|
||||
/// Download and install a specific toolchain.
|
||||
/// Download and install toolchains.
|
||||
Install(ToolchainInstallArgs),
|
||||
|
||||
/// Search for a toolchain
|
||||
/// Search for a toolchain.
|
||||
#[command(disable_version_flag = true)]
|
||||
Find(ToolchainFindArgs),
|
||||
|
||||
/// Show the toolchains directory.
|
||||
Dir,
|
||||
|
||||
/// Uninstall toolchains.
|
||||
Uninstall(ToolchainUninstallArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
@ -2064,6 +2067,13 @@ pub struct ToolchainInstallArgs {
|
|||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct ToolchainUninstallArgs {
|
||||
/// The toolchains to uninstall.
|
||||
pub targets: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct ToolchainFindArgs {
|
||||
|
|
|
@ -17,7 +17,7 @@ pub enum ImplementationName {
|
|||
PyPy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd)]
|
||||
pub enum LenientImplementationName {
|
||||
Known(ImplementationName),
|
||||
Unknown(String),
|
||||
|
|
|
@ -204,7 +204,7 @@ Error=This toolchain is managed by uv and should not be modified.
|
|||
";
|
||||
|
||||
/// A uv-managed Python toolchain installed on the current system..
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct InstalledToolchain {
|
||||
/// The path to the top-level directory of the installed toolchain.
|
||||
path: PathBuf,
|
||||
|
|
|
@ -33,6 +33,7 @@ pub(crate) use toolchain::dir::dir as toolchain_dir;
|
|||
pub(crate) use toolchain::find::find as toolchain_find;
|
||||
pub(crate) use toolchain::install::install as toolchain_install;
|
||||
pub(crate) use toolchain::list::list as toolchain_list;
|
||||
pub(crate) use toolchain::uninstall::uninstall as toolchain_uninstall;
|
||||
use uv_cache::Cache;
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::compile_tree;
|
||||
|
|
|
@ -63,7 +63,7 @@ pub(crate) async fn install(
|
|||
{
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Found installed toolchain '{}' that satisfies {request}",
|
||||
"Found installed toolchain `{}` that satisfies {request}",
|
||||
toolchain.key()
|
||||
)?;
|
||||
if force {
|
||||
|
|
|
@ -2,3 +2,4 @@ pub(crate) mod dir;
|
|||
pub(crate) mod find;
|
||||
pub(crate) mod install;
|
||||
pub(crate) mod list;
|
||||
pub(crate) mod uninstall;
|
||||
|
|
120
crates/uv/src/commands/toolchain/uninstall.rs
Normal file
120
crates/uv/src/commands/toolchain/uninstall.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
use anyhow::Result;
|
||||
use futures::StreamExt;
|
||||
use itertools::Itertools;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::Write;
|
||||
use uv_configuration::PreviewMode;
|
||||
use uv_toolchain::downloads::{self, PythonDownloadRequest};
|
||||
use uv_toolchain::managed::InstalledToolchains;
|
||||
use uv_toolchain::ToolchainRequest;
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::printer::Printer;
|
||||
|
||||
/// Uninstall Python toolchains.
|
||||
pub(crate) async fn uninstall(
|
||||
targets: Vec<String>,
|
||||
preview: PreviewMode,
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
if preview.is_disabled() {
|
||||
warn_user_once!("`uv toolchain uninstall` is experimental and may change without warning.");
|
||||
}
|
||||
|
||||
let toolchains = InstalledToolchains::from_settings()?.init()?;
|
||||
|
||||
let requests = targets
|
||||
.iter()
|
||||
.map(|target| ToolchainRequest::parse(target.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let download_requests = requests
|
||||
.iter()
|
||||
.map(PythonDownloadRequest::from_request)
|
||||
.collect::<Result<Vec<_>, downloads::Error>>()?;
|
||||
|
||||
let installed_toolchains: Vec<_> = toolchains.find_all()?.collect();
|
||||
let mut matching_toolchains = BTreeSet::default();
|
||||
for (request, download_request) in requests.iter().zip(download_requests) {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Looking for installed toolchains matching {request} ({download_request})"
|
||||
)?;
|
||||
let mut found = false;
|
||||
for toolchain in installed_toolchains
|
||||
.iter()
|
||||
.filter(|toolchain| download_request.satisfied_by_key(toolchain.key()))
|
||||
{
|
||||
found = true;
|
||||
if matching_toolchains.insert(toolchain.clone()) {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Found toolchain `{}` that matches {request}",
|
||||
toolchain.key()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
writeln!(printer.stderr(), "No toolchains found matching {request}")?;
|
||||
}
|
||||
}
|
||||
|
||||
if matching_toolchains.is_empty() {
|
||||
if matches!(requests.as_slice(), [ToolchainRequest::Any]) {
|
||||
writeln!(printer.stderr(), "No installed toolchains found")?;
|
||||
} else if requests.len() > 1 {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"No toolchains found matching the requests"
|
||||
)?;
|
||||
} else {
|
||||
writeln!(printer.stderr(), "No toolchains found matching the request")?;
|
||||
}
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
||||
let tasks = futures::stream::iter(matching_toolchains.iter())
|
||||
.map(|toolchain| async {
|
||||
(
|
||||
toolchain.key(),
|
||||
fs_err::tokio::remove_dir_all(toolchain.path()).await,
|
||||
)
|
||||
})
|
||||
.buffered(4);
|
||||
|
||||
let results = tasks.collect::<Vec<_>>().await;
|
||||
let mut failed = false;
|
||||
for (key, result) in results.iter().sorted_by_key(|(key, _)| key) {
|
||||
if let Err(err) = result {
|
||||
failed = true;
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Failed to uninstall toolchain `{key}`: {err}"
|
||||
)?;
|
||||
} else {
|
||||
writeln!(printer.stderr(), "Uninstalled toolchain `{key}`")?;
|
||||
}
|
||||
}
|
||||
|
||||
if failed {
|
||||
if matching_toolchains.len() > 1 {
|
||||
writeln!(printer.stderr(), "Uninstall of some toolchains failed")?;
|
||||
}
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
||||
let s = if matching_toolchains.len() == 1 {
|
||||
""
|
||||
} else {
|
||||
"s"
|
||||
};
|
||||
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Uninstalled {} toolchain{s}",
|
||||
matching_toolchains.len()
|
||||
)?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
|
@ -906,6 +906,15 @@ async fn run() -> Result<ExitStatus> {
|
|||
)
|
||||
.await
|
||||
}
|
||||
Commands::Toolchain(ToolchainNamespace {
|
||||
command: ToolchainCommand::Uninstall(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::ToolchainUninstallSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
commands::toolchain_uninstall(args.targets, globals.preview, printer).await
|
||||
}
|
||||
Commands::Toolchain(ToolchainNamespace {
|
||||
command: ToolchainCommand::Find(args),
|
||||
}) => {
|
||||
|
|
|
@ -15,7 +15,7 @@ use uv_cli::{
|
|||
PipCheckArgs, PipCompileArgs, PipFreezeArgs, PipInstallArgs, PipListArgs, PipShowArgs,
|
||||
PipSyncArgs, PipTreeArgs, PipUninstallArgs, RemoveArgs, RunArgs, SyncArgs, ToolInstallArgs,
|
||||
ToolListArgs, ToolRunArgs, ToolUninstallArgs, ToolchainFindArgs, ToolchainInstallArgs,
|
||||
ToolchainListArgs, VenvArgs,
|
||||
ToolchainListArgs, ToolchainUninstallArgs, VenvArgs,
|
||||
};
|
||||
use uv_client::Connectivity;
|
||||
use uv_configuration::{
|
||||
|
@ -374,6 +374,26 @@ impl ToolchainInstallSettings {
|
|||
}
|
||||
}
|
||||
|
||||
/// The resolved settings to use for a `toolchain uninstall` invocation.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ToolchainUninstallSettings {
|
||||
pub(crate) targets: Vec<String>,
|
||||
}
|
||||
|
||||
impl ToolchainUninstallSettings {
|
||||
/// Resolve the [`ToolchainUninstallSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(
|
||||
args: ToolchainUninstallArgs,
|
||||
_filesystem: Option<FilesystemOptions>,
|
||||
) -> Self {
|
||||
let ToolchainUninstallArgs { targets } = args;
|
||||
|
||||
Self { targets }
|
||||
}
|
||||
}
|
||||
|
||||
/// The resolved settings to use for a `toolchain find` invocation.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue