Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions crates/uv-requirements/src/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ impl RequirementsSource {

/// Parse a [`RequirementsSource`] from a `requirements.txt` file.
pub fn from_requirements_txt(path: PathBuf) -> Result<Self> {
if path == Path::new("-") {
return Ok(Self::Extensionless(path));
}

for file_name in ["pyproject.toml", "setup.py", "setup.cfg"] {
if path.ends_with(file_name) {
return Err(anyhow::anyhow!(
Expand Down Expand Up @@ -110,6 +114,10 @@ impl RequirementsSource {

/// Parse a [`RequirementsSource`] from a `constraints.txt` file.
pub fn from_constraints_txt(path: PathBuf) -> Result<Self> {
if path == Path::new("-") {
return Ok(Self::Extensionless(path));
}

for file_name in ["pyproject.toml", "setup.py", "setup.cfg"] {
if path.ends_with(file_name) {
return Err(anyhow::anyhow!(
Expand Down Expand Up @@ -142,6 +150,10 @@ impl RequirementsSource {

/// Parse a [`RequirementsSource`] from an `overrides.txt` file.
pub fn from_overrides_txt(path: PathBuf) -> Result<Self> {
if path == Path::new("-") {
return Ok(Self::Extensionless(path));
}

for file_name in ["pyproject.toml", "setup.py", "setup.cfg"] {
if path.ends_with(file_name) {
return Err(anyhow::anyhow!(
Expand Down
121 changes: 121 additions & 0 deletions crates/uv/tests/it/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3561,6 +3561,40 @@ fn install_constraints_txt() -> Result<()> {
Ok(())
}

/// Install a package from a `requirements.txt` file, with a `constraints.txt` file.
#[test]
#[allow(clippy::disallowed_types)]
fn install_constraints_txt_from_stdin() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("anyio==3.7.0")?;

let constraints_txt = context.temp_dir.child("constraints.txt");
constraints_txt.write_str("idna<3.4")?;

uv_snapshot!(context.pip_install()
.arg("-r")
.arg("requirements.txt")
.arg("--constraint")
.arg("-")
.stdin(std::fs::File::open(constraints_txt)?), @r###"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 3 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==3.7.0
+ idna==3.3
+ sniffio==1.3.1
"###
);

Ok(())
}

/// Check that `tool.uv.constraint-dependencies` in `pyproject.toml` is respected.
#[test]
fn install_constraints_from_pyproject() -> Result<()> {
Expand Down Expand Up @@ -7303,6 +7337,64 @@ fn require_hashes_override() -> Result<()> {
Ok(())
}

/// Install with overrides from stdin.
#[test]
#[allow(clippy::disallowed_types)]
fn install_with_overrides_from_stdin() -> Result<()> {
let context = TestContext::new("3.12");

let overrides_txt = context.temp_dir.child("overrides.txt");
overrides_txt.write_str("anyio==4.0.0")?;

uv_snapshot!(context.pip_install()
.arg("anyio==4.0.1")
.arg("--override")
.arg("-")
.stdin(std::fs::File::open(overrides_txt)?), @r###"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 3 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.0.0
+ idna==3.6
+ sniffio==1.3.1
"###
);

Ok(())
}

/// Install with excludes from stdin.
#[test]
#[allow(clippy::disallowed_types)]
fn install_with_excludes_from_stdin() -> Result<()> {
let context = TestContext::new("3.12");

let excludes_txt = context.temp_dir.child("excludes.txt");
excludes_txt.write_str("anyio>4.0.0")?;

uv_snapshot!(context.pip_install()
.arg("anyio==4.0.1")
.arg("--exclude")
.arg("-")
.stdin(std::fs::File::open(excludes_txt)?), @r###"
success: false
exit_code: 1
----- stdout -----

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ 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.
"###
);

Ok(())
}

/// Provide valid hashes for all dependencies with `--require-hashes` with accompanying markers.
/// Critically, one package (`requests`) depends on another (`urllib3`).
#[test]
Expand Down Expand Up @@ -8602,6 +8694,35 @@ fn incompatible_build_constraint() -> Result<()> {
Ok(())
}

/// Include a `build_constraints.txt` file with an incompatible constraint from stdin.
#[test]
#[allow(clippy::disallowed_types)]
fn incompatible_build_constraint_from_stdin() -> Result<()> {
let context = TestContext::new(DEFAULT_PYTHON_VERSION);

let constraints_txt = context.temp_dir.child("build_constraints.txt");
constraints_txt.write_str("setuptools==1")?;

uv_snapshot!(context.pip_install()
.arg("requests==1.2")
.arg("--build-constraint")
.arg("-")
.stdin(std::fs::File::open(constraints_txt)?), @r###"
success: false
exit_code: 1
----- stdout -----

----- stderr -----
× Failed to download and build `requests==1.2.0`
├─▶ Failed to resolve requirements from `setup.py` build
├─▶ No solution found when resolving: `setuptools>=40.8.0`
╰─▶ Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that your requirements are unsatisfiable.
"###
);

Ok(())
}

/// Include a `build_constraints.txt` file with a compatible constraint.
#[test]
fn compatible_build_constraint() -> Result<()> {
Expand Down
Loading