Skip to content

Commit 459c902

Browse files
authored
add --show-extras to uv tool list to list extra requirements installed with tools (#13783)
## Summary Implemented as suggested in #13761 eg. ``` $ uv tool install 'harlequin[postgres]' $ uv tool list --show-extras harlequin v2.1.2 [extras: postgres] - harlequin ``` ## Test Plan Added a new test with the argument along with the others from the `uv tool list` cli.
1 parent 5400434 commit 459c902

File tree

6 files changed

+145
-2
lines changed

6 files changed

+145
-2
lines changed

crates/uv-cli/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4419,6 +4419,10 @@ pub struct ToolListArgs {
44194419
#[arg(long)]
44204420
pub show_with: bool,
44214421

4422+
/// Whether to display the extra requirements installed with each tool.
4423+
#[arg(long)]
4424+
pub show_extras: bool,
4425+
44224426
// Hide unused global Python options.
44234427
#[arg(long, hide = true)]
44244428
pub python_preference: Option<PythonPreference>,

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ use crate::commands::ExitStatus;
1313
use crate::printer::Printer;
1414

1515
/// List installed tools.
16+
#[allow(clippy::fn_params_excessive_bools)]
1617
pub(crate) async fn list(
1718
show_paths: bool,
1819
show_version_specifiers: bool,
1920
show_with: bool,
21+
show_extras: bool,
2022
cache: &Cache,
2123
printer: Printer,
2224
) -> Result<ExitStatus> {
@@ -80,6 +82,21 @@ pub(crate) async fn list(
8082
})
8183
.unwrap_or_default();
8284

85+
let extra_requirements = show_extras
86+
.then(|| {
87+
tool.requirements()
88+
.iter()
89+
.filter(|req| req.name == name)
90+
.flat_map(|req| req.extras.iter()) // Flatten the extras from all matching requirements
91+
.peekable()
92+
})
93+
.take_if(|extras| extras.peek().is_some())
94+
.map(|extras| {
95+
let extras_str = extras.map(ToString::to_string).join(", ");
96+
format!(" [extras: {extras_str}]")
97+
})
98+
.unwrap_or_default();
99+
83100
let with_requirements = show_with
84101
.then(|| {
85102
tool.requirements()
@@ -100,14 +117,20 @@ pub(crate) async fn list(
100117
writeln!(
101118
printer.stdout(),
102119
"{} ({})",
103-
format!("{name} v{version}{version_specifier}{with_requirements}").bold(),
120+
format!(
121+
"{name} v{version}{version_specifier}{extra_requirements}{with_requirements}"
122+
)
123+
.bold(),
104124
installed_tools.tool_dir(&name).simplified_display().cyan(),
105125
)?;
106126
} else {
107127
writeln!(
108128
printer.stdout(),
109129
"{}",
110-
format!("{name} v{version}{version_specifier}{with_requirements}").bold()
130+
format!(
131+
"{name} v{version}{version_specifier}{extra_requirements}{with_requirements}"
132+
)
133+
.bold()
111134
)?;
112135
}
113136

crates/uv/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
12891289
args.show_paths,
12901290
args.show_version_specifiers,
12911291
args.show_with,
1292+
args.show_extras,
12921293
&cache,
12931294
printer,
12941295
)

crates/uv/src/settings.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,7 @@ pub(crate) struct ToolListSettings {
782782
pub(crate) show_paths: bool,
783783
pub(crate) show_version_specifiers: bool,
784784
pub(crate) show_with: bool,
785+
pub(crate) show_extras: bool,
785786
}
786787

787788
impl ToolListSettings {
@@ -792,6 +793,7 @@ impl ToolListSettings {
792793
show_paths,
793794
show_version_specifiers,
794795
show_with,
796+
show_extras,
795797
python_preference: _,
796798
no_python_downloads: _,
797799
} = args;
@@ -800,6 +802,7 @@ impl ToolListSettings {
800802
show_paths,
801803
show_version_specifiers,
802804
show_with,
805+
show_extras,
803806
}
804807
}
805808
}

crates/uv/tests/it/tool_list.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,114 @@ fn tool_list_show_with() {
452452
----- stderr -----
453453
"###);
454454
}
455+
456+
#[test]
457+
fn tool_list_show_extras() {
458+
let context = TestContext::new("3.12").with_filtered_exe_suffix();
459+
let tool_dir = context.temp_dir.child("tools");
460+
let bin_dir = context.temp_dir.child("bin");
461+
462+
// Install `black` without extras
463+
context
464+
.tool_install()
465+
.arg("black==24.2.0")
466+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
467+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
468+
.assert()
469+
.success();
470+
471+
// Install `flask` with extras and additional requirements
472+
context
473+
.tool_install()
474+
.arg("flask[async,dotenv]")
475+
.arg("--with")
476+
.arg("requests")
477+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
478+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
479+
.assert()
480+
.success();
481+
482+
// Test with --show-extras only
483+
uv_snapshot!(context.filters(), context.tool_list().arg("--show-extras")
484+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
485+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
486+
success: true
487+
exit_code: 0
488+
----- stdout -----
489+
black v24.2.0
490+
- black
491+
- blackd
492+
flask v3.0.2 [extras: async, dotenv]
493+
- flask
494+
495+
----- stderr -----
496+
"###);
497+
498+
// Test with both --show-extras and --show-with
499+
uv_snapshot!(context.filters(), context.tool_list().arg("--show-extras").arg("--show-with")
500+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
501+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
502+
success: true
503+
exit_code: 0
504+
----- stdout -----
505+
black v24.2.0
506+
- black
507+
- blackd
508+
flask v3.0.2 [extras: async, dotenv] [with: requests]
509+
- flask
510+
511+
----- stderr -----
512+
"###);
513+
514+
// Test with --show-extras and --show-paths
515+
uv_snapshot!(context.filters(), context.tool_list().arg("--show-extras").arg("--show-paths")
516+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
517+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
518+
success: true
519+
exit_code: 0
520+
----- stdout -----
521+
black v24.2.0 ([TEMP_DIR]/tools/black)
522+
- black ([TEMP_DIR]/bin/black)
523+
- blackd ([TEMP_DIR]/bin/blackd)
524+
flask v3.0.2 [extras: async, dotenv] ([TEMP_DIR]/tools/flask)
525+
- flask ([TEMP_DIR]/bin/flask)
526+
527+
----- stderr -----
528+
"###);
529+
530+
// Test with --show-extras and --show-version-specifiers
531+
uv_snapshot!(context.filters(), context.tool_list().arg("--show-extras").arg("--show-version-specifiers")
532+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
533+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
534+
success: true
535+
exit_code: 0
536+
----- stdout -----
537+
black v24.2.0 [required: ==24.2.0]
538+
- black
539+
- blackd
540+
flask v3.0.2 [extras: async, dotenv]
541+
- flask
542+
543+
----- stderr -----
544+
"###);
545+
546+
// Test with all flags including --show-extras
547+
uv_snapshot!(context.filters(), context.tool_list()
548+
.arg("--show-extras")
549+
.arg("--show-with")
550+
.arg("--show-version-specifiers")
551+
.arg("--show-paths")
552+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
553+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
554+
success: true
555+
exit_code: 0
556+
----- stdout -----
557+
black v24.2.0 [required: ==24.2.0] ([TEMP_DIR]/tools/black)
558+
- black ([TEMP_DIR]/bin/black)
559+
- blackd ([TEMP_DIR]/bin/blackd)
560+
flask v3.0.2 [extras: async, dotenv] [with: requests] ([TEMP_DIR]/tools/flask)
561+
- flask ([TEMP_DIR]/bin/flask)
562+
563+
----- stderr -----
564+
"###);
565+
}

docs/reference/cli.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2293,6 +2293,7 @@ uv tool list [OPTIONS]
22932293
<p>This setting has no effect when used in the <code>uv pip</code> interface.</p>
22942294
<p>May also be set with the <code>UV_PROJECT</code> environment variable.</p></dd><dt id="uv-tool-list--quiet"><a href="#uv-tool-list--quiet"><code>--quiet</code></a>, <code>-q</code></dt><dd><p>Use quiet output.</p>
22952295
<p>Repeating this option, e.g., <code>-qq</code>, will enable a silent mode in which uv will write no output to stdout.</p>
2296+
</dd><dt id="uv-tool-list--show-extras"><a href="#uv-tool-list--show-extras"><code>--show-extras</code></a></dt><dd><p>Whether to display the extra requirements installed with each tool</p>
22962297
</dd><dt id="uv-tool-list--show-paths"><a href="#uv-tool-list--show-paths"><code>--show-paths</code></a></dt><dd><p>Whether to display the path to each tool environment and installed executable</p>
22972298
</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>
22982299
</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>

0 commit comments

Comments
 (0)