mirror of https://github.com/astral-sh/uv
Add hint for misplaced `--verbose` in `uv tool run` (#17020)
Resolves #16777 ## Summary When a command fails, users sometimes add --verbose after the package name (e.g., uvx foo --verbose) instead of before it (e.g., uvx --verbose foo). This adds a hint that suggests moving --verbose before the command. The hint appears when a verbose flag is detected in the subcommand arguments and the command fails to resolve. It works for both uvx and uv tool run. ## Test Plan Tested by running: uvx foo-does-not-exist --verbose - shows the hint uv tool run foo-does-not-exist --verbose - shows the hint The hint only appears when verbose flags are detected, and the message shows the correct command format. ## Screenshot <img width="920" height="34" alt="image" src="https://github.com/user-attachments/assets/f6c303f6-b5e6-441f-8d8d-9f5e6ab87c87" /> Open to feedback and happy to make changes as needed! 💯 --------- Co-authored-by: Tomasz (Tom) Kramkowski <tom@astral.sh>
This commit is contained in:
parent
a70ee58ae1
commit
38ce3b2919
|
|
@ -80,6 +80,20 @@ impl Display for ToolRunCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the given arguments contain a verbose flag (e.g., `--verbose`, `-v`, `-vv`, etc.)
|
||||||
|
fn find_verbose_flag(args: &[std::ffi::OsString]) -> Option<&str> {
|
||||||
|
args.iter().find_map(|arg| {
|
||||||
|
let arg_str = arg.to_str()?;
|
||||||
|
if arg_str == "--verbose" {
|
||||||
|
Some("--verbose")
|
||||||
|
} else if arg_str.starts_with("-v") && arg_str.chars().skip(1).all(|c| c == 'v') {
|
||||||
|
Some(arg_str)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Run a command.
|
/// Run a command.
|
||||||
#[allow(clippy::fn_params_excessive_bools)]
|
#[allow(clippy::fn_params_excessive_bools)]
|
||||||
pub(crate) async fn run(
|
pub(crate) async fn run(
|
||||||
|
|
@ -309,11 +323,24 @@ pub(crate) async fn run(
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
let diagnostic =
|
||||||
.with_context("tool")
|
diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls());
|
||||||
|
let diagnostic = if let Some(verbose_flag) = find_verbose_flag(args) {
|
||||||
|
diagnostic.with_hint(format!(
|
||||||
|
"You provided `{}` to `{}`. Did you mean to provide it to `{}`? e.g., `{}`",
|
||||||
|
verbose_flag.cyan(),
|
||||||
|
target.cyan(),
|
||||||
|
invocation_source.to_string().cyan(),
|
||||||
|
format!("{invocation_source} {verbose_flag} {target}").green()
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
diagnostic.with_context("tool")
|
||||||
|
};
|
||||||
|
return diagnostic
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ProjectError::Requirements(err)) => {
|
Err(ProjectError::Requirements(err)) => {
|
||||||
let err = miette::Report::msg(format!("{err}"))
|
let err = miette::Report::msg(format!("{err}"))
|
||||||
.context("Failed to resolve `--with` requirement");
|
.context("Failed to resolve `--with` requirement");
|
||||||
|
|
|
||||||
|
|
@ -2820,6 +2820,78 @@ fn tool_run_with_script_and_from_script() {
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test that when a user provides `--verbose` to the subcommand,
|
||||||
|
/// we show a helpful hint.
|
||||||
|
#[test]
|
||||||
|
fn tool_run_verbose_hint() {
|
||||||
|
let context = TestContext::new("3.12").with_filtered_counts();
|
||||||
|
let tool_dir = context.temp_dir.child("tools");
|
||||||
|
let bin_dir = context.temp_dir.child("bin");
|
||||||
|
|
||||||
|
// Test with --verbose flag
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("nonexistent-package-foo")
|
||||||
|
.arg("--verbose")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
× No solution found when resolving dependencies:
|
||||||
|
╰─▶ Because nonexistent-package-foo was not found in the package registry and you require nonexistent-package-foo, we can conclude that your requirements are unsatisfiable.
|
||||||
|
help: You provided `--verbose` to `nonexistent-package-foo`. Did you mean to provide it to `uv tool run`? e.g., `uv tool run --verbose nonexistent-package-foo`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test with -v flag
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("nonexistent-package-bar")
|
||||||
|
.arg("-v")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
× No solution found when resolving dependencies:
|
||||||
|
╰─▶ Because nonexistent-package-bar was not found in the package registry and you require nonexistent-package-bar, we can conclude that your requirements are unsatisfiable.
|
||||||
|
help: You provided `-v` to `nonexistent-package-bar`. Did you mean to provide it to `uv tool run`? e.g., `uv tool run -v nonexistent-package-bar`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test with -vv flag
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("nonexistent-package-baz")
|
||||||
|
.arg("-vv")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
× No solution found when resolving dependencies:
|
||||||
|
╰─▶ Because nonexistent-package-baz was not found in the package registry and you require nonexistent-package-baz, we can conclude that your requirements are unsatisfiable.
|
||||||
|
help: You provided `-vv` to `nonexistent-package-baz`. Did you mean to provide it to `uv tool run`? e.g., `uv tool run -vv nonexistent-package-baz`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test for false positives
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("nonexistent-package-quux")
|
||||||
|
.arg("-version")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
× No solution found when resolving tool dependencies:
|
||||||
|
╰─▶ Because nonexistent-package-quux was not found in the package registry and you require nonexistent-package-quux, we can conclude that your requirements are unsatisfiable.
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tool_run_with_compatible_build_constraints() -> Result<()> {
|
fn tool_run_with_compatible_build_constraints() -> Result<()> {
|
||||||
let context = TestContext::new("3.9")
|
let context = TestContext::new("3.9")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue