-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate to XDG and Linux strategy for macOS directories #5806
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,24 @@ pub fn replace_symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io: | |
} | ||
} | ||
|
||
#[cfg(unix)] | ||
pub fn remove_symlink(path: impl AsRef<Path>) -> std::io::Result<()> { | ||
fs_err::remove_file(path.as_ref()) | ||
} | ||
|
||
#[cfg(windows)] | ||
pub fn remove_symlink(path: impl AsRef<Path>) -> std::io::Result<()> { | ||
match junction::delete(dunce::simplified(path.as_ref())) { | ||
Ok(()) => match fs_err::remove_dir_all(path.as_ref()) { | ||
Ok(()) => Ok(()), | ||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()), | ||
Err(err) => Err(err), | ||
}, | ||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()), | ||
Err(err) => Err(err), | ||
} | ||
} | ||
|
||
/// Return a [`NamedTempFile`] in the specified directory. | ||
/// | ||
/// Sets the permissions of the temporary file to `0o666`, to match the non-temporary file default. | ||
|
@@ -283,6 +301,14 @@ pub fn files(path: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> { | |
.map(|entry| entry.path()) | ||
} | ||
|
||
/// Returns `true` if a path is a temporary file or directory. | ||
pub fn is_temporary(path: impl AsRef<Path>) -> bool { | ||
path.as_ref() | ||
.file_name() | ||
.and_then(|name| name.to_str()) | ||
.map_or(false, |name| name.starts_with(".tmp")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to check this on Windows. |
||
} | ||
|
||
/// A file lock that is automatically released when dropped. | ||
#[derive(Debug)] | ||
pub struct LockedFile(fs_err::File); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,11 +22,42 @@ pub(crate) async fn uninstall( | |
|
||
printer: Printer, | ||
) -> Result<ExitStatus> { | ||
let start = std::time::Instant::now(); | ||
|
||
let installations = ManagedPythonInstallations::from_settings()?.init()?; | ||
let _lock = installations.acquire_lock()?; | ||
|
||
// Perform the uninstallation. | ||
do_uninstall(&installations, targets, all, printer).await?; | ||
|
||
// Clean up any empty directories. | ||
if uv_fs::directories(installations.root()).all(|path| uv_fs::is_temporary(&path)) { | ||
fs_err::tokio::remove_dir_all(&installations.root()).await?; | ||
|
||
if let Some(top_level) = installations.root().parent() { | ||
// Remove the `toolchains` symlink. | ||
match uv_fs::remove_symlink(top_level.join("toolchains")) { | ||
Ok(()) => {} | ||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {} | ||
Err(err) => return Err(err.into()), | ||
} | ||
|
||
if uv_fs::directories(top_level).all(|path| uv_fs::is_temporary(&path)) { | ||
fs_err::tokio::remove_dir_all(top_level).await?; | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a little bit more manual than I'd like. |
||
|
||
Ok(ExitStatus::Success) | ||
} | ||
|
||
/// Perform the uninstallation of managed Python installations. | ||
async fn do_uninstall( | ||
installations: &ManagedPythonInstallations, | ||
targets: Vec<String>, | ||
all: bool, | ||
printer: Printer, | ||
) -> Result<ExitStatus> { | ||
let start = std::time::Instant::now(); | ||
|
||
let requests = if all { | ||
vec![PythonRequest::Any] | ||
} else { | ||
|
@@ -108,6 +139,7 @@ pub(crate) async fn uninstall( | |
} | ||
} | ||
|
||
// Report on any uninstalled installations. | ||
if !uninstalled.is_empty() { | ||
if let [uninstalled] = uninstalled.as_slice() { | ||
// Ex) "Uninstalled Python 3.9.7 in 1.68s" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,28 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re | |
Err(err) => return Err(err.into()), | ||
}; | ||
|
||
// Perform the uninstallation. | ||
do_uninstall(&installed_tools, name, printer).await?; | ||
|
||
// Clean up any empty directories. | ||
if uv_fs::directories(installed_tools.root()).all(|path| uv_fs::is_temporary(&path)) { | ||
fs_err::tokio::remove_dir_all(&installed_tools.root()).await?; | ||
if let Some(top_level) = installed_tools.root().parent() { | ||
if uv_fs::directories(top_level).all(|path| uv_fs::is_temporary(&path)) { | ||
fs_err::tokio::remove_dir_all(top_level).await?; | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We clean up the directories, if empty, such that if you |
||
|
||
Ok(ExitStatus::Success) | ||
} | ||
|
||
/// Perform the uninstallation. | ||
async fn do_uninstall( | ||
installed_tools: &InstalledTools, | ||
name: Option<PackageName>, | ||
printer: Printer, | ||
) -> Result<()> { | ||
let mut dangling = false; | ||
let mut entrypoints = if let Some(name) = name { | ||
let Some(receipt) = installed_tools.get_tool_receipt(&name)? else { | ||
|
@@ -37,7 +59,7 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re | |
printer.stderr(), | ||
"Removed dangling environment for `{name}`" | ||
)?; | ||
return Ok(ExitStatus::Success); | ||
return Ok(()); | ||
} | ||
Err(uv_tool::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => { | ||
bail!("`{name}` is not installed"); | ||
|
@@ -48,7 +70,7 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re | |
} | ||
}; | ||
|
||
uninstall_tool(&name, &receipt, &installed_tools).await? | ||
uninstall_tool(&name, &receipt, installed_tools).await? | ||
} else { | ||
let mut entrypoints = vec![]; | ||
for (name, receipt) in installed_tools.tools()? { | ||
|
@@ -72,7 +94,7 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re | |
} | ||
}; | ||
|
||
entrypoints.extend(uninstall_tool(&name, &receipt, &installed_tools).await?); | ||
entrypoints.extend(uninstall_tool(&name, &receipt, installed_tools).await?); | ||
} | ||
entrypoints | ||
}; | ||
|
@@ -83,7 +105,7 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re | |
if !dangling { | ||
writeln!(printer.stderr(), "Nothing to uninstall")?; | ||
} | ||
return Ok(ExitStatus::Success); | ||
return Ok(()); | ||
} | ||
|
||
let s = if entrypoints.len() == 1 { "" } else { "s" }; | ||
|
@@ -97,7 +119,7 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re | |
.join(", ") | ||
)?; | ||
|
||
Ok(ExitStatus::Success) | ||
Ok(()) | ||
} | ||
|
||
/// Uninstall a tool. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a comment about existing only for backwards compatibility