Skip to content

Commit cc4600c

Browse files
authored
Fix dropped support of - in pip constraints/overrides/excludes/build_constraints (#17188)
Since #16923, `-` stdin paths are suddenly only supported on the `RequirementsSource::Extensionless`. However, parsing of cli arguments using `from_requirements_txt`, `from_constraints_txt` `from_overrides_txt` would always output a `RequirementsSource::RequirementsTxt`. Resulting in the error: ``` $ cat overrides.txt | cargo run --bin uv --profile dev --manifest-path ./uv/crates/uv/Cargo.toml pip install 'numpy' --overrides=- Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s Running `./uv/target/debug/uv pip install 'numpy' --overrides=-` error: File not found: `-` ``` In this PR, I've added a small check in those for the `-` paths to use `RequirementsSource::ExtensionLess`. I'm not too sure about this change though, as it would also implicitly start allowing PEP 723 scripts as input to overrides/constraints. I don't see the direct issue in that, but then maybe we should explicitly handle it so that an `--overrides=script.py` would also be supported. @zanieb what do you think? Relates to #17227
1 parent 7865672 commit cc4600c

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

crates/uv-requirements/src/sources.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ impl RequirementsSource {
7878

7979
/// Parse a [`RequirementsSource`] from a `requirements.txt` file.
8080
pub fn from_requirements_txt(path: PathBuf) -> Result<Self> {
81+
if path == Path::new("-") {
82+
return Ok(Self::Extensionless(path));
83+
}
84+
8185
for file_name in ["pyproject.toml", "setup.py", "setup.cfg"] {
8286
if path.ends_with(file_name) {
8387
return Err(anyhow::anyhow!(
@@ -110,6 +114,10 @@ impl RequirementsSource {
110114

111115
/// Parse a [`RequirementsSource`] from a `constraints.txt` file.
112116
pub fn from_constraints_txt(path: PathBuf) -> Result<Self> {
117+
if path == Path::new("-") {
118+
return Ok(Self::Extensionless(path));
119+
}
120+
113121
for file_name in ["pyproject.toml", "setup.py", "setup.cfg"] {
114122
if path.ends_with(file_name) {
115123
return Err(anyhow::anyhow!(
@@ -142,6 +150,10 @@ impl RequirementsSource {
142150

143151
/// Parse a [`RequirementsSource`] from an `overrides.txt` file.
144152
pub fn from_overrides_txt(path: PathBuf) -> Result<Self> {
153+
if path == Path::new("-") {
154+
return Ok(Self::Extensionless(path));
155+
}
156+
145157
for file_name in ["pyproject.toml", "setup.py", "setup.cfg"] {
146158
if path.ends_with(file_name) {
147159
return Err(anyhow::anyhow!(

crates/uv/tests/it/pip_install.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3623,6 +3623,40 @@ fn install_constraints_txt() -> Result<()> {
36233623
Ok(())
36243624
}
36253625

3626+
/// Install a package from a `requirements.txt` file, with a `constraints.txt` file.
3627+
#[test]
3628+
#[allow(clippy::disallowed_types)]
3629+
fn install_constraints_txt_from_stdin() -> Result<()> {
3630+
let context = TestContext::new("3.12");
3631+
let requirements_txt = context.temp_dir.child("requirements.txt");
3632+
requirements_txt.write_str("anyio==3.7.0")?;
3633+
3634+
let constraints_txt = context.temp_dir.child("constraints.txt");
3635+
constraints_txt.write_str("idna<3.4")?;
3636+
3637+
uv_snapshot!(context.pip_install()
3638+
.arg("-r")
3639+
.arg("requirements.txt")
3640+
.arg("--constraint")
3641+
.arg("-")
3642+
.stdin(std::fs::File::open(constraints_txt)?), @r###"
3643+
success: true
3644+
exit_code: 0
3645+
----- stdout -----
3646+
3647+
----- stderr -----
3648+
Resolved 3 packages in [TIME]
3649+
Prepared 3 packages in [TIME]
3650+
Installed 3 packages in [TIME]
3651+
+ anyio==3.7.0
3652+
+ idna==3.3
3653+
+ sniffio==1.3.1
3654+
"###
3655+
);
3656+
3657+
Ok(())
3658+
}
3659+
36263660
/// Check that `tool.uv.constraint-dependencies` in `pyproject.toml` is respected.
36273661
#[test]
36283662
fn install_constraints_from_pyproject() -> Result<()> {
@@ -7365,6 +7399,64 @@ fn require_hashes_override() -> Result<()> {
73657399
Ok(())
73667400
}
73677401

7402+
/// Install with overrides from stdin.
7403+
#[test]
7404+
#[allow(clippy::disallowed_types)]
7405+
fn install_with_overrides_from_stdin() -> Result<()> {
7406+
let context = TestContext::new("3.12");
7407+
7408+
let overrides_txt = context.temp_dir.child("overrides.txt");
7409+
overrides_txt.write_str("anyio==4.0.0")?;
7410+
7411+
uv_snapshot!(context.pip_install()
7412+
.arg("anyio==4.0.1")
7413+
.arg("--override")
7414+
.arg("-")
7415+
.stdin(std::fs::File::open(overrides_txt)?), @r###"
7416+
success: true
7417+
exit_code: 0
7418+
----- stdout -----
7419+
7420+
----- stderr -----
7421+
Resolved 3 packages in [TIME]
7422+
Prepared 3 packages in [TIME]
7423+
Installed 3 packages in [TIME]
7424+
+ anyio==4.0.0
7425+
+ idna==3.6
7426+
+ sniffio==1.3.1
7427+
"###
7428+
);
7429+
7430+
Ok(())
7431+
}
7432+
7433+
/// Install with excludes from stdin.
7434+
#[test]
7435+
#[allow(clippy::disallowed_types)]
7436+
fn install_with_excludes_from_stdin() -> Result<()> {
7437+
let context = TestContext::new("3.12");
7438+
7439+
let excludes_txt = context.temp_dir.child("excludes.txt");
7440+
excludes_txt.write_str("anyio>4.0.0")?;
7441+
7442+
uv_snapshot!(context.pip_install()
7443+
.arg("anyio==4.0.1")
7444+
.arg("--exclude")
7445+
.arg("-")
7446+
.stdin(std::fs::File::open(excludes_txt)?), @r###"
7447+
success: false
7448+
exit_code: 1
7449+
----- stdout -----
7450+
7451+
----- stderr -----
7452+
× No solution found when resolving dependencies:
7453+
╰─▶ Because there is no version of anyio==4.0.1 and you require anyio==4.0.1, we can conclude that your requirements are unsatisfiable.
7454+
"###
7455+
);
7456+
7457+
Ok(())
7458+
}
7459+
73687460
/// Provide valid hashes for all dependencies with `--require-hashes` with accompanying markers.
73697461
/// Critically, one package (`requests`) depends on another (`urllib3`).
73707462
#[test]
@@ -8664,6 +8756,35 @@ fn incompatible_build_constraint() -> Result<()> {
86648756
Ok(())
86658757
}
86668758

8759+
/// Include a `build_constraints.txt` file with an incompatible constraint from stdin.
8760+
#[test]
8761+
#[allow(clippy::disallowed_types)]
8762+
fn incompatible_build_constraint_from_stdin() -> Result<()> {
8763+
let context = TestContext::new(DEFAULT_PYTHON_VERSION);
8764+
8765+
let constraints_txt = context.temp_dir.child("build_constraints.txt");
8766+
constraints_txt.write_str("setuptools==1")?;
8767+
8768+
uv_snapshot!(context.pip_install()
8769+
.arg("requests==1.2")
8770+
.arg("--build-constraint")
8771+
.arg("-")
8772+
.stdin(std::fs::File::open(constraints_txt)?), @r###"
8773+
success: false
8774+
exit_code: 1
8775+
----- stdout -----
8776+
8777+
----- stderr -----
8778+
× Failed to download and build `requests==1.2.0`
8779+
├─▶ Failed to resolve requirements from `setup.py` build
8780+
├─▶ No solution found when resolving: `setuptools>=40.8.0`
8781+
╰─▶ Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that your requirements are unsatisfiable.
8782+
"###
8783+
);
8784+
8785+
Ok(())
8786+
}
8787+
86678788
/// Include a `build_constraints.txt` file with a compatible constraint.
86688789
#[test]
86698790
fn compatible_build_constraint() -> Result<()> {

0 commit comments

Comments
 (0)