Add `--show-with` to `uv tool list` to list packages included by `--with` (#13264)

## Summary

Add a `--show-extras` argument to the `uv tool list` cli, to show which
extra dependencies were installed with the tool.

i.e.

```bash
$ uv tool install fastapi --with requests --with typer==0.14
```

```bash
$ uv tool list --show-extras
fastapi v0.115.12 [extras: requests, typer==0.14]
- fastapi
```

## Test Plan

Added a new test function based on the others in the same file, with the
other arguments tested with the new argument as well.
This commit is contained in:
Tobias Gårdhus 2025-05-06 22:23:50 +02:00 committed by GitHub
parent 1ec1935693
commit 5e7f3d2920
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 163 additions and 22 deletions

View File

@ -4250,11 +4250,11 @@ pub struct ToolInstallArgs {
#[arg(long, hide = true)]
pub from: Option<String>,
/// Include the following extra requirements.
/// Include the following additional requirements.
#[arg(long)]
pub with: Vec<comma::CommaSeparatedRequirements>,
/// 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<Maybe<PathBuf>>,
@ -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<PythonPreference>,

View File

@ -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<ExitStatus> {
@ -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()
)?;
}

View File

@ -1259,6 +1259,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
commands::tool_list(
args.show_paths,
args.show_version_specifiers,
args.show_with,
&cache,
printer,
)

View File

@ -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,
}
}
}

View File

@ -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")

View File

@ -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 -----
"###);
}

View File

@ -3759,11 +3759,11 @@ uv tool install [OPTIONS] <PACKAGE>
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (&lt;https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives&gt;)</p>
</dd><dt id="uv-tool-install--with"><a href="#uv-tool-install--with"><code>--with</code></a> <i>with</i></dt><dd><p>Include the following extra requirements</p>
</dd><dt id="uv-tool-install--with"><a href="#uv-tool-install--with"><code>--with</code></a> <i>with</i></dt><dd><p>Include the following additional requirements</p>
</dd><dt id="uv-tool-install--with-editable"><a href="#uv-tool-install--with-editable"><code>--with-editable</code></a> <i>with-editable</i></dt><dd><p>Include the given packages in editable mode</p>
</dd><dt id="uv-tool-install--with-requirements"><a href="#uv-tool-install--with-requirements"><code>--with-requirements</code></a> <i>with-requirements</i></dt><dd><p>Run all requirements listed in the given <code>requirements.txt</code> files</p>
</dd><dt id="uv-tool-install--with-requirements"><a href="#uv-tool-install--with-requirements"><code>--with-requirements</code></a> <i>with-requirements</i></dt><dd><p>Include all requirements listed in the given <code>requirements.txt</code> files</p>
</dd></dl>
@ -4176,6 +4176,8 @@ uv tool list [OPTIONS]
</dd><dt id="uv-tool-list--show-version-specifiers"><a href="#uv-tool-list--show-version-specifiers"><code>--show-version-specifiers</code></a></dt><dd><p>Whether to display the version specifier(s) used to install each tool</p>
</dd><dt id="uv-tool-list--show-with"><a href="#uv-tool-list--show-with"><code>--show-with</code></a></dt><dd><p>Whether to display the additional requirements installed with each tool</p>
</dd><dt id="uv-tool-list--verbose"><a href="#uv-tool-list--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p>
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (&lt;https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives&gt;)</p>