Skip to content

Commit 38ce3b2

Browse files
F4RANEliteTK
andauthored
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>
1 parent a70ee58 commit 38ce3b2

2 files changed

Lines changed: 101 additions & 2 deletions

File tree

crates/uv/src/commands/tool/run.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,20 @@ impl Display for ToolRunCommand {
8080
}
8181
}
8282

83+
/// Check if the given arguments contain a verbose flag (e.g., `--verbose`, `-v`, `-vv`, etc.)
84+
fn find_verbose_flag(args: &[std::ffi::OsString]) -> Option<&str> {
85+
args.iter().find_map(|arg| {
86+
let arg_str = arg.to_str()?;
87+
if arg_str == "--verbose" {
88+
Some("--verbose")
89+
} else if arg_str.starts_with("-v") && arg_str.chars().skip(1).all(|c| c == 'v') {
90+
Some(arg_str)
91+
} else {
92+
None
93+
}
94+
})
95+
}
96+
8397
/// Run a command.
8498
#[allow(clippy::fn_params_excessive_bools)]
8599
pub(crate) async fn run(
@@ -309,11 +323,24 @@ pub(crate) async fn run(
309323
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
310324
}
311325

312-
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
313-
.with_context("tool")
326+
let diagnostic =
327+
diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls());
328+
let diagnostic = if let Some(verbose_flag) = find_verbose_flag(args) {
329+
diagnostic.with_hint(format!(
330+
"You provided `{}` to `{}`. Did you mean to provide it to `{}`? e.g., `{}`",
331+
verbose_flag.cyan(),
332+
target.cyan(),
333+
invocation_source.to_string().cyan(),
334+
format!("{invocation_source} {verbose_flag} {target}").green()
335+
))
336+
} else {
337+
diagnostic.with_context("tool")
338+
};
339+
return diagnostic
314340
.report(err)
315341
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
316342
}
343+
317344
Err(ProjectError::Requirements(err)) => {
318345
let err = miette::Report::msg(format!("{err}"))
319346
.context("Failed to resolve `--with` requirement");

crates/uv/tests/it/tool_run.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2820,6 +2820,78 @@ fn tool_run_with_script_and_from_script() {
28202820
");
28212821
}
28222822

2823+
/// Test that when a user provides `--verbose` to the subcommand,
2824+
/// we show a helpful hint.
2825+
#[test]
2826+
fn tool_run_verbose_hint() {
2827+
let context = TestContext::new("3.12").with_filtered_counts();
2828+
let tool_dir = context.temp_dir.child("tools");
2829+
let bin_dir = context.temp_dir.child("bin");
2830+
2831+
// Test with --verbose flag
2832+
uv_snapshot!(context.filters(), context.tool_run()
2833+
.arg("nonexistent-package-foo")
2834+
.arg("--verbose")
2835+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
2836+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
2837+
success: false
2838+
exit_code: 1
2839+
----- stdout -----
2840+
2841+
----- stderr -----
2842+
× No solution found when resolving dependencies:
2843+
╰─▶ 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.
2844+
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`
2845+
"###);
2846+
2847+
// Test with -v flag
2848+
uv_snapshot!(context.filters(), context.tool_run()
2849+
.arg("nonexistent-package-bar")
2850+
.arg("-v")
2851+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
2852+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
2853+
success: false
2854+
exit_code: 1
2855+
----- stdout -----
2856+
2857+
----- stderr -----
2858+
× No solution found when resolving dependencies:
2859+
╰─▶ 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.
2860+
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`
2861+
"###);
2862+
2863+
// Test with -vv flag
2864+
uv_snapshot!(context.filters(), context.tool_run()
2865+
.arg("nonexistent-package-baz")
2866+
.arg("-vv")
2867+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
2868+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
2869+
success: false
2870+
exit_code: 1
2871+
----- stdout -----
2872+
2873+
----- stderr -----
2874+
× No solution found when resolving dependencies:
2875+
╰─▶ 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.
2876+
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`
2877+
"###);
2878+
2879+
// Test for false positives
2880+
uv_snapshot!(context.filters(), context.tool_run()
2881+
.arg("nonexistent-package-quux")
2882+
.arg("-version")
2883+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
2884+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r"
2885+
success: false
2886+
exit_code: 1
2887+
----- stdout -----
2888+
2889+
----- stderr -----
2890+
× No solution found when resolving tool dependencies:
2891+
╰─▶ 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.
2892+
");
2893+
}
2894+
28232895
#[test]
28242896
fn tool_run_with_compatible_build_constraints() -> Result<()> {
28252897
let context = TestContext::new("3.9")

0 commit comments

Comments
 (0)