Enable --all to uninstall all managed Pythons (#4932)

## Summary

Allows `--all` as an alternative to specifying specific targets.

## Test Plan

Verified that `cargo run python uninstall` still fails.

```
❯ cargo run python uninstall --all
   Compiling uv-cli v0.0.1 (/Users/crmarsh/workspace/puffin/crates/uv-cli)
   Compiling uv v0.2.23 (/Users/crmarsh/workspace/puffin/crates/uv)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.86s
     Running `target/debug/uv python uninstall --all`
warning: `uv python uninstall` is experimental and may change without warning.
Searching for Python installations
Found existing installation: cpython-3.9.18-macos-aarch64-none
Found existing installation: cpython-3.8.18-macos-aarch64-none
Found existing installation: cpython-3.8.12-macos-aarch64-none
Found existing installation: cpython-3.12.1-macos-aarch64-none
Found existing installation: cpython-3.11.7-macos-aarch64-none
Found existing installation: cpython-3.10.13-macos-aarch64-none
Uninstalled cpython-3.10.13-macos-aarch64-none
Uninstalled cpython-3.11.7-macos-aarch64-none
Uninstalled cpython-3.12.1-macos-aarch64-none
Uninstalled cpython-3.8.12-macos-aarch64-none
Uninstalled cpython-3.8.18-macos-aarch64-none
Uninstalled cpython-3.9.18-macos-aarch64-none
Uninstalled 6 versions in 479ms
```
This commit is contained in:
Charlie Marsh 2024-07-09 11:15:16 -07:00 committed by GitHub
parent bd7a25f604
commit 0a04108a15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 47 additions and 28 deletions

View file

@ -2198,6 +2198,10 @@ pub struct PythonUninstallArgs {
/// The Python version(s) to uninstall. /// The Python version(s) to uninstall.
#[arg(required = true)] #[arg(required = true)]
pub targets: Vec<String>, pub targets: Vec<String>,
/// Uninstall all managed Python versions.
#[arg(long, conflicts_with("targets"))]
pub all: bool,
} }
#[derive(Args)] #[derive(Args)]

View file

@ -18,6 +18,7 @@ use crate::printer::Printer;
/// Uninstall managed Python versions. /// Uninstall managed Python versions.
pub(crate) async fn uninstall( pub(crate) async fn uninstall(
targets: Vec<String>, targets: Vec<String>,
all: bool,
preview: PreviewMode, preview: PreviewMode,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
@ -30,11 +31,15 @@ pub(crate) async fn uninstall(
let installations = ManagedPythonInstallations::from_settings()?.init()?; let installations = ManagedPythonInstallations::from_settings()?.init()?;
let _lock = installations.acquire_lock()?; let _lock = installations.acquire_lock()?;
let targets = targets.into_iter().collect::<BTreeSet<_>>(); let requests = if all {
let requests = targets vec![PythonRequest::Any]
.iter() } else {
.map(|target| PythonRequest::parse(target.as_str())) let targets = targets.into_iter().collect::<BTreeSet<_>>();
.collect::<Vec<_>>(); targets
.iter()
.map(|target| PythonRequest::parse(target.as_str()))
.collect::<Vec<_>>()
};
let download_requests = requests let download_requests = requests
.iter() .iter()
@ -44,11 +49,15 @@ pub(crate) async fn uninstall(
let installed_installations: Vec<_> = installations.find_all()?.collect(); let installed_installations: Vec<_> = installations.find_all()?.collect();
let mut matching_installations = BTreeSet::default(); let mut matching_installations = BTreeSet::default();
for (request, download_request) in requests.iter().zip(download_requests) { for (request, download_request) in requests.iter().zip(download_requests) {
writeln!( if matches!(requests.as_slice(), [PythonRequest::Any]) {
printer.stderr(), writeln!(printer.stderr(), "Searching for Python installations")?;
"Searching for Python versions matching: {}", } else {
request.cyan() writeln!(
)?; printer.stderr(),
"Searching for Python versions matching: {}",
request.cyan()
)?;
}
let mut found = false; let mut found = false;
for installation in installed_installations for installation in installed_installations
.iter() .iter()
@ -56,15 +65,24 @@ pub(crate) async fn uninstall(
{ {
found = true; found = true;
if matching_installations.insert(installation.clone()) { if matching_installations.insert(installation.clone()) {
writeln!( if matches!(requests.as_slice(), [PythonRequest::Any]) {
printer.stderr(), writeln!(printer.stderr(), "Found: {}", installation.key().green(),)?;
"Found existing installation for {}: {}", } else {
request.cyan(), writeln!(
installation.key().green(), printer.stderr(),
)?; "Found existing installation for {}: {}",
request.cyan(),
installation.key().green(),
)?;
}
} }
} }
if !found { if !found {
if matches!(requests.as_slice(), [PythonRequest::Any]) {
writeln!(printer.stderr(), "No Python installations found")?;
return Ok(ExitStatus::Failure);
}
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"No existing installations found for: {}", "No existing installations found for: {}",
@ -74,14 +92,10 @@ pub(crate) async fn uninstall(
} }
if matching_installations.is_empty() { if matching_installations.is_empty() {
if matches!(requests.as_slice(), [PythonRequest::Any]) { writeln!(
writeln!(printer.stderr(), "No Python installations found")?; printer.stderr(),
} else if requests.len() > 1 { "No Python installations found matching the requests"
writeln!( )?;
printer.stderr(),
"No Python installations found matching the requests"
)?;
}
return Ok(ExitStatus::Failure); return Ok(ExitStatus::Failure);
} }
@ -105,7 +119,7 @@ pub(crate) async fn uninstall(
key.green() key.green()
)?; )?;
} else { } else {
writeln!(printer.stderr(), "Uninstalled {}", key.green())?; writeln!(printer.stderr(), "Uninstalled: {}", key.green())?;
} }
} }

View file

@ -788,7 +788,7 @@ async fn run() -> Result<ExitStatus> {
let args = settings::PythonUninstallSettings::resolve(args, filesystem); let args = settings::PythonUninstallSettings::resolve(args, filesystem);
show_settings!(args); show_settings!(args);
commands::python_uninstall(args.targets, globals.preview, printer).await commands::python_uninstall(args.targets, args.all, globals.preview, printer).await
} }
Commands::Python(PythonNamespace { Commands::Python(PythonNamespace {
command: PythonCommand::Find(args), command: PythonCommand::Find(args),

View file

@ -376,6 +376,7 @@ impl PythonInstallSettings {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct PythonUninstallSettings { pub(crate) struct PythonUninstallSettings {
pub(crate) targets: Vec<String>, pub(crate) targets: Vec<String>,
pub(crate) all: bool,
} }
impl PythonUninstallSettings { impl PythonUninstallSettings {
@ -385,9 +386,9 @@ impl PythonUninstallSettings {
args: PythonUninstallArgs, args: PythonUninstallArgs,
_filesystem: Option<FilesystemOptions>, _filesystem: Option<FilesystemOptions>,
) -> Self { ) -> Self {
let PythonUninstallArgs { targets } = args; let PythonUninstallArgs { targets, all } = args;
Self { targets } Self { targets, all }
} }
} }