From b818059afabb850c5c45d43c8bfb7c9d5254cba1 Mon Sep 17 00:00:00 2001 From: pythonweb2 <32141163+pythonweb2@users.noreply.github.com> Date: Fri, 15 Aug 2025 15:48:41 +0000 Subject: [PATCH 01/12] Trying to figure out how to get the python info --- crates/uv-cli/src/lib.rs | 4 ++++ crates/uv/src/commands/tool/list.rs | 19 +++++++++++++++++++ crates/uv/src/lib.rs | 1 + crates/uv/src/settings.rs | 3 +++ 4 files changed, 27 insertions(+) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index f89ebf90d1827..fcde3de3fde37 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -4581,6 +4581,10 @@ pub struct ToolListArgs { #[arg(long)] pub show_extras: bool, + /// Whether to display the Python installation used to build each tool. + #[arg(long)] + pub show_python: 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 21b16b020d107..3458945474540 100644 --- a/crates/uv/src/commands/tool/list.rs +++ b/crates/uv/src/commands/tool/list.rs @@ -19,6 +19,7 @@ pub(crate) async fn list( show_version_specifiers: bool, show_with: bool, show_extras: bool, + show_python: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -97,6 +98,24 @@ pub(crate) async fn list( }) .unwrap_or_default(); + writeln!(printer.stdout(), "show_python: {}", show_python)?; + let python_version = if show_python { + if let Some(python_request) = tool.python() { + writeln!(printer.stdout(), "python_request")?; + if let uv_python::PythonRequest::ImplementationVersion(implementation, version_request) = python_request { + writeln!(printer.stdout(), "uv_python::PythonRequest::ImplementationVersion")?; + format!(" [python: {} {}]", implementation, version_request) + } else { + format!(" [python: {}]", python_request) + } + } else { + String::new() + } + } else { + String::new() + }; + writeln!(printer.stdout(), "python version: {}", python_version)?; + let with_requirements = show_with .then(|| { tool.requirements() diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 5f2399219d723..3ec3c3d5d9f2d 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -1316,6 +1316,7 @@ async fn run(mut cli: Cli) -> Result { args.show_paths, args.show_version_specifiers, args.show_with, + args.show_python, args.show_extras, &cache, printer, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 2f1646b768351..203785843f7ab 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -798,6 +798,7 @@ pub(crate) struct ToolListSettings { pub(crate) show_version_specifiers: bool, pub(crate) show_with: bool, pub(crate) show_extras: bool, + pub(crate) show_python: bool, } impl ToolListSettings { @@ -809,6 +810,7 @@ impl ToolListSettings { show_version_specifiers, show_with, show_extras, + show_python, python_preference: _, no_python_downloads: _, } = args; @@ -818,6 +820,7 @@ impl ToolListSettings { show_version_specifiers, show_with, show_extras, + show_python, } } } From ace18f818f8b0e79b8939fc3e3cd56c738ceec41 Mon Sep 17 00:00:00 2001 From: pythonweb2 <32141163+pythonweb2@users.noreply.github.com> Date: Fri, 15 Aug 2025 09:54:48 -0600 Subject: [PATCH 02/12] update CLI help text --- crates/uv-cli/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index fcde3de3fde37..9db04f94c9864 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -4581,7 +4581,7 @@ pub struct ToolListArgs { #[arg(long)] pub show_extras: bool, - /// Whether to display the Python installation used to build each tool. + /// Whether to display the Python installation used to run each tool. #[arg(long)] pub show_python: bool, From 533e89c579ec69925cf7f8694da9af053ca717de Mon Sep 17 00:00:00 2001 From: pythonweb2 <32141163+pythonweb2@users.noreply.github.com> Date: Fri, 15 Aug 2025 15:57:09 +0000 Subject: [PATCH 03/12] add docs --- docs/reference/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 10c9831eac1db..4fece889181b3 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -2422,6 +2422,7 @@ uv tool list [OPTIONS]

This setting has no effect when used in the uv pip interface.

May also be set with the UV_PROJECT environment variable.

--quiet, -q

Use quiet output.

Repeating this option, e.g., -qq, will enable a silent mode in which uv will write no output to stdout.

+
--show-python

Whether to display the Python installation used to run each tool.

--show-extras

Whether to display the extra requirements installed with each tool

--show-paths

Whether to display the path to each tool environment and installed executable

--show-version-specifiers

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

@@ -5617,4 +5618,3 @@ uv help [OPTIONS] [COMMAND]...
--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)

- From 80e3e96cc4eaf2a98acbe05bd7b3dc0ff3761272 Mon Sep 17 00:00:00 2001 From: Wade Roberts Date: Fri, 15 Aug 2025 11:58:53 -0600 Subject: [PATCH 04/12] arg was mixed up --- crates/uv/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 3ec3c3d5d9f2d..6a5ea3ad30b72 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -1316,8 +1316,8 @@ async fn run(mut cli: Cli) -> Result { args.show_paths, args.show_version_specifiers, args.show_with, - args.show_python, args.show_extras, + args.show_python, &cache, printer, ) From 51fe15a47da63de7c69e69576b5cc2cb33bb4088 Mon Sep 17 00:00:00 2001 From: Wade Roberts Date: Fri, 15 Aug 2025 14:03:04 -0600 Subject: [PATCH 05/12] use environment and not tool options --- crates/uv/src/commands/tool/list.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/uv/src/commands/tool/list.rs b/crates/uv/src/commands/tool/list.rs index 3458945474540..7fa3832edce49 100644 --- a/crates/uv/src/commands/tool/list.rs +++ b/crates/uv/src/commands/tool/list.rs @@ -98,23 +98,18 @@ pub(crate) async fn list( }) .unwrap_or_default(); - writeln!(printer.stdout(), "show_python: {}", show_python)?; let python_version = if show_python { - if let Some(python_request) = tool.python() { - writeln!(printer.stdout(), "python_request")?; - if let uv_python::PythonRequest::ImplementationVersion(implementation, version_request) = python_request { - writeln!(printer.stdout(), "uv_python::PythonRequest::ImplementationVersion")?; - format!(" [python: {} {}]", implementation, version_request) - } else { - format!(" [python: {}]", python_request) + match installed_tools.get_environment(&name, cache) { + Ok(Some(env)) => { + let interpreter = env.interpreter(); + format!(" [{}]", interpreter.markers().implementation_version()) } - } else { - String::new() + Ok(None) => String::from(" [python: not found]"), + Err(e) => format!(" [python: error: {}]", e), } } else { String::new() }; - writeln!(printer.stdout(), "python version: {}", python_version)?; let with_requirements = show_with .then(|| { @@ -137,7 +132,7 @@ pub(crate) async fn list( printer.stdout(), "{} ({})", format!( - "{name} v{version}{version_specifier}{extra_requirements}{with_requirements}" + "{name} v{version}{version_specifier}{extra_requirements}{with_requirements}{python_version}" ) .bold(), installed_tools.tool_dir(&name).simplified_display().cyan(), @@ -147,7 +142,7 @@ pub(crate) async fn list( printer.stdout(), "{}", format!( - "{name} v{version}{version_specifier}{extra_requirements}{with_requirements}" + "{name} v{version}{version_specifier}{extra_requirements}{with_requirements}{python_version}" ) .bold() )?; From 92ad15a85523df19fff2528a9631b530770d7456 Mon Sep 17 00:00:00 2001 From: Wade Roberts Date: Fri, 15 Aug 2025 14:14:08 -0600 Subject: [PATCH 06/12] linter fixes --- crates/uv/src/commands/tool/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/uv/src/commands/tool/list.rs b/crates/uv/src/commands/tool/list.rs index 7fa3832edce49..bcc32506ca7d6 100644 --- a/crates/uv/src/commands/tool/list.rs +++ b/crates/uv/src/commands/tool/list.rs @@ -105,7 +105,7 @@ pub(crate) async fn list( format!(" [{}]", interpreter.markers().implementation_version()) } Ok(None) => String::from(" [python: not found]"), - Err(e) => format!(" [python: error: {}]", e), + Err(e) => format!(" [python: error: {e}]"), } } else { String::new() From 432f2216b2c5eb138801c4241ef8ca6b642de24d Mon Sep 17 00:00:00 2001 From: Wade Roberts Date: Fri, 15 Aug 2025 14:46:55 -0600 Subject: [PATCH 07/12] add tests, use tool for updating docs --- crates/uv/tests/it/tool_list.rs | 78 +++++++++++++++++++++++++++++++++ docs/reference/cli.md | 3 +- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/crates/uv/tests/it/tool_list.rs b/crates/uv/tests/it/tool_list.rs index cb767d457ee11..e68cced2392e9 100644 --- a/crates/uv/tests/it/tool_list.rs +++ b/crates/uv/tests/it/tool_list.rs @@ -563,3 +563,81 @@ fn tool_list_show_extras() { ----- stderr ----- "###); } + +#[test] +fn tool_list_show_python() { + 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` with python 3.12 + 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(); + + // Test with --show-python + uv_snapshot!(context.filters(), context.tool_list().arg("--show-python") + .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 [3.12.[X]] + - black + - blackd + + ----- stderr ----- + "###); +} + +#[test] +fn tool_list_show_all() { + 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 extras + 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 extras and additional requirements + context + .tool_install() + .arg("flask[async,dotenv]") + .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 all flags + uv_snapshot!(context.filters(), context.tool_list() + .arg("--show-extras") + .arg("--show-with") + .arg("--show-version-specifiers") + .arg("--show-paths") + .arg("--show-python") + .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] [3.12.[X]] ([TEMP_DIR]/tools/black) + - black ([TEMP_DIR]/bin/black) + - blackd ([TEMP_DIR]/bin/blackd) + flask v3.0.2 [extras: async, dotenv] [with: requests] [3.12.[X]] ([TEMP_DIR]/tools/flask) + - flask ([TEMP_DIR]/bin/flask) + + ----- stderr ----- + "###); +} diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 4fece889181b3..c04f2a844f74e 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -2422,9 +2422,9 @@ uv tool list [OPTIONS]

This setting has no effect when used in the uv pip interface.

May also be set with the UV_PROJECT environment variable.

--quiet, -q

Use quiet output.

Repeating this option, e.g., -qq, will enable a silent mode in which uv will write no output to stdout.

-
--show-python

Whether to display the Python installation used to run each tool.

--show-extras

Whether to display the extra requirements installed with each tool

--show-paths

Whether to display the path to each tool environment and installed executable

+
--show-python

Whether to display the Python installation used to run each tool

--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.

@@ -5618,3 +5618,4 @@ uv help [OPTIONS] [COMMAND]...
--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)

+ From 503be45317c05c595c309139668c39c632b61f65 Mon Sep 17 00:00:00 2001 From: Harshith VH Date: Fri, 12 Sep 2025 20:45:53 +0530 Subject: [PATCH 08/12] finish work on python-list-option --- crates/uv-tool/src/lib.rs | 60 +++++++++++++++++++++----- crates/uv/src/commands/tool/install.rs | 2 +- crates/uv/src/commands/tool/list.rs | 45 +++++++++++-------- crates/uv/src/commands/tool/run.rs | 2 +- crates/uv/src/commands/tool/upgrade.rs | 2 +- 5 files changed, 79 insertions(+), 32 deletions(-) diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index cf279302accd7..ce6fbb3fda633 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -29,6 +29,48 @@ use uv_virtualenv::remove_virtualenv; mod receipt; mod tool; +/// A wrapper around [`PythonEnvironment`] for tools that provides additional functionality. +#[derive(Debug, Clone)] +pub struct ToolEnvironment { + environment: PythonEnvironment, + name: PackageName, +} + +impl ToolEnvironment { + pub fn new(environment: PythonEnvironment, name: PackageName) -> Self { + Self { environment, name } + } + + /// Return the [`Version`] of the tool package in this environment. + pub fn version(&self) -> Result { + let site_packages = SitePackages::from_environment(&self.environment) + .map_err(|err| Error::EnvironmentRead(self.environment.root().to_path_buf(), err.to_string()))?; + let packages = site_packages.get_packages(&self.name); + let package = packages + .first() + .ok_or_else(|| Error::MissingToolPackage(self.name.clone()))?; + Ok(package.version().clone()) + } + + /// Get the underlying [`PythonEnvironment`]. + pub fn into_inner(self) -> PythonEnvironment { + self.environment + } + + /// Get a reference to the underlying [`PythonEnvironment`]. + pub fn environment(&self) -> &PythonEnvironment { + &self.environment + } +} + +impl std::ops::Deref for ToolEnvironment { + type Target = PythonEnvironment; + + fn deref(&self) -> &Self::Target { + &self.environment + } +} + #[derive(Error, Debug)] pub enum Error { #[error(transparent)] @@ -53,6 +95,8 @@ pub enum Error { EnvironmentRead(PathBuf, String), #[error("Failed find package `{0}` in tool environment")] MissingToolPackage(PackageName), + #[error("Tool `{0}` environment not found at `{1}`")] + ToolEnvironmentNotFound(PackageName, PathBuf), } /// A collection of uv-managed tools installed on the current system. @@ -204,7 +248,7 @@ impl InstalledTools { &self, name: &PackageName, cache: &Cache, - ) -> Result, Error> { + ) -> Result, Error> { let environment_path = self.tool_dir(name); match PythonEnvironment::from_root(&environment_path, cache) { @@ -213,7 +257,7 @@ impl InstalledTools { "Found existing environment for tool `{name}`: {}", environment_path.user_display() ); - Ok(Some(venv)) + Ok(Some(ToolEnvironment::new(venv, name.clone()))) } Err(uv_python::Error::MissingEnvironment(_)) => Ok(None), Err(uv_python::Error::Query(uv_python::InterpreterError::NotFound( @@ -295,15 +339,9 @@ impl InstalledTools { /// Return the [`Version`] of an installed tool. pub fn version(&self, name: &PackageName, cache: &Cache) -> Result { - let environment_path = self.tool_dir(name); - let environment = PythonEnvironment::from_root(&environment_path, cache)?; - let site_packages = SitePackages::from_environment(&environment) - .map_err(|err| Error::EnvironmentRead(environment_path.clone(), err.to_string()))?; - let packages = site_packages.get_packages(name); - let package = packages - .first() - .ok_or_else(|| Error::MissingToolPackage(name.clone()))?; - Ok(package.version().clone()) + let tool_env = self.get_environment(name, cache)? + .ok_or_else(|| Error::ToolEnvironmentNotFound(name.clone(), self.tool_dir(name)))?; + tool_env.version() } /// Initialize the tools directory. diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index b191d0e1e3d7e..1570c2cc8d61f 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -440,7 +440,7 @@ pub(crate) async fn install( // be invalidated by moving the environment. let environment = if let Some(environment) = existing_environment { let environment = match update_environment( - environment, + environment.into_inner(), spec, Modifications::Exact, Constraints::from_requirements(build_constraints.iter().cloned()), diff --git a/crates/uv/src/commands/tool/list.rs b/crates/uv/src/commands/tool/list.rs index bcc32506ca7d6..ab7326420a405 100644 --- a/crates/uv/src/commands/tool/list.rs +++ b/crates/uv/src/commands/tool/list.rs @@ -51,18 +51,33 @@ pub(crate) async fn list( continue; }; - // Output tool name and version - let version = match installed_tools.version(&name, cache) { + // Get the tool environment + let tool_env = match installed_tools.get_environment(&name, cache) { + Ok(Some(env)) => env, + Ok(None) => { + warn_user!( + "Tool `{name}` environment not found (run `{}` to reinstall)", + format!("uv tool install {name} --reinstall").green() + ); + continue; + } + Err(e) => { + warn_user!( + "{e} (run `{}` to reinstall)", + format!("uv tool install {name} --reinstall").green() + ); + continue; + } + }; + + // Get the tool version + let version = match tool_env.version() { Ok(version) => version, Err(e) => { - if let uv_tool::Error::EnvironmentError(e) = e { - warn_user!( - "{e} (run `{}` to reinstall)", - format!("uv tool install {name} --reinstall").green() - ); - } else { - writeln!(printer.stderr(), "{e}")?; - } + warn_user!( + "{e} (run `{}` to reinstall)", + format!("uv tool install {name} --reinstall").green() + ); continue; } }; @@ -99,14 +114,8 @@ pub(crate) async fn list( .unwrap_or_default(); let python_version = if show_python { - match installed_tools.get_environment(&name, cache) { - Ok(Some(env)) => { - let interpreter = env.interpreter(); - format!(" [{}]", interpreter.markers().implementation_version()) - } - Ok(None) => String::from(" [python: not found]"), - Err(e) => format!(" [python: error: {e}]"), - } + let interpreter = tool_env.interpreter(); + format!(" [{}]", interpreter.python_full_version()) } else { String::new() }; diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 64628e56a4637..da763cf53dca0 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -957,7 +957,7 @@ async fn get_or_create_environment( Ok(SatisfiesResult::Fresh { .. }) ) { debug!("Using existing tool `{}`", requirement.name); - return Ok((from, environment)); + return Ok((from, environment.into_inner())); } } } diff --git a/crates/uv/src/commands/tool/upgrade.rs b/crates/uv/src/commands/tool/upgrade.rs index 4c62e15d665e3..044d96e82e8a7 100644 --- a/crates/uv/src/commands/tool/upgrade.rs +++ b/crates/uv/src/commands/tool/upgrade.rs @@ -335,7 +335,7 @@ async fn upgrade_tool( environment, changelog, } = update_environment( - environment, + environment.into_inner(), spec, Modifications::Exact, build_constraints, From 7d3a1dcebe3478e87567a8231e540ae8fc8d9d6b Mon Sep 17 00:00:00 2001 From: Harshith VH Date: Fri, 12 Sep 2025 21:44:07 +0530 Subject: [PATCH 09/12] fix formatting --- crates/uv-tool/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index ce6fbb3fda633..cefd3d1bdc109 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -43,8 +43,9 @@ impl ToolEnvironment { /// Return the [`Version`] of the tool package in this environment. pub fn version(&self) -> Result { - let site_packages = SitePackages::from_environment(&self.environment) - .map_err(|err| Error::EnvironmentRead(self.environment.root().to_path_buf(), err.to_string()))?; + let site_packages = SitePackages::from_environment(&self.environment).map_err(|err| { + Error::EnvironmentRead(self.environment.root().to_path_buf(), err.to_string()) + })?; let packages = site_packages.get_packages(&self.name); let package = packages .first() @@ -339,7 +340,8 @@ impl InstalledTools { /// Return the [`Version`] of an installed tool. pub fn version(&self, name: &PackageName, cache: &Cache) -> Result { - let tool_env = self.get_environment(name, cache)? + let tool_env = self + .get_environment(name, cache)? .ok_or_else(|| Error::ToolEnvironmentNotFound(name.clone(), self.tool_dir(name)))?; tool_env.version() } From 3ae7b9f55c69f64bb7417507e69f835db274bf3f Mon Sep 17 00:00:00 2001 From: Harshith VH Date: Fri, 19 Sep 2025 11:12:42 +0530 Subject: [PATCH 10/12] update --- crates/uv-cli/src/lib.rs | 2 +- crates/uv-tool/src/lib.rs | 18 +----------------- crates/uv/src/commands/tool/install.rs | 6 +++--- crates/uv/src/commands/tool/list.rs | 22 ++++++++++++++++------ crates/uv/src/commands/tool/run.rs | 10 ++++++---- crates/uv/src/commands/tool/upgrade.rs | 4 ++-- 6 files changed, 29 insertions(+), 33 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 9db04f94c9864..57ac2704884cf 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -4581,7 +4581,7 @@ pub struct ToolListArgs { #[arg(long)] pub show_extras: bool, - /// Whether to display the Python installation used to run each tool. + /// Whether to display the Python version associated with run each tool. #[arg(long)] pub show_python: bool, diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index cefd3d1bdc109..2613ed94db175 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -54,7 +54,7 @@ impl ToolEnvironment { } /// Get the underlying [`PythonEnvironment`]. - pub fn into_inner(self) -> PythonEnvironment { + pub fn into_environment(self) -> PythonEnvironment { self.environment } @@ -64,14 +64,6 @@ impl ToolEnvironment { } } -impl std::ops::Deref for ToolEnvironment { - type Target = PythonEnvironment; - - fn deref(&self) -> &Self::Target { - &self.environment - } -} - #[derive(Error, Debug)] pub enum Error { #[error(transparent)] @@ -338,14 +330,6 @@ impl InstalledTools { )) } - /// Return the [`Version`] of an installed tool. - pub fn version(&self, name: &PackageName, cache: &Cache) -> Result { - let tool_env = self - .get_environment(name, cache)? - .ok_or_else(|| Error::ToolEnvironmentNotFound(name.clone(), self.tool_dir(name)))?; - tool_env.version() - } - /// Initialize the tools directory. /// /// Ensures the directory is created. diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index 1570c2cc8d61f..b42915649408a 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -360,11 +360,11 @@ pub(crate) async fn install( installed_tools .get_environment(package_name, &cache)? .filter(|environment| { - if environment.uses(&interpreter) { + if environment.environment().uses(&interpreter) { trace!( "Existing interpreter matches the requested interpreter for `{}`: {}", package_name, - environment.interpreter().sys_executable().display() + environment.environment().interpreter().sys_executable().display() ); true } else { @@ -440,7 +440,7 @@ pub(crate) async fn install( // be invalidated by moving the environment. let environment = if let Some(environment) = existing_environment { let environment = match update_environment( - environment.into_inner(), + environment.into_environment(), spec, Modifications::Exact, Constraints::from_requirements(build_constraints.iter().cloned()), diff --git a/crates/uv/src/commands/tool/list.rs b/crates/uv/src/commands/tool/list.rs index ab7326420a405..e6e66a4a79078 100644 --- a/crates/uv/src/commands/tool/list.rs +++ b/crates/uv/src/commands/tool/list.rs @@ -6,6 +6,7 @@ use owo_colors::OwoColorize; use uv_cache::Cache; use uv_fs::Simplified; +use uv_python::LenientImplementationName; use uv_tool::InstalledTools; use uv_warnings::warn_user; @@ -74,10 +75,14 @@ pub(crate) async fn list( let version = match tool_env.version() { Ok(version) => version, Err(e) => { - warn_user!( - "{e} (run `{}` to reinstall)", - format!("uv tool install {name} --reinstall").green() - ); + if let uv_tool::Error::EnvironmentError(e) = e { + warn_user!( + "{e} (run `{}` to reinstall)", + format!("uv tool install {name} --reinstall").green() + ); + } else { + writeln!(printer.stderr(), "{e}")?; + } continue; } }; @@ -114,8 +119,13 @@ pub(crate) async fn list( .unwrap_or_default(); let python_version = if show_python { - let interpreter = tool_env.interpreter(); - format!(" [{}]", interpreter.python_full_version()) + let interpreter = tool_env.environment().interpreter(); + let implementation = LenientImplementationName::from(interpreter.implementation_name()); + format!( + " [{} {}]", + implementation.pretty(), + interpreter.python_full_version() + ) } else { String::new() }; diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index da763cf53dca0..a20bec4e34392 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -452,8 +452,10 @@ async fn show_help( .filter_map(|(name, tool)| { tool.ok().and_then(|_| { installed_tools - .version(&name, cache) + .get_environment(&name, cache) .ok() + .flatten() + .and_then(|tool_env| tool_env.version().ok()) .map(|version| (name, version)) }) }) @@ -933,7 +935,7 @@ async fn get_or_create_environment( .get_environment(&requirement.name, cache)? .filter(|environment| { python_request.as_ref().is_none_or(|python_request| { - python_request.satisfied(environment.interpreter(), cache) + python_request.satisfied(environment.environment().interpreter(), cache) }) }); @@ -946,7 +948,7 @@ async fn get_or_create_environment( .is_some_and(|receipt| ToolOptions::from(options) == *receipt.options()) { // Check if the installed packages meet the requirements. - let site_packages = SitePackages::from_environment(&environment)?; + let site_packages = SitePackages::from_environment(environment.environment())?; if matches!( site_packages.satisfies_requirements( requirements.iter(), @@ -957,7 +959,7 @@ async fn get_or_create_environment( Ok(SatisfiesResult::Fresh { .. }) ) { debug!("Using existing tool `{}`", requirement.name); - return Ok((from, environment.into_inner())); + return Ok((from, environment.into_environment())); } } } diff --git a/crates/uv/src/commands/tool/upgrade.rs b/crates/uv/src/commands/tool/upgrade.rs index 044d96e82e8a7..24fdcbd3c5219 100644 --- a/crates/uv/src/commands/tool/upgrade.rs +++ b/crates/uv/src/commands/tool/upgrade.rs @@ -289,7 +289,7 @@ async fn upgrade_tool( // Check if we need to create a new environment — if so, resolve it first, then // install the requested tool let (environment, outcome) = if let Some(interpreter) = - interpreter.filter(|interpreter| !environment.uses(interpreter)) + interpreter.filter(|interpreter| !environment.environment().uses(interpreter)) { // If we're using a new interpreter, re-create the environment for each tool. let resolution = resolve_environment( @@ -335,7 +335,7 @@ async fn upgrade_tool( environment, changelog, } = update_environment( - environment.into_inner(), + environment.into_environment(), spec, Modifications::Exact, build_constraints, From 209f688e5be18dd52b621a1133028138d4e9181f Mon Sep 17 00:00:00 2001 From: Harshith VH Date: Fri, 19 Sep 2025 12:01:34 +0530 Subject: [PATCH 11/12] fix linting --- crates/uv/src/commands/tool/install.rs | 2 +- docs/reference/cli.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index 28092aec0fe76..0f35ad70ce0bb 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -399,7 +399,7 @@ pub(crate) async fn install( let tags = resolution_tags(None, python_platform.as_ref(), &interpreter)?; // Check if the installed packages meet the requirements. - let site_packages = SitePackages::from_environment(environment)?; + let site_packages = SitePackages::from_environment(environment.environment())?; if matches!( site_packages.satisfies_requirements( requirements.iter(), diff --git a/docs/reference/cli.md b/docs/reference/cli.md index c4996045575d0..7f043a6e8bcfc 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -3047,7 +3047,7 @@ uv tool list [OPTIONS]

Repeating this option, e.g., -qq, will enable a silent mode in which uv will write no output to stdout.

--show-extras

Whether to display the extra requirements installed with each tool

--show-paths

Whether to display the path to each tool environment and installed executable

-
--show-python

Whether to display the Python installation used to run each tool

+
--show-python

Whether to display the Python version associated with run each tool

--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.

From 9a8d0de1ee352708d169e4b393558a3f868ef4a3 Mon Sep 17 00:00:00 2001 From: Harshith VH Date: Fri, 19 Sep 2025 12:26:08 +0530 Subject: [PATCH 12/12] fix tests --- crates/uv/tests/it/tool_list.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/uv/tests/it/tool_list.rs b/crates/uv/tests/it/tool_list.rs index e68cced2392e9..3d77f0178b0c3 100644 --- a/crates/uv/tests/it/tool_list.rs +++ b/crates/uv/tests/it/tool_list.rs @@ -586,7 +586,7 @@ fn tool_list_show_python() { success: true exit_code: 0 ----- stdout ----- - black v24.2.0 [3.12.[X]] + black v24.2.0 [CPython 3.12.[X]] - black - blackd @@ -632,10 +632,10 @@ fn tool_list_show_all() { success: true exit_code: 0 ----- stdout ----- - black v24.2.0 [required: ==24.2.0] [3.12.[X]] ([TEMP_DIR]/tools/black) + black v24.2.0 [required: ==24.2.0] [CPython 3.12.[X]] ([TEMP_DIR]/tools/black) - black ([TEMP_DIR]/bin/black) - blackd ([TEMP_DIR]/bin/blackd) - flask v3.0.2 [extras: async, dotenv] [with: requests] [3.12.[X]] ([TEMP_DIR]/tools/flask) + flask v3.0.2 [extras: async, dotenv] [with: requests] [CPython 3.12.[X]] ([TEMP_DIR]/tools/flask) - flask ([TEMP_DIR]/bin/flask) ----- stderr -----