Fix ``uv pip install -r /dev/stdin`` (#16855)

## Summary

Fix ``uv pip install -r /dev/stdin`` which was broken in uv 0.9.12 by
https://github.com/astral-sh/uv/pull/16805 .

Example of the issue:

```
$ echo "flask" | uv pip install -r /dev/stdin
warning: Requirements file `/dev/stdin` does not contain any dependencies
Audited in 8ms
```

Note that "upstream" ``pip install`` does support `-r /dev/stdin` and
doesn't support `-r -` .

## Test Plan

2 new tests added.
This commit is contained in:
Nicola Soranzo 2025-11-26 03:13:32 +00:00 committed by GitHub
parent bfdee80f6c
commit 4bb219f8b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 2 deletions

View File

@ -65,7 +65,7 @@ impl RequirementsSource {
.is_some_and(|ext| ext.eq_ignore_ascii_case("txt") || ext.eq_ignore_ascii_case("in")) .is_some_and(|ext| ext.eq_ignore_ascii_case("txt") || ext.eq_ignore_ascii_case("in"))
{ {
Ok(Self::RequirementsTxt(path)) Ok(Self::RequirementsTxt(path))
} else if path == Path::new("-") { } else if path == Path::new("-") || path == Path::new("/dev/stdin") {
// If the path is `-`, treat it as a requirements.txt file from stdin. // If the path is `-`, treat it as a requirements.txt file from stdin.
Ok(Self::RequirementsTxt(path)) Ok(Self::RequirementsTxt(path))
} else if path.extension().is_none() { } else if path.extension().is_none() {

View File

@ -555,7 +555,7 @@ fn install_requirements_txt() -> Result<()> {
context.assert_command("import flask").success(); context.assert_command("import flask").success();
// Install Jinja2 (which should already be installed, but shouldn't remove other packages). // Install iniconfig (which shouldn't remove other packages).
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("iniconfig")?; requirements_txt.write_str("iniconfig")?;
@ -580,6 +580,81 @@ fn install_requirements_txt() -> Result<()> {
Ok(()) Ok(())
} }
/// Install a package from a `requirements.txt` passed via `-r -` into a virtual environment.
#[test]
#[allow(clippy::disallowed_types)]
fn install_from_stdin() -> Result<()> {
let context = TestContext::new("3.12");
// Install Flask.
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("Flask")?;
uv_snapshot!(context.pip_install()
.arg("-r")
.arg("-")
.arg("--strict").stdin(std::fs::File::open(requirements_txt)?), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 7 packages in [TIME]
Prepared 7 packages in [TIME]
Installed 7 packages in [TIME]
+ blinker==1.7.0
+ click==8.1.7
+ flask==3.0.2
+ itsdangerous==2.1.2
+ jinja2==3.1.3
+ markupsafe==2.1.5
+ werkzeug==3.0.1
"###
);
context.assert_command("import flask").success();
Ok(())
}
/// Install a package from a `requirements.txt` passed via `-r /dev/stdin` into a virtual environment.
#[test]
#[cfg(not(windows))]
#[allow(clippy::disallowed_types)]
fn install_from_dev_stdin() -> Result<()> {
let context = TestContext::new("3.12");
// Install Flask.
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("Flask")?;
uv_snapshot!(context.pip_install()
.arg("-r")
.arg("/dev/stdin")
.arg("--strict").stdin(std::fs::File::open(requirements_txt)?), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 7 packages in [TIME]
Prepared 7 packages in [TIME]
Installed 7 packages in [TIME]
+ blinker==1.7.0
+ click==8.1.7
+ flask==3.0.2
+ itsdangerous==2.1.2
+ jinja2==3.1.3
+ markupsafe==2.1.5
+ werkzeug==3.0.1
"###
);
context.assert_command("import flask").success();
Ok(())
}
/// Install a package from a remote `requirements.txt` into a virtual environment. /// Install a package from a remote `requirements.txt` into a virtual environment.
#[tokio::test] #[tokio::test]
async fn install_remote_requirements_txt() -> Result<()> { async fn install_remote_requirements_txt() -> Result<()> {