mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-15 21:09:46 +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>),
|
||||
/// Execute a `python` script provided via `stdin`.
|
||||
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.
|
||||
PythonRemote(tempfile::NamedTempFile, Vec<OsString>),
|
||||
/// Execute an external command.
|
||||
|
@ -1209,6 +1211,13 @@ impl RunCommand {
|
|||
}
|
||||
}
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
@ -1280,6 +1289,38 @@ impl RunCommand {
|
|||
|
||||
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) => {
|
||||
let mut process = Command::new(executable);
|
||||
process.args(args);
|
||||
|
@ -1328,6 +1369,10 @@ impl std::fmt::Display for RunCommand {
|
|||
write!(f, "python -c")?;
|
||||
Ok(())
|
||||
}
|
||||
Self::PythonGuiStdin(..) => {
|
||||
write!(f, "pythonw -c")?;
|
||||
Ok(())
|
||||
}
|
||||
Self::External(executable, args) => {
|
||||
write!(f, "{}", executable.to_string_lossy())?;
|
||||
for arg in args {
|
||||
|
@ -1360,6 +1405,19 @@ impl RunCommand {
|
|||
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);
|
||||
|
||||
// Determine whether the user provided a remote script.
|
||||
|
@ -1402,21 +1460,17 @@ impl RunCommand {
|
|||
|
||||
if module {
|
||||
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 {
|
||||
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 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);
|
||||
|
||||
if target.eq_ignore_ascii_case("-") {
|
||||
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") {
|
||||
if target.eq_ignore_ascii_case("python") {
|
||||
Ok(Self::Python(args.to_vec()))
|
||||
} else if target_path
|
||||
.extension()
|
||||
|
|
|
@ -175,9 +175,9 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
Some(RunCommand::PythonRemote(script, _)) => {
|
||||
Pep723Metadata::read(&script).await?.map(Pep723Item::Remote)
|
||||
}
|
||||
Some(RunCommand::PythonStdin(contents, _)) => {
|
||||
Pep723Metadata::parse(contents)?.map(Pep723Item::Stdin)
|
||||
}
|
||||
Some(
|
||||
RunCommand::PythonStdin(contents, _) | RunCommand::PythonGuiStdin(contents, _),
|
||||
) => Pep723Metadata::parse(contents)?.map(Pep723Item::Stdin),
|
||||
_ => None,
|
||||
}
|
||||
} 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.
|
||||
#[test]
|
||||
fn run_project_toml_error() -> Result<()> {
|
||||
|
@ -2874,6 +2888,40 @@ fn run_script_explicit() -> Result<()> {
|
|||
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]
|
||||
fn run_script_explicit_no_file() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
@ -2942,6 +2990,41 @@ fn run_gui_script_explicit_windows() -> Result<()> {
|
|||
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]
|
||||
#[cfg(not(windows))]
|
||||
fn run_gui_script_explicit_unix() -> Result<()> {
|
||||
|
@ -2974,6 +3057,41 @@ fn run_gui_script_explicit_unix() -> Result<()> {
|
|||
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]
|
||||
fn run_remote_pep723_script() {
|
||||
let context = TestContext::new("3.12").with_filtered_python_names();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue