mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Support python find --script
(#11891)
Some checks are pending
CI / lint (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on linux (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on opensuse (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
Some checks are pending
CI / lint (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on linux (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on opensuse (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
## Summary Resolves #11794. When `uv python find` is given a `--script` option, either the existing environment for that script or the Python executable that would be used to create it will be returned. If neither are found, the command exits with exit code 1. `--script` is incompatible with all other options to the same command. ## Test Plan Unit tests.
This commit is contained in:
parent
46967723bb
commit
b128aa0499
7 changed files with 270 additions and 13 deletions
|
@ -4740,6 +4740,16 @@ pub struct PythonFindArgs {
|
|||
|
||||
#[arg(long, overrides_with("system"), hide = true)]
|
||||
pub no_system: bool,
|
||||
|
||||
/// Find the environment for a Python script, rather than the current project.
|
||||
#[arg(
|
||||
long,
|
||||
conflicts_with = "request",
|
||||
conflicts_with = "no_project",
|
||||
conflicts_with = "system",
|
||||
conflicts_with = "no_system"
|
||||
)]
|
||||
pub script: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
|
@ -32,6 +32,7 @@ pub(crate) use project::tree::tree;
|
|||
pub(crate) use publish::publish;
|
||||
pub(crate) use python::dir::dir as python_dir;
|
||||
pub(crate) use python::find::find as python_find;
|
||||
pub(crate) use python::find::find_script as python_find_script;
|
||||
pub(crate) use python::install::install as python_install;
|
||||
pub(crate) use python::list::list as python_list;
|
||||
pub(crate) use python::pin::pin as python_pin;
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
use anstream::println;
|
||||
use anyhow::Result;
|
||||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use uv_cache::Cache;
|
||||
use uv_fs::Simplified;
|
||||
use uv_python::{EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest};
|
||||
use uv_python::{
|
||||
EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest,
|
||||
};
|
||||
use uv_scripts::Pep723ItemRef;
|
||||
use uv_settings::PythonInstallMirrors;
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceCache, WorkspaceError};
|
||||
|
||||
use crate::commands::{
|
||||
project::{validate_project_requires_python, WorkspacePython},
|
||||
project::{validate_project_requires_python, ScriptInterpreter, WorkspacePython},
|
||||
ExitStatus,
|
||||
};
|
||||
use crate::printer::Printer;
|
||||
use crate::settings::NetworkSettings;
|
||||
|
||||
/// Find a Python interpreter.
|
||||
pub(crate) async fn find(
|
||||
|
@ -88,3 +95,48 @@ pub(crate) async fn find(
|
|||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
||||
pub(crate) async fn find_script(
|
||||
script: Pep723ItemRef<'_>,
|
||||
network_settings: &NetworkSettings,
|
||||
python_preference: PythonPreference,
|
||||
python_downloads: PythonDownloads,
|
||||
no_config: bool,
|
||||
cache: &Cache,
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
match ScriptInterpreter::discover(
|
||||
script,
|
||||
None,
|
||||
network_settings,
|
||||
python_preference,
|
||||
python_downloads,
|
||||
&PythonInstallMirrors::default(),
|
||||
no_config,
|
||||
Some(false),
|
||||
cache,
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Err(error) => {
|
||||
writeln!(printer.stderr(), "{error}")?;
|
||||
|
||||
Ok(ExitStatus::Failure)
|
||||
}
|
||||
|
||||
Ok(ScriptInterpreter::Interpreter(interpreter)) => {
|
||||
let path = interpreter.sys_executable();
|
||||
println!("{}", std::path::absolute(path)?.simplified_display());
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
||||
Ok(ScriptInterpreter::Environment(environment)) => {
|
||||
let path = environment.interpreter().sys_executable();
|
||||
println!("{}", std::path::absolute(path)?.simplified_display());
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ use uv_pep508::VersionOrUrl;
|
|||
use uv_pypi_types::{ParsedDirectoryUrl, ParsedUrl};
|
||||
use uv_requirements::RequirementsSource;
|
||||
use uv_requirements_txt::RequirementsTxtRequirement;
|
||||
use uv_scripts::{Pep723Error, Pep723Item, Pep723Metadata, Pep723Script};
|
||||
use uv_scripts::{Pep723Error, Pep723Item, Pep723ItemRef, Pep723Metadata, Pep723Script};
|
||||
use uv_settings::{Combine, FilesystemOptions, Options};
|
||||
use uv_static::EnvVars;
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
|
@ -246,6 +246,32 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
},
|
||||
_ => None,
|
||||
}
|
||||
} else if let Commands::Python(uv_cli::PythonNamespace {
|
||||
command:
|
||||
PythonCommand::Find(uv_cli::PythonFindArgs {
|
||||
script: Some(script),
|
||||
..
|
||||
}),
|
||||
}) = &*cli.command
|
||||
{
|
||||
match Pep723Script::read(&script).await {
|
||||
Ok(Some(script)) => Some(Pep723Item::Script(script)),
|
||||
Ok(None) => {
|
||||
bail!(
|
||||
"`{}` does not contain a PEP 723 metadata tag; run `{}` to initialize the script",
|
||||
script.user_display().cyan(),
|
||||
format!("uv init --script {}", script.user_display()).green()
|
||||
)
|
||||
}
|
||||
Err(Pep723Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
bail!(
|
||||
"Failed to read `{}` (not found); run `{}` to create a PEP 723 script",
|
||||
script.user_display().cyan(),
|
||||
format!("uv init --script {}", script.user_display()).green()
|
||||
)
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -1306,16 +1332,29 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
||||
commands::python_find(
|
||||
&project_dir,
|
||||
args.request,
|
||||
args.no_project,
|
||||
cli.top_level.no_config,
|
||||
args.system,
|
||||
globals.python_preference,
|
||||
&cache,
|
||||
)
|
||||
.await
|
||||
if let Some(Pep723Item::Script(script)) = script {
|
||||
commands::python_find_script(
|
||||
Pep723ItemRef::Script(&script),
|
||||
&globals.network_settings,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
cli.top_level.no_config,
|
||||
&cache,
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
commands::python_find(
|
||||
&project_dir,
|
||||
args.request,
|
||||
args.no_project,
|
||||
cli.top_level.no_config,
|
||||
args.system,
|
||||
globals.python_preference,
|
||||
&cache,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
Commands::Python(PythonNamespace {
|
||||
command: PythonCommand::Pin(args),
|
||||
|
|
|
@ -976,6 +976,7 @@ impl PythonFindSettings {
|
|||
no_project,
|
||||
system,
|
||||
no_system,
|
||||
script: _,
|
||||
} = args;
|
||||
|
||||
Self {
|
||||
|
|
|
@ -707,3 +707,155 @@ fn python_required_python_major_minor() {
|
|||
error: No interpreter found for Python >3.11.[X], <3.12 in virtual environments, managed installations, or search path
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_find_script() {
|
||||
let context = TestContext::new("3.13")
|
||||
.with_filtered_exe_suffix()
|
||||
.with_filtered_virtualenv_bin()
|
||||
.with_filtered_python_names();
|
||||
let filters = context
|
||||
.filters()
|
||||
.into_iter()
|
||||
.chain(vec![(
|
||||
r"environments-v2/[\w-]+",
|
||||
"environments-v2/[HASHEDNAME]",
|
||||
)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
uv_snapshot!(filters, context.init().arg("--script").arg("foo.py"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Initialized script at `foo.py`
|
||||
"###);
|
||||
|
||||
uv_snapshot!(filters, context.sync().arg("--script").arg("foo.py"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Creating script environment at: [CACHE_DIR]/environments-v2/[HASHEDNAME]
|
||||
Resolved in [TIME]
|
||||
Audited in [TIME]
|
||||
");
|
||||
|
||||
uv_snapshot!(filters, context.python_find().arg("--script").arg("foo.py"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[CACHE_DIR]/environments-v2/[HASHEDNAME]/[BIN]/python
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_find_script_no_environment() {
|
||||
let context = TestContext::new("3.13")
|
||||
.with_filtered_exe_suffix()
|
||||
.with_filtered_virtualenv_bin()
|
||||
.with_filtered_python_names();
|
||||
|
||||
let script = context.temp_dir.child("foo.py");
|
||||
|
||||
script
|
||||
.write_str(indoc! {r"
|
||||
# /// script
|
||||
# dependencies = []
|
||||
# ///
|
||||
"})
|
||||
.unwrap();
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("--script").arg("foo.py"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[VENV]/[BIN]/python
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_find_script_python_not_found() {
|
||||
let context = TestContext::new_with_versions(&[]).with_filtered_python_sources();
|
||||
|
||||
let script = context.temp_dir.child("foo.py");
|
||||
|
||||
script
|
||||
.write_str(indoc! {r"
|
||||
# /// script
|
||||
# dependencies = []
|
||||
# ///
|
||||
"})
|
||||
.unwrap();
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("--script").arg("foo.py"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
No interpreter found in [PYTHON SOURCES]
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_find_script_no_such_version() {
|
||||
let context = TestContext::new("3.13")
|
||||
.with_filtered_exe_suffix()
|
||||
.with_filtered_virtualenv_bin()
|
||||
.with_filtered_python_names()
|
||||
.with_filtered_python_sources();
|
||||
let filters = context
|
||||
.filters()
|
||||
.into_iter()
|
||||
.chain(vec![(
|
||||
r"environments-v2/[\w-]+",
|
||||
"environments-v2/[HASHEDNAME]",
|
||||
)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let script = context.temp_dir.child("foo.py");
|
||||
script
|
||||
.write_str(indoc! {r#"
|
||||
# /// script
|
||||
# requires-python = ">=3.13"
|
||||
# dependencies = []
|
||||
# ///
|
||||
"#})
|
||||
.unwrap();
|
||||
|
||||
uv_snapshot!(filters, context.sync().arg("--script").arg("foo.py"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Creating script environment at: [CACHE_DIR]/environments-v2/[HASHEDNAME]
|
||||
Resolved in [TIME]
|
||||
Audited in [TIME]
|
||||
");
|
||||
|
||||
script
|
||||
.write_str(indoc! {r#"
|
||||
# /// script
|
||||
# requires-python = ">=3.14"
|
||||
# dependencies = []
|
||||
# ///
|
||||
"#})
|
||||
.unwrap();
|
||||
|
||||
uv_snapshot!(filters, context.python_find().arg("--script").arg("foo.py"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
No interpreter found for Python >=3.14 in [PYTHON SOURCES]
|
||||
");
|
||||
}
|
||||
|
|
|
@ -5018,6 +5018,8 @@ uv python find [OPTIONS] [REQUEST]
|
|||
|
||||
</dd><dt id="uv-python-find--quiet"><a href="#uv-python-find--quiet"><code>--quiet</code></a>, <code>-q</code></dt><dd><p>Do not print any output</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--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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue