mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-17 05:47:45 +00:00
Allow --script
to be provided with uv run -
(#10035)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (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 / check windows trampoline | x86_64 (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 / build binary | linux (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 (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 | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / integration test | conda 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 / 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 | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | alpine (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 (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 (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (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 linux (push) Blocked by required conditions
CI / check system | conda3.8 on linux (push) Blocked by required conditions
CI / check system | conda3.11 on macos (push) Blocked by required conditions
CI / check system | conda3.8 on macos (push) Blocked by required conditions
CI / check system | conda3.11 on windows (push) Blocked by required conditions
CI / check system | conda3.8 on windows (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (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 / check windows trampoline | x86_64 (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 / build binary | linux (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 (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 | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / integration test | conda 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 / 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 | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | alpine (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 (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 (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (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 linux (push) Blocked by required conditions
CI / check system | conda3.8 on linux (push) Blocked by required conditions
CI / check system | conda3.11 on macos (push) Blocked by required conditions
CI / check system | conda3.8 on macos (push) Blocked by required conditions
CI / check system | conda3.11 on windows (push) Blocked by required conditions
CI / check system | conda3.8 on windows (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
## Summary Closes #10021.
This commit is contained in:
parent
5a3826d9ff
commit
4513ce0e2a
3 changed files with 182 additions and 10 deletions
|
@ -1182,6 +1182,8 @@ pub(crate) enum RunCommand {
|
||||||
PythonZipapp(PathBuf, Vec<OsString>),
|
PythonZipapp(PathBuf, Vec<OsString>),
|
||||||
/// Execute a `python` script provided via `stdin`.
|
/// Execute a `python` script provided via `stdin`.
|
||||||
PythonStdin(Vec<u8>, Vec<OsString>),
|
PythonStdin(Vec<u8>, Vec<OsString>),
|
||||||
|
/// Execute a `pythonw` script provided via `stdin`.
|
||||||
|
PythonGuiStdin(Vec<u8>, Vec<OsString>),
|
||||||
/// Execute a Python script provided via a remote URL.
|
/// Execute a Python script provided via a remote URL.
|
||||||
PythonRemote(tempfile::NamedTempFile, Vec<OsString>),
|
PythonRemote(tempfile::NamedTempFile, Vec<OsString>),
|
||||||
/// Execute an external command.
|
/// Execute an external command.
|
||||||
|
@ -1209,6 +1211,13 @@ impl RunCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::PythonStdin(..) => Cow::Borrowed("python -c"),
|
Self::PythonStdin(..) => Cow::Borrowed("python -c"),
|
||||||
|
Self::PythonGuiStdin(..) => {
|
||||||
|
if cfg!(windows) {
|
||||||
|
Cow::Borrowed("pythonw -c")
|
||||||
|
} else {
|
||||||
|
Cow::Borrowed("python -c")
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::External(executable, _) => executable.to_string_lossy(),
|
Self::External(executable, _) => executable.to_string_lossy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1280,6 +1289,38 @@ impl RunCommand {
|
||||||
|
|
||||||
process
|
process
|
||||||
}
|
}
|
||||||
|
Self::PythonGuiStdin(script, args) => {
|
||||||
|
let python_executable = interpreter.sys_executable();
|
||||||
|
|
||||||
|
// Use `pythonw.exe` if it exists, otherwise fall back to `python.exe`.
|
||||||
|
// See `install-wheel-rs::get_script_executable`.gd
|
||||||
|
let pythonw_executable = python_executable
|
||||||
|
.file_name()
|
||||||
|
.map(|name| {
|
||||||
|
let new_name = name.to_string_lossy().replace("python", "pythonw");
|
||||||
|
python_executable.with_file_name(new_name)
|
||||||
|
})
|
||||||
|
.filter(|path| path.is_file())
|
||||||
|
.unwrap_or_else(|| python_executable.to_path_buf());
|
||||||
|
|
||||||
|
let mut process = Command::new(&pythonw_executable);
|
||||||
|
process.arg("-c");
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::ffi::OsStringExt;
|
||||||
|
process.arg(OsString::from_vec(script.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
{
|
||||||
|
let script = String::from_utf8(script.clone()).expect("script is valid UTF-8");
|
||||||
|
process.arg(script);
|
||||||
|
}
|
||||||
|
process.args(args);
|
||||||
|
|
||||||
|
process
|
||||||
|
}
|
||||||
Self::External(executable, args) => {
|
Self::External(executable, args) => {
|
||||||
let mut process = Command::new(executable);
|
let mut process = Command::new(executable);
|
||||||
process.args(args);
|
process.args(args);
|
||||||
|
@ -1328,6 +1369,10 @@ impl std::fmt::Display for RunCommand {
|
||||||
write!(f, "python -c")?;
|
write!(f, "python -c")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Self::PythonGuiStdin(..) => {
|
||||||
|
write!(f, "pythonw -c")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Self::External(executable, args) => {
|
Self::External(executable, args) => {
|
||||||
write!(f, "{}", executable.to_string_lossy())?;
|
write!(f, "{}", executable.to_string_lossy())?;
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
@ -1360,6 +1405,19 @@ impl RunCommand {
|
||||||
return Ok(Self::Empty);
|
return Ok(Self::Empty);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if target.eq_ignore_ascii_case("-") {
|
||||||
|
let mut buf = Vec::with_capacity(1024);
|
||||||
|
std::io::stdin().read_to_end(&mut buf)?;
|
||||||
|
|
||||||
|
return if module {
|
||||||
|
Err(anyhow!("Cannot run a Python module from stdin"))
|
||||||
|
} else if gui_script {
|
||||||
|
Ok(Self::PythonGuiStdin(buf, args.to_vec()))
|
||||||
|
} else {
|
||||||
|
Ok(Self::PythonStdin(buf, args.to_vec()))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let target_path = PathBuf::from(target);
|
let target_path = PathBuf::from(target);
|
||||||
|
|
||||||
// Determine whether the user provided a remote script.
|
// Determine whether the user provided a remote script.
|
||||||
|
@ -1402,21 +1460,17 @@ impl RunCommand {
|
||||||
|
|
||||||
if module {
|
if module {
|
||||||
return Ok(Self::PythonModule(target.clone(), args.to_vec()));
|
return Ok(Self::PythonModule(target.clone(), args.to_vec()));
|
||||||
} else if script {
|
|
||||||
return Ok(Self::PythonScript(target.clone().into(), args.to_vec()));
|
|
||||||
} else if gui_script {
|
} else if gui_script {
|
||||||
return Ok(Self::PythonGuiScript(target.clone().into(), args.to_vec()));
|
return Ok(Self::PythonGuiScript(target.clone().into(), args.to_vec()));
|
||||||
|
} else if script {
|
||||||
|
return Ok(Self::PythonScript(target.clone().into(), args.to_vec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = target_path.metadata();
|
let metadata = target_path.metadata();
|
||||||
let is_file = metadata.as_ref().map_or(false, std::fs::Metadata::is_file);
|
let is_file = metadata.as_ref().map_or(false, std::fs::Metadata::is_file);
|
||||||
let is_dir = metadata.as_ref().map_or(false, std::fs::Metadata::is_dir);
|
let is_dir = metadata.as_ref().map_or(false, std::fs::Metadata::is_dir);
|
||||||
|
|
||||||
if target.eq_ignore_ascii_case("-") {
|
if target.eq_ignore_ascii_case("python") {
|
||||||
let mut buf = Vec::with_capacity(1024);
|
|
||||||
std::io::stdin().read_to_end(&mut buf)?;
|
|
||||||
Ok(Self::PythonStdin(buf, args.to_vec()))
|
|
||||||
} else if target.eq_ignore_ascii_case("python") {
|
|
||||||
Ok(Self::Python(args.to_vec()))
|
Ok(Self::Python(args.to_vec()))
|
||||||
} else if target_path
|
} else if target_path
|
||||||
.extension()
|
.extension()
|
||||||
|
|
|
@ -175,9 +175,9 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
||||||
Some(RunCommand::PythonRemote(script, _)) => {
|
Some(RunCommand::PythonRemote(script, _)) => {
|
||||||
Pep723Metadata::read(&script).await?.map(Pep723Item::Remote)
|
Pep723Metadata::read(&script).await?.map(Pep723Item::Remote)
|
||||||
}
|
}
|
||||||
Some(RunCommand::PythonStdin(contents, _)) => {
|
Some(
|
||||||
Pep723Metadata::parse(contents)?.map(Pep723Item::Stdin)
|
RunCommand::PythonStdin(contents, _) | RunCommand::PythonGuiStdin(contents, _),
|
||||||
}
|
) => Pep723Metadata::parse(contents)?.map(Pep723Item::Stdin),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
} else if let ProjectCommand::Remove(uv_cli::RemoveArgs {
|
} else if let ProjectCommand::Remove(uv_cli::RemoveArgs {
|
||||||
|
|
|
@ -2544,6 +2544,20 @@ fn run_module() {
|
||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_module_stdin() {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("-m").arg("-"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Cannot run a Python module from stdin
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
/// When the `pyproject.toml` file is invalid.
|
/// When the `pyproject.toml` file is invalid.
|
||||||
#[test]
|
#[test]
|
||||||
fn run_project_toml_error() -> Result<()> {
|
fn run_project_toml_error() -> Result<()> {
|
||||||
|
@ -2874,6 +2888,40 @@ fn run_script_explicit() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_script_explicit_stdin() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("script");
|
||||||
|
test_script.write_str(indoc! { r#"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.11"
|
||||||
|
# dependencies = [
|
||||||
|
# "iniconfig",
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
|
import iniconfig
|
||||||
|
print("Hello, world!")
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--script").arg("-").stdin(std::fs::File::open(test_script)?), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Hello, world!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Reading inline script metadata from `stdin`
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_script_explicit_no_file() {
|
fn run_script_explicit_no_file() {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
@ -2942,6 +2990,41 @@ fn run_gui_script_explicit_windows() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn run_gui_script_explicit_stdin_windows() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("script");
|
||||||
|
test_script.write_str(indoc! { r#"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.11"
|
||||||
|
# dependencies = [
|
||||||
|
# "iniconfig",
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
|
import iniconfig
|
||||||
|
print("Hello, world!")
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--gui-script").arg("-").stdin(std::fs::File::open(test_script)?), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Hello, world!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Reading inline script metadata from `stdin`
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn run_gui_script_explicit_unix() -> Result<()> {
|
fn run_gui_script_explicit_unix() -> Result<()> {
|
||||||
|
@ -2974,6 +3057,41 @@ fn run_gui_script_explicit_unix() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn run_gui_script_explicit_stdin_unix() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("script");
|
||||||
|
test_script.write_str(indoc! { r#"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.11"
|
||||||
|
# dependencies = [
|
||||||
|
# "iniconfig",
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
|
import iniconfig
|
||||||
|
print("Hello, world!")
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--gui-script").arg("-").stdin(std::fs::File::open(test_script)?), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Hello, world!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Reading inline script metadata from `stdin`
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_remote_pep723_script() {
|
fn run_remote_pep723_script() {
|
||||||
let context = TestContext::new("3.12").with_filtered_python_names();
|
let context = TestContext::new("3.12").with_filtered_python_names();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue