Add `--show-version` to `uv python find` (#12376)

@jtfmumm mentioned a desire for this. I'm not sure how we should do
this. I kind of want to change this to something like...

```
$ uv python find
CPython 3.13 @ <path>
$ uv python find --only-path
<path>
$ uv python find --short
<path>
$ uv python find --only-version 
3.13
```

The change in defaults would be breaking though.
This commit is contained in:
Zanie Blue 2025-04-03 08:34:45 -05:00 committed by GitHub
parent a6c621d4a5
commit be3d5dfa84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 90 additions and 21 deletions

View File

@ -4785,6 +4785,10 @@ pub struct PythonFindArgs {
conflicts_with = "no_system" conflicts_with = "no_system"
)] )]
pub script: Option<PathBuf>, pub script: Option<PathBuf>,
/// Show the Python version that would be used instead of the path to the interpreter.
#[arg(long)]
pub show_version: bool,
} }
#[derive(Args)] #[derive(Args)]

View File

@ -1,4 +1,3 @@
use anstream::println;
use anyhow::Result; use anyhow::Result;
use std::fmt::Write; use std::fmt::Write;
use std::path::Path; use std::path::Path;
@ -21,14 +20,17 @@ use crate::printer::Printer;
use crate::settings::NetworkSettings; use crate::settings::NetworkSettings;
/// Find a Python interpreter. /// Find a Python interpreter.
#[allow(clippy::fn_params_excessive_bools)]
pub(crate) async fn find( pub(crate) async fn find(
project_dir: &Path, project_dir: &Path,
request: Option<String>, request: Option<String>,
show_version: bool,
no_project: bool, no_project: bool,
no_config: bool, no_config: bool,
system: bool, system: bool,
python_preference: PythonPreference, python_preference: PythonPreference,
cache: &Cache, cache: &Cache,
printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
let environment_preference = if system { let environment_preference = if system {
EnvironmentPreference::OnlySystem EnvironmentPreference::OnlySystem
@ -88,16 +90,26 @@ pub(crate) async fn find(
} }
}; };
println!( if show_version {
"{}", writeln!(
std::path::absolute(python.interpreter().sys_executable())?.simplified_display() printer.stdout(),
); "{}",
python.interpreter().python_version()
)?;
} else {
writeln!(
printer.stdout(),
"{}",
std::path::absolute(python.interpreter().sys_executable())?.simplified_display()
)?;
}
Ok(ExitStatus::Success) Ok(ExitStatus::Success)
} }
pub(crate) async fn find_script( pub(crate) async fn find_script(
script: Pep723ItemRef<'_>, script: Pep723ItemRef<'_>,
show_version: bool,
network_settings: &NetworkSettings, network_settings: &NetworkSettings,
python_preference: PythonPreference, python_preference: PythonPreference,
python_downloads: PythonDownloads, python_downloads: PythonDownloads,
@ -105,7 +117,7 @@ pub(crate) async fn find_script(
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
match ScriptInterpreter::discover( let interpreter = match ScriptInterpreter::discover(
script, script,
None, None,
network_settings, network_settings,
@ -121,22 +133,21 @@ pub(crate) async fn find_script(
{ {
Err(error) => { Err(error) => {
writeln!(printer.stderr(), "{error}")?; writeln!(printer.stderr(), "{error}")?;
return Ok(ExitStatus::Failure);
Ok(ExitStatus::Failure)
} }
Ok(ScriptInterpreter::Interpreter(interpreter)) => interpreter,
Ok(ScriptInterpreter::Environment(environment)) => environment.into_interpreter(),
};
Ok(ScriptInterpreter::Interpreter(interpreter)) => { if show_version {
let path = interpreter.sys_executable(); writeln!(printer.stdout(), "{}", interpreter.python_version())?;
println!("{}", std::path::absolute(path)?.simplified_display()); } else {
writeln!(
Ok(ExitStatus::Success) printer.stdout(),
} "{}",
std::path::absolute(interpreter.sys_executable())?.simplified_display()
Ok(ScriptInterpreter::Environment(environment)) => { )?;
let path = environment.interpreter().sys_executable();
println!("{}", std::path::absolute(path)?.simplified_display());
Ok(ExitStatus::Success)
}
} }
Ok(ExitStatus::Success)
} }

View File

@ -1340,6 +1340,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
if let Some(Pep723Item::Script(script)) = script { if let Some(Pep723Item::Script(script)) = script {
commands::python_find_script( commands::python_find_script(
Pep723ItemRef::Script(&script), Pep723ItemRef::Script(&script),
args.show_version,
&globals.network_settings, &globals.network_settings,
globals.python_preference, globals.python_preference,
globals.python_downloads, globals.python_downloads,
@ -1352,11 +1353,13 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
commands::python_find( commands::python_find(
&project_dir, &project_dir,
args.request, args.request,
args.show_version,
args.no_project, args.no_project,
cli.top_level.no_config, cli.top_level.no_config,
args.system, args.system,
globals.python_preference, globals.python_preference,
&cache, &cache,
printer,
) )
.await .await
} }

View File

@ -972,6 +972,7 @@ impl PythonUninstallSettings {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct PythonFindSettings { pub(crate) struct PythonFindSettings {
pub(crate) request: Option<String>, pub(crate) request: Option<String>,
pub(crate) show_version: bool,
pub(crate) no_project: bool, pub(crate) no_project: bool,
pub(crate) system: bool, pub(crate) system: bool,
} }
@ -982,6 +983,7 @@ impl PythonFindSettings {
pub(crate) fn resolve(args: PythonFindArgs, _filesystem: Option<FilesystemOptions>) -> Self { pub(crate) fn resolve(args: PythonFindArgs, _filesystem: Option<FilesystemOptions>) -> Self {
let PythonFindArgs { let PythonFindArgs {
request, request,
show_version,
no_project, no_project,
system, system,
no_system, no_system,
@ -990,6 +992,7 @@ impl PythonFindSettings {
Self { Self {
request, request,
show_version,
no_project, no_project,
system: flag(system, no_system).unwrap_or_default(), system: flag(system, no_system).unwrap_or_default(),
} }

View File

@ -844,3 +844,49 @@ fn python_find_script_no_such_version() {
No interpreter found for Python >=3.14 in [PYTHON SOURCES] No interpreter found for Python >=3.14 in [PYTHON SOURCES]
"); ");
} }
#[test]
fn python_find_show_version() {
let context: TestContext =
TestContext::new_with_versions(&["3.11", "3.12"]).with_filtered_python_sources();
// No interpreters found
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::UV_TEST_PYTHON_PATH, "").arg("--show-version"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: No interpreter found in [PYTHON SOURCES]
");
// Show the first version found
uv_snapshot!(context.filters(), context.python_find().arg("--show-version"), @r"
success: true
exit_code: 0
----- stdout -----
3.11.[X]
----- stderr -----
");
// Request Python 3.12
uv_snapshot!(context.filters(), context.python_find().arg("--show-version").arg("3.12"), @r"
success: true
exit_code: 0
----- stdout -----
3.12.[X]
----- stderr -----
");
// Request Python 3.11
uv_snapshot!(context.filters(), context.python_find().arg("--show-version").arg("3.11"), @r"
success: true
exit_code: 0
----- stdout -----
3.11.[X]
----- stderr -----
");
}

View File

@ -5096,6 +5096,8 @@ uv python find [OPTIONS] [REQUEST]
</dd><dt id="uv-python-find--script"><a href="#uv-python-find--script"><code>--script</code></a> <i>script</i></dt><dd><p>Find the environment for a Python script, rather than the current project</p> </dd><dt id="uv-python-find--script"><a href="#uv-python-find--script"><code>--script</code></a> <i>script</i></dt><dd><p>Find the environment for a Python script, rather than the current project</p>
</dd><dt id="uv-python-find--show-version"><a href="#uv-python-find--show-version"><code>--show-version</code></a></dt><dd><p>Show the Python version that would be used instead of the path to the interpreter</p>
</dd><dt id="uv-python-find--system"><a href="#uv-python-find--system"><code>--system</code></a></dt><dd><p>Only find system Python interpreters.</p> </dd><dt id="uv-python-find--system"><a href="#uv-python-find--system"><code>--system</code></a></dt><dd><p>Only find system Python interpreters.</p>
<p>By default, uv will report the first Python interpreter it would use, including those in an active virtual environment or a virtual environment in the current working directory or any parent directory.</p> <p>By default, uv will report the first Python interpreter it would use, including those in an active virtual environment or a virtual environment in the current working directory or any parent directory.</p>