Display path separators as backslashes on Windows (#11667)

Currently, `uv tool list --show-paths` will show backslashes as path
separators for packages but not entrypoints. This PR changes this to be
consistent.

Closes #10426.
This commit is contained in:
John Mumm 2025-02-20 22:28:38 +01:00 committed by GitHub
parent 5f6529a69a
commit 88f0dfd03d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 88 additions and 7 deletions

View File

@ -1,3 +1,4 @@
use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use serde::Deserialize;
@ -6,7 +7,7 @@ use toml_edit::Table;
use toml_edit::Value;
use toml_edit::{Array, Item};
use uv_fs::PortablePath;
use uv_fs::{PortablePath, Simplified};
use uv_pypi_types::{Requirement, VerbatimParsedUrl};
use uv_settings::ToolOptions;
@ -98,6 +99,32 @@ pub struct ToolEntrypoint {
pub install_path: PathBuf,
}
impl Display for ToolEntrypoint {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
#[cfg(windows)]
{
write!(
f,
"{} ({})",
self.name,
self.install_path
.simplified_display()
.to_string()
.replace('/', "\\")
)
}
#[cfg(unix)]
{
write!(
f,
"{} ({})",
self.name,
self.install_path.simplified_display()
)
}
}
}
/// Format an array so that each element is on its own line and has a trailing comma.
///
/// Example:

View File

@ -98,12 +98,7 @@ pub(crate) async fn list(
// Output tool entrypoints
for entrypoint in tool.entrypoints() {
if show_paths {
writeln!(
printer.stdout(),
"- {} ({})",
entrypoint.name,
entrypoint.install_path.simplified_display().cyan()
)?;
writeln!(printer.stdout(), "- {}", entrypoint.to_string().cyan())?;
} else {
writeln!(printer.stdout(), "- {}", entrypoint.name)?;
}

View File

@ -250,6 +250,19 @@ impl TestContext {
self
}
/// Add a filter that ignores temporary directory in path.
pub fn with_filtered_windows_temp_dir(mut self) -> Self {
let pattern = regex::escape(
&self
.temp_dir
.simplified_display()
.to_string()
.replace('/', "\\"),
);
self.filters.push((pattern, "[TEMP_DIR]".to_string()));
self
}
/// Add extra directories and configuration for managed Python installations.
#[must_use]
pub fn with_managed_python_dirs(mut self) -> Self {
@ -267,6 +280,12 @@ impl TestContext {
self
}
/// Clear filters on `TestContext`.
pub fn clear_filters(mut self) -> Self {
self.filters.clear();
self
}
/// Discover the path to the XDG state directory. We use this, rather than the OS-specific
/// temporary directory, because on macOS (and Windows on GitHub Actions), they involve
/// symlinks. (On macOS, the temporary directory is, like `/var/...`, which resolves to
@ -988,6 +1007,14 @@ impl TestContext {
.collect()
}
/// Only the filters added to this test context.
pub fn filters_without_standard_filters(&self) -> Vec<(&str, &str)> {
self.filters
.iter()
.map(|(p, r)| (p.as_str(), r.as_str()))
.collect()
}
/// For when we add pypy to the test suite.
#[allow(clippy::unused_self)]
pub fn python_kind(&self) -> &'static str {

View File

@ -64,6 +64,38 @@ fn tool_list_paths() {
"###);
}
#[cfg(windows)]
#[test]
fn tool_list_paths_windows() {
let context = TestContext::new("3.12")
.clear_filters()
.with_filtered_windows_temp_dir();
let tool_dir = context.temp_dir.child("tools");
let bin_dir = context.temp_dir.child("bin");
// Install `black`
context
.tool_install()
.arg("black==24.2.0")
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
.assert()
.success();
uv_snapshot!(context.filters_without_standard_filters(), context.tool_list().arg("--show-paths")
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
success: true
exit_code: 0
----- stdout -----
black v24.2.0 ([TEMP_DIR]\tools\black)
- black.exe ([TEMP_DIR]\bin\black.exe)
- blackd.exe ([TEMP_DIR]\bin\blackd.exe)
----- stderr -----
"###);
}
#[test]
fn tool_list_empty() {
let context = TestContext::new("3.12").with_filtered_exe_suffix();