Use fs_err for cachedir errors (#3304)

When running

```
set UV_CACHE_DIR=%LOCALAPPDATA%\uv\cache-foo && uv venv venv
```

in windows CMD, the error would be just

```
error: The system cannot find the path specified. (os error 3)
```

The problem is that the first action in the cache dir is adding the tag,
and the `cachedir` crate is using `std::fs` instead of `fs_err`. I've
copied the two functions we use from the crate and changed the import
from `std::fs` to `fs_err`.

The new error is

```
error: failed to open file `C:\Users\Konstantin\AppData\Local\uv\cache-foo \CACHEDIR.TAG`
  Caused by: The system cannot find the path specified. (os error 3)
```

which correctly explains the problem.

Closes #3280
This commit is contained in:
konsti 2024-04-29 16:33:10 +02:00 committed by GitHub
parent 22d8619c37
commit 1344cfae4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 78 additions and 7 deletions

3
Cargo.lock generated
View file

@ -4540,7 +4540,6 @@ name = "uv-cache"
version = "0.0.1"
dependencies = [
"cache-key",
"cachedir",
"clap",
"directories",
"distribution-types",
@ -4761,6 +4760,7 @@ name = "uv-fs"
version = "0.0.1"
dependencies = [
"backoff",
"cachedir",
"dunce",
"encoding_rs_io",
"fs-err",
@ -5011,7 +5011,6 @@ name = "uv-virtualenv"
version = "0.0.4"
dependencies = [
"anstream",
"cachedir",
"clap",
"directories",
"fs-err",

View file

@ -20,7 +20,6 @@ pypi-types = { workspace = true }
uv-fs = { workspace = true, features = ["tokio"] }
uv-normalize = { workspace = true }
cachedir = { workspace = true }
clap = { workspace = true, features = ["derive", "env"], optional = true }
directories = { workspace = true }
fs-err = { workspace = true, features = ["tokio"] }

View file

@ -13,7 +13,7 @@ use tracing::debug;
use distribution_types::InstalledDist;
use pypi_types::Metadata23;
use uv_fs::directories;
use uv_fs::{cachedir, directories};
use uv_normalize::PackageName;
pub use crate::by_timestamp::CachedByTimestamp;

View file

@ -16,6 +16,7 @@ workspace = true
uv-warnings = { workspace = true }
backoff = { workspace = true }
cachedir = { workspace = true }
dunce = { workspace = true }
encoding_rs_io = { workspace = true }
fs-err = { workspace = true }

View file

@ -0,0 +1,42 @@
//! Vendored from cachedir 0.3.1 to replace `std::fs` with `fs_err`.
use std::io::Write;
use std::{io, path};
use cachedir::HEADER;
/// Adds a tag to the specified `directory`.
///
/// Will return an error if:
///
/// * The `directory` exists and contains a `CACHEDIR.TAG` file, regardless of its content.
/// * The file can't be created for any reason (the `directory` doesn't exist, permission error,
/// can't write to the file etc.)
pub fn add_tag<P: AsRef<path::Path>>(directory: P) -> io::Result<()> {
let directory = directory.as_ref();
match fs_err::OpenOptions::new()
.write(true)
.create_new(true)
.open(directory.join("CACHEDIR.TAG"))
{
Ok(mut cachedir_tag) => cachedir_tag.write_all(HEADER),
Err(e) => Err(e),
}
}
/// Ensures the tag exists in `directory`.
///
/// This function considers the `CACHEDIR.TAG` file in `directory` existing, regardless of its
/// content, as a success.
///
/// Will return an error if The tag file doesn't exist and can't be created for any reason
/// (the `directory` doesn't exist, permission error, can't write to the file etc.).
pub fn ensure_tag<P: AsRef<path::Path>>(directory: P) -> io::Result<()> {
match add_tag(directory) {
Err(e) => match e.kind() {
io::ErrorKind::AlreadyExists => Ok(()),
_ => Err(e),
},
other => other,
}
}

View file

@ -10,6 +10,7 @@ use uv_warnings::warn_user;
pub use crate::path::*;
pub mod cachedir;
mod path;
/// Reads data from the path and requires that it be valid UTF-8.

View file

@ -29,7 +29,6 @@ uv-interpreter = { workspace = true }
uv-version = { workspace = true }
anstream = { workspace = true }
cachedir = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
directories = { workspace = true }
fs-err = { workspace = true }

View file

@ -12,7 +12,7 @@ use pypi_types::Scheme;
use tracing::info;
use crate::{Error, Prompt};
use uv_fs::Simplified;
use uv_fs::{cachedir, Simplified};
use uv_interpreter::{Interpreter, Virtualenv};
use uv_version::version;

View file

@ -5,7 +5,6 @@ use std::vec;
use anstream::eprint;
use anyhow::Result;
use itertools::Itertools;
use miette::{Diagnostic, IntoDiagnostic};
use owo_colors::OwoColorize;

View file

@ -527,3 +527,34 @@ fn verify_nested_pyvenv_cfg() -> Result<()> {
Ok(())
}
/// See <https://github.com/astral-sh/uv/issues/3280>
#[test]
#[cfg(windows)]
fn path_with_trailing_space_gives_proper_error() {
let context = VenvTestContext::new(&["3.12"]);
let mut filters = context.filters();
filters.push((
regex::escape(&context.cache_dir.path().display().to_string()).to_string(),
r"C:\Path\to\Cache\dir".to_string(),
));
// Create a virtual environment at `.venv`.
uv_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(context.venv.as_os_str())
.arg("--python")
.arg("3.12")
.env("UV_CACHE_DIR", format!("{} ", context.cache_dir.path().display()))
.env("UV_TEST_PYTHON_PATH", context.python_path.clone())
.current_dir(context.temp_dir.path()), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: failed to open file `C:\Path\to\Cache\dir \CACHEDIR.TAG`
Caused by: The system cannot find the path specified. (os error 3)
"###
);
}