mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-14 17:59:46 +00:00
More resilient registry removal (#14717)
With the previous order of operations, there could be warnings from race conditions between two process A and B removing and installing Python versions. * A removes the files for CPython3.9.18 * B sees the key CPython3.9.18 * B sees that CPython3.9.18 has no files * A removes the key for CPython3.9.18 * B try to removes the key for CPython3.9.18, gets and error that it's already gone, issues a warning We make the more resilient in two ways: * We remove the registry key first, avoiding dangling registry keys in the removal process * We ignore not found errors in registry removal operations: If we try to remove something that's already gone, that's fine. Fixes #14714 (hopefully)
This commit is contained in:
parent
8f2f43c561
commit
d1f4f8a358
2 changed files with 19 additions and 11 deletions
|
|
@ -268,6 +268,9 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
|
||||||
// Separate assignment since `keys()` creates a borrow.
|
// Separate assignment since `keys()` creates a borrow.
|
||||||
let subkeys = match key.keys() {
|
let subkeys = match key.keys() {
|
||||||
Ok(subkeys) => subkeys,
|
Ok(subkeys) => subkeys,
|
||||||
|
Err(err) if err.code() == ERROR_NOT_FOUND => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// TODO(konsti): We don't have an installation key here.
|
// TODO(konsti): We don't have an installation key here.
|
||||||
warn_user_once!("Failed to list subkeys of HKCU:\\{astral_key}: {err}");
|
warn_user_once!("Failed to list subkeys of HKCU:\\{astral_key}: {err}");
|
||||||
|
|
@ -281,6 +284,9 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
|
||||||
let python_entry = format!("{astral_key}\\{subkey}");
|
let python_entry = format!("{astral_key}\\{subkey}");
|
||||||
debug!("Removing orphan registry key HKCU:\\{}", python_entry);
|
debug!("Removing orphan registry key HKCU:\\{}", python_entry);
|
||||||
if let Err(err) = CURRENT_USER.remove_tree(&python_entry) {
|
if let Err(err) = CURRENT_USER.remove_tree(&python_entry) {
|
||||||
|
if err.code() == ERROR_NOT_FOUND {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// TODO(konsti): We don't have an installation key here.
|
// TODO(konsti): We don't have an installation key here.
|
||||||
warn_user_once!("Failed to remove orphan registry key HKCU:\\{python_entry}: {err}");
|
warn_user_once!("Failed to remove orphan registry key HKCU:\\{python_entry}: {err}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,19 @@ async fn do_uninstall(
|
||||||
return Ok(ExitStatus::Failure);
|
return Ok(ExitStatus::Failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove registry entries first, so we don't have dangling entries between the file removal
|
||||||
|
// and the registry removal.
|
||||||
|
let mut errors = vec![];
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
uv_python::windows_registry::remove_registry_entry(
|
||||||
|
&matching_installations,
|
||||||
|
all,
|
||||||
|
&mut errors,
|
||||||
|
);
|
||||||
|
uv_python::windows_registry::remove_orphan_registry_entries(&installed_installations);
|
||||||
|
}
|
||||||
|
|
||||||
// Find and remove all relevant Python executables
|
// Find and remove all relevant Python executables
|
||||||
let mut uninstalled_executables: FxHashMap<PythonInstallationKey, FxHashSet<PathBuf>> =
|
let mut uninstalled_executables: FxHashMap<PythonInstallationKey, FxHashSet<PathBuf>> =
|
||||||
FxHashMap::default();
|
FxHashMap::default();
|
||||||
|
|
@ -201,7 +214,6 @@ async fn do_uninstall(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut uninstalled = IndexSet::<PythonInstallationKey>::default();
|
let mut uninstalled = IndexSet::<PythonInstallationKey>::default();
|
||||||
let mut errors = vec![];
|
|
||||||
while let Some((key, result)) = tasks.next().await {
|
while let Some((key, result)) = tasks.next().await {
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
errors.push((key.clone(), anyhow::Error::new(err)));
|
errors.push((key.clone(), anyhow::Error::new(err)));
|
||||||
|
|
@ -210,16 +222,6 @@ async fn do_uninstall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
uv_python::windows_registry::remove_registry_entry(
|
|
||||||
&matching_installations,
|
|
||||||
all,
|
|
||||||
&mut errors,
|
|
||||||
);
|
|
||||||
uv_python::windows_registry::remove_orphan_registry_entries(&installed_installations);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read all existing managed installations and find the highest installed patch
|
// Read all existing managed installations and find the highest installed patch
|
||||||
// for each installed minor version. Ensure the minor version link directory
|
// for each installed minor version. Ensure the minor version link directory
|
||||||
// is still valid.
|
// is still valid.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue