diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 5ff82a219..71bb1eb49 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -4250,11 +4250,11 @@ pub struct ToolInstallArgs { #[arg(long, hide = true)] pub from: Option, - /// Include the following extra requirements. + /// Include the following additional requirements. #[arg(long)] pub with: Vec, - /// Run all requirements listed in the given `requirements.txt` files. + /// Include all requirements listed in the given `requirements.txt` files. #[arg(long, value_delimiter = ',', value_parser = parse_maybe_file_path)] pub with_requirements: Vec>, @@ -4338,6 +4338,10 @@ pub struct ToolListArgs { #[arg(long)] pub show_version_specifiers: bool, + /// Whether to display the additional requirements installed with each tool. + #[arg(long)] + pub show_with: bool, + // Hide unused global Python options. #[arg(long, hide = true)] pub python_preference: Option, diff --git a/crates/uv/src/commands/tool/list.rs b/crates/uv/src/commands/tool/list.rs index 3af75ae5c..4def9cfe4 100644 --- a/crates/uv/src/commands/tool/list.rs +++ b/crates/uv/src/commands/tool/list.rs @@ -16,6 +16,7 @@ use crate::printer::Printer; pub(crate) async fn list( show_paths: bool, show_version_specifiers: bool, + show_with: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -63,35 +64,50 @@ pub(crate) async fn list( } }; - let version_specifier = if show_version_specifiers { - let specifiers = tool - .requirements() - .iter() - .filter(|req| req.name == name) - .map(|req| req.source.to_string()) - .filter(|s| !s.is_empty()) - .join(", "); - if specifiers.is_empty() { - String::new() - } else { + let version_specifier = show_version_specifiers + .then(|| { + tool.requirements() + .iter() + .filter(|req| req.name == name) + .map(|req| req.source.to_string()) + .filter(|s| !s.is_empty()) + .peekable() + }) + .take_if(|specifiers| specifiers.peek().is_some()) + .map(|mut specifiers| { + let specifiers = specifiers.join(", "); format!(" [required: {specifiers}]") - } - } else { - String::new() - }; + }) + .unwrap_or_default(); + + let with_requirements = show_with + .then(|| { + tool.requirements() + .iter() + .filter(|req| req.name != name) + .peekable() + }) + .take_if(|requirements| requirements.peek().is_some()) + .map(|requirements| { + let requirements = requirements + .map(|req| format!("{}{}", req.name, req.source)) + .join(", "); + format!(" [with: {requirements}]") + }) + .unwrap_or_default(); if show_paths { writeln!( printer.stdout(), "{} ({})", - format!("{name} v{version}{version_specifier}").bold(), + format!("{name} v{version}{version_specifier}{with_requirements}").bold(), installed_tools.tool_dir(&name).simplified_display().cyan(), )?; } else { writeln!( printer.stdout(), "{}", - format!("{name} v{version}{version_specifier}").bold() + format!("{name} v{version}{version_specifier}{with_requirements}").bold() )?; } diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index be406ce82..48b502290 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -1259,6 +1259,7 @@ async fn run(mut cli: Cli) -> Result { commands::tool_list( args.show_paths, args.show_version_specifiers, + args.show_with, &cache, printer, ) diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 162826826..245129034 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -772,6 +772,7 @@ impl ToolUpgradeSettings { pub(crate) struct ToolListSettings { pub(crate) show_paths: bool, pub(crate) show_version_specifiers: bool, + pub(crate) show_with: bool, } impl ToolListSettings { @@ -781,6 +782,7 @@ impl ToolListSettings { let ToolListArgs { show_paths, show_version_specifiers, + show_with, python_preference: _, no_python_downloads: _, } = args; @@ -788,6 +790,7 @@ impl ToolListSettings { Self { show_paths, show_version_specifiers, + show_with, } } } diff --git a/crates/uv/tests/it/tool_install.rs b/crates/uv/tests/it/tool_install.rs index 5733df92d..9b32bcc26 100644 --- a/crates/uv/tests/it/tool_install.rs +++ b/crates/uv/tests/it/tool_install.rs @@ -2021,7 +2021,7 @@ fn tool_install_unnamed_with() { "###); } -/// Test installing a tool with extra requirements from a `requirements.txt` file. +/// Test installing a tool with additional requirements from a `requirements.txt` file. #[test] fn tool_install_requirements_txt() { let context = TestContext::new("3.12") diff --git a/crates/uv/tests/it/tool_list.rs b/crates/uv/tests/it/tool_list.rs index 3326bb7be..7bc00116d 100644 --- a/crates/uv/tests/it/tool_list.rs +++ b/crates/uv/tests/it/tool_list.rs @@ -337,3 +337,118 @@ fn tool_list_show_version_specifiers() { ----- stderr ----- "###); } + +#[test] +fn tool_list_show_with() { + let context = TestContext::new("3.12").with_filtered_exe_suffix(); + let tool_dir = context.temp_dir.child("tools"); + let bin_dir = context.temp_dir.child("bin"); + + // Install `black` without additional requirements + 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(); + + // Install `flask` with additional requirements + context + .tool_install() + .arg("flask") + .arg("--with") + .arg("requests") + .arg("--with") + .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(); + + // Install `ruff` with version specifier and additional requirements + context + .tool_install() + .arg("ruff==0.3.4") + .arg("--with") + .arg("requests") + .env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str()) + .env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()) + .assert() + .success(); + + // Test with --show-with + uv_snapshot!(context.filters(), context.tool_list().arg("--show-with") + .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 + - black + - blackd + flask v3.0.2 [with: requests, black==24.2.0] + - flask + ruff v0.3.4 [with: requests] + - ruff + + ----- stderr ----- + "###); + + // Test with both --show-with and --show-paths + uv_snapshot!(context.filters(), context.tool_list().arg("--show-with").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 ([TEMP_DIR]/bin/black) + - blackd ([TEMP_DIR]/bin/blackd) + flask v3.0.2 [with: requests, black==24.2.0] ([TEMP_DIR]/tools/flask) + - flask ([TEMP_DIR]/bin/flask) + ruff v0.3.4 [with: requests] ([TEMP_DIR]/tools/ruff) + - ruff ([TEMP_DIR]/bin/ruff) + + ----- stderr ----- + "###); + + // Test with both --show-with and --show-version-specifiers + uv_snapshot!(context.filters(), context.tool_list().arg("--show-with").arg("--show-version-specifiers") + .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 [required: ==24.2.0] + - black + - blackd + flask v3.0.2 [with: requests, black==24.2.0] + - flask + ruff v0.3.4 [required: ==0.3.4] [with: requests] + - ruff + + ----- stderr ----- + "###); + + // Test with all flags + uv_snapshot!(context.filters(), context.tool_list() + .arg("--show-with") + .arg("--show-version-specifiers") + .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 [required: ==24.2.0] ([TEMP_DIR]/tools/black) + - black ([TEMP_DIR]/bin/black) + - blackd ([TEMP_DIR]/bin/blackd) + flask v3.0.2 [with: requests, black==24.2.0] ([TEMP_DIR]/tools/flask) + - flask ([TEMP_DIR]/bin/flask) + ruff v0.3.4 [required: ==0.3.4] [with: requests] ([TEMP_DIR]/tools/ruff) + - ruff ([TEMP_DIR]/bin/ruff) + + ----- stderr ----- + "###); +} diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 1c5bbeed7..1bdad2717 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -3759,11 +3759,11 @@ uv tool install [OPTIONS]

You can configure fine-grained logging using the RUST_LOG environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)

-
--with with

Include the following extra requirements

+
--with with

Include the following additional requirements

--with-editable with-editable

Include the given packages in editable mode

-
--with-requirements with-requirements

Run all requirements listed in the given requirements.txt files

+
--with-requirements with-requirements

Include all requirements listed in the given requirements.txt files

@@ -4176,6 +4176,8 @@ uv tool list [OPTIONS]
--show-version-specifiers

Whether to display the version specifier(s) used to install each tool

+
--show-with

Whether to display the additional requirements installed with each tool

+
--verbose, -v

Use verbose output.

You can configure fine-grained logging using the RUST_LOG environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)