Avoid removing local wheels when unzipping (#560)

## Summary

When installing a local wheel, we need to avoid removing the zipped
wheel (since it lives outside of the cache), _and_ need to ensure that
we unzip the wheel into the cache (rather than replacing the zipped
wheel, which may even live outside of the project).

Closes https://github.com/astral-sh/puffin/issues/553.
This commit is contained in:
Charlie Marsh 2023-12-05 12:50:08 -05:00 committed by GitHub
parent 6f055ecf3b
commit a15da36d74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 53 deletions

View file

@ -46,47 +46,55 @@ pub fn write_atomic_sync(path: impl AsRef<Path>, data: impl AsRef<[u8]>) -> std:
/// Rename `from` to `to` atomically using a temporary file and atomic rename.
///
/// Returns `false` if the `to` path already existed and thus was removed before performing the
/// Returns a [`Target`] if the `to` path already existed and thus was removed before performing the
/// rename.
pub fn rename_atomic_sync(from: impl AsRef<Path>, to: impl AsRef<Path>) -> std::io::Result<bool> {
// Remove the destination if it exists.
let safe = if let Ok(metadata) = fs_err::metadata(&to) {
if metadata.is_dir() {
pub fn rename_atomic_sync(
from: impl AsRef<Path>,
to: impl AsRef<Path>,
) -> std::io::Result<Option<Target>> {
// Remove the destination, if it exists.
let target = if let Ok(metadata) = fs_err::metadata(&to) {
Some(if metadata.is_dir() {
fs_err::remove_dir_all(&to)?;
Target::Directory
} else {
fs_err::remove_file(&to)?;
}
false
Target::File
})
} else {
true
None
};
// Move the source file to the destination.
fs_err::rename(from, to)?;
Ok(safe)
Ok(target)
}
/// Copy `from` to `to` atomically using a temporary file and atomic rename.
///
/// Returns `false` if the `to` path already existed and thus was removed before performing the
/// rename.
pub fn copy_atomic_sync(from: impl AsRef<Path>, to: impl AsRef<Path>) -> std::io::Result<bool> {
/// Returns a [`Target`] if the `to` path already existed and thus was removed before performing the
/// copy.
pub fn copy_atomic_sync(
from: impl AsRef<Path>,
to: impl AsRef<Path>,
) -> std::io::Result<Option<Target>> {
// Copy to a temporary file.
let temp_file =
NamedTempFile::new_in(to.as_ref().parent().expect("Write path must have a parent"))?;
fs_err::copy(from, &temp_file)?;
// Remove the destination if it exists.
let safe = if let Ok(metadata) = fs_err::metadata(&to) {
if metadata.is_dir() {
// Remove the destination, if it exists.
let target = if let Ok(metadata) = fs_err::metadata(&to) {
Some(if metadata.is_dir() {
fs_err::remove_dir_all(&to)?;
Target::Directory
} else {
fs_err::remove_file(&to)?;
}
false
Target::File
})
} else {
true
None
};
// Move the temporary file to the destination.
@ -101,5 +109,25 @@ pub fn copy_atomic_sync(from: impl AsRef<Path>, to: impl AsRef<Path>) -> std::io
)
})?;
Ok(safe)
Ok(target)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Target {
/// The target path was an existing file.
File,
/// The target path was an existing directory.
Directory,
/// The target path did not exist.
NotFound,
}
impl Target {
pub fn is_file(self) -> bool {
matches!(self, Self::File)
}
pub fn is_directory(self) -> bool {
matches!(self, Self::Directory)
}
}