From 1f8da58b388452a6aa2d4b9476cb6272eed3f3f9 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 9 Jan 2025 15:36:50 -0600 Subject: [PATCH] Allow reading `--with-requirements` from stdin in `uv add` and `uv run` --- crates/uv/src/commands/project/add.rs | 5 ---- crates/uv/src/commands/project/run.rs | 15 ++++++++++- crates/uv/tests/it/edit.rs | 13 +++++++++ crates/uv/tests/it/run.rs | 38 ++++++++++++++++++++++++--- 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 227bf676faed..0191c3a0745c 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -98,11 +98,6 @@ pub(crate) async fn add( RequirementsSource::SetupCfg(_) => { bail!("Adding requirements from a `setup.cfg` is not supported in `uv add`"); } - RequirementsSource::RequirementsTxt(path) => { - if path == Path::new("-") { - bail!("Reading requirements from stdin is not supported in `uv add`"); - } - } _ => {} } } diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 27a2e22b1bcb..dd327ede6c91 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -93,6 +93,7 @@ pub(crate) async fn run( ) -> anyhow::Result { // These cases seem quite complex because (in theory) they should change the "current package". // Let's ban them entirely for now. + let mut requirements_from_stdin: bool = false; for source in &requirements { match source { RequirementsSource::PyprojectToml(_) => { @@ -106,13 +107,22 @@ pub(crate) async fn run( } RequirementsSource::RequirementsTxt(path) => { if path == Path::new("-") { - bail!("Reading requirements from stdin is not supported in `uv run`"); + requirements_from_stdin = true; } } _ => {} } } + // Fail early if stdin is used for multiple purposes. + if matches!( + command, + Some(RunCommand::PythonStdin(..) | RunCommand::PythonGuiStdin(..)) + ) && requirements_from_stdin + { + bail!("Cannot read both requirements file and script from stdin"); + } + // Initialize any shared state. let state = SharedState::default(); @@ -169,6 +179,9 @@ pub(crate) async fn run( )?; } Pep723Item::Stdin(_) => { + if requirements_from_stdin { + bail!("Cannot read both requirements file and script from stdin"); + } writeln!( printer.stderr(), "Reading inline script metadata from `{}`", diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs index e3e1d8b39012..537f2f83d454 100644 --- a/crates/uv/tests/it/edit.rs +++ b/crates/uv/tests/it/edit.rs @@ -1,3 +1,5 @@ +#![allow(clippy::disallowed_types)] + use anyhow::Result; use assert_cmd::assert::OutputAssertExt; use assert_fs::prelude::*; @@ -4611,6 +4613,17 @@ fn add_requirements_file() -> Result<()> { ); }); + // Passing stdin should succeed + uv_snapshot!(context.filters(), context.add().arg("-r").arg("-").stdin(std::fs::File::open(requirements_txt)?), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved [N] packages in [TIME] + Audited [N] packages in [TIME] + "###); + // Passing a `setup.py` should fail. uv_snapshot!(context.filters(), context.add().arg("-r").arg("setup.py"), @r###" success: false diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs index fd506a7a2853..d915d58522b7 100644 --- a/crates/uv/tests/it/run.rs +++ b/crates/uv/tests/it/run.rs @@ -2124,19 +2124,51 @@ fn run_requirements_txt() -> Result<()> { + sniffio==1.3.1 "###); - // But reject `-` as a requirements file. + // Allow `-` for stdin. uv_snapshot!(context.filters(), context.run() .arg("--with-requirements") .arg("-") .arg("--with") .arg("iniconfig") - .arg("main.py"), @r###" + .arg("main.py") + .stdin(std::fs::File::open(&requirements_txt)?), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 6 packages in [TIME] + Audited 4 packages in [TIME] + Resolved 2 packages in [TIME] + "###); + + // But not in combination with reading the script from stdin + uv_snapshot!(context.filters(), context.run() + .arg("--with-requirements") + .arg("-") + // The script to run + .arg("-") + .stdin(std::fs::File::open(&requirements_txt)?), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Cannot read both requirements file and script from stdin + "###); + + uv_snapshot!(context.filters(), context.run() + .arg("--with-requirements") + .arg("-") + .arg("--script") + .arg("-") + .stdin(std::fs::File::open(&requirements_txt)?), @r###" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - error: Reading requirements from stdin is not supported in `uv run` + error: Cannot read both requirements file and script from stdin "###); Ok(())