From 1344cfae4b092a00667775f0ece9f05c5652ec81 Mon Sep 17 00:00:00 2001 From: konsti Date: Mon, 29 Apr 2024 16:33:10 +0200 Subject: [PATCH] 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 --- Cargo.lock | 3 +-- crates/uv-cache/Cargo.toml | 1 - crates/uv-cache/src/lib.rs | 2 +- crates/uv-fs/Cargo.toml | 1 + crates/uv-fs/src/cachedir.rs | 42 ++++++++++++++++++++++++++++++++ crates/uv-fs/src/lib.rs | 1 + crates/uv-virtualenv/Cargo.toml | 1 - crates/uv-virtualenv/src/bare.rs | 2 +- crates/uv/src/commands/venv.rs | 1 - crates/uv/tests/venv.rs | 31 +++++++++++++++++++++++ 10 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 crates/uv-fs/src/cachedir.rs diff --git a/Cargo.lock b/Cargo.lock index b0b14d427..519885d01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/crates/uv-cache/Cargo.toml b/crates/uv-cache/Cargo.toml index 2b7a48cac..eaa77fce3 100644 --- a/crates/uv-cache/Cargo.toml +++ b/crates/uv-cache/Cargo.toml @@ -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"] } diff --git a/crates/uv-cache/src/lib.rs b/crates/uv-cache/src/lib.rs index 98dd5f709..871582c55 100644 --- a/crates/uv-cache/src/lib.rs +++ b/crates/uv-cache/src/lib.rs @@ -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; diff --git a/crates/uv-fs/Cargo.toml b/crates/uv-fs/Cargo.toml index 393b8771b..a3a064708 100644 --- a/crates/uv-fs/Cargo.toml +++ b/crates/uv-fs/Cargo.toml @@ -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 } diff --git a/crates/uv-fs/src/cachedir.rs b/crates/uv-fs/src/cachedir.rs new file mode 100644 index 000000000..e19625d89 --- /dev/null +++ b/crates/uv-fs/src/cachedir.rs @@ -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>(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>(directory: P) -> io::Result<()> { + match add_tag(directory) { + Err(e) => match e.kind() { + io::ErrorKind::AlreadyExists => Ok(()), + _ => Err(e), + }, + other => other, + } +} diff --git a/crates/uv-fs/src/lib.rs b/crates/uv-fs/src/lib.rs index e7d44a882..bebe91b20 100644 --- a/crates/uv-fs/src/lib.rs +++ b/crates/uv-fs/src/lib.rs @@ -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. diff --git a/crates/uv-virtualenv/Cargo.toml b/crates/uv-virtualenv/Cargo.toml index 7fa1512f2..c3c94b6f0 100644 --- a/crates/uv-virtualenv/Cargo.toml +++ b/crates/uv-virtualenv/Cargo.toml @@ -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 } diff --git a/crates/uv-virtualenv/src/bare.rs b/crates/uv-virtualenv/src/bare.rs index 2ee30baf8..3dc7dd917 100644 --- a/crates/uv-virtualenv/src/bare.rs +++ b/crates/uv-virtualenv/src/bare.rs @@ -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; diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index f96df2b26..b8daef1d9 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -5,7 +5,6 @@ use std::vec; use anstream::eprint; use anyhow::Result; - use itertools::Itertools; use miette::{Diagnostic, IntoDiagnostic}; use owo_colors::OwoColorize; diff --git a/crates/uv/tests/venv.rs b/crates/uv/tests/venv.rs index 26e098300..aab95a699 100644 --- a/crates/uv/tests/venv.rs +++ b/crates/uv/tests/venv.rs @@ -527,3 +527,34 @@ fn verify_nested_pyvenv_cfg() -> Result<()> { Ok(()) } + +/// See +#[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) + "### + ); +}