mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-19 03:05:06 +00:00

This PR builds on #780 by making both version parsing faster, and perhaps more importantly, making version comparisons much faster. Overall, these changes result in a considerable improvement for the `boto3.in` workload. Here's the status quo: ``` $ time puffin pip-compile --no-build --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/requirements/boto3.in Resolved 31 packages in 34.56s real 34.579 user 34.004 sys 0.413 maxmem 2867 MB faults 0 ``` And now with this PR: ``` $ time puffin pip-compile --no-build --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/requirements/boto3.in Resolved 31 packages in 9.20s real 9.218 user 8.919 sys 0.165 maxmem 463 MB faults 0 ``` This particular workload gets stuck in pubgrub doing resolution, and thus benefits mightily from a faster `Version::cmp` routine. With that said, this change does also help a fair bit with "normal" runs: ``` $ hyperfine -w10 \ "puffin-base pip-compile --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/benchmarks/requirements.in" \ "puffin-cmparc pip-compile --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/benchmarks/requirements.in" Benchmark 1: puffin-base pip-compile --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/benchmarks/requirements.in Time (mean ± σ): 337.5 ms ± 3.9 ms [User: 310.5 ms, System: 73.2 ms] Range (min … max): 333.6 ms … 343.4 ms 10 runs Benchmark 2: puffin-cmparc pip-compile --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/benchmarks/requirements.in Time (mean ± σ): 189.8 ms ± 3.0 ms [User: 168.1 ms, System: 78.4 ms] Range (min … max): 185.0 ms … 196.2 ms 15 runs Summary puffin-cmparc pip-compile --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/benchmarks/requirements.in ran 1.78 ± 0.03 times faster than puffin-base pip-compile --cache-dir ~/astral/tmp/cache/ -o /dev/null ./scripts/benchmarks/requirements.in ``` There is perhaps some future work here (detailed in the commit messages), but I suspect it would be more fruitful to explore ways of making resolution itself and/or deserialization faster. Fixes #373, Closes #396
2814 lines
90 KiB
Rust
2814 lines
90 KiB
Rust
#![cfg(all(feature = "python", feature = "pypi"))]
|
||
|
||
use std::iter;
|
||
use std::path::PathBuf;
|
||
use std::process::Command;
|
||
|
||
use anyhow::{bail, Context, Result};
|
||
use assert_cmd::prelude::*;
|
||
use assert_fs::prelude::*;
|
||
use assert_fs::TempDir;
|
||
use indoc::indoc;
|
||
use insta_cmd::_macro_support::insta;
|
||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||
|
||
use common::{create_venv_py312, BIN_NAME, INSTA_FILTERS};
|
||
|
||
mod common;
|
||
|
||
// Exclude any packages uploaded after this date.
|
||
static EXCLUDE_NEWER: &str = "2023-11-18T12:00:00Z";
|
||
|
||
/// Resolve a specific version of Django from a `requirements.in` file.
|
||
#[test]
|
||
fn compile_requirements_in() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("django==5.0b1")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
asgiref==3.7.2
|
||
# via django
|
||
django==5.0b1
|
||
sqlparse==0.4.4
|
||
# via django
|
||
|
||
----- stderr -----
|
||
Resolved 3 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn missing_requirements_in() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: failed to open file `requirements.in`
|
||
Caused by: No such file or directory (os error 2)
|
||
"###);
|
||
|
||
requirements_in.assert(predicates::path::missing());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn missing_venv() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = temp_dir.child(".venv");
|
||
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: failed to open file `requirements.in`
|
||
Caused by: No such file or directory (os error 2)
|
||
"###);
|
||
|
||
venv.assert(predicates::path::missing());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific version of Django from a `pyproject.toml` file.
|
||
#[test]
|
||
fn compile_pyproject_toml() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = [
|
||
"django==5.0b1",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile pyproject.toml --cache-dir [CACHE_DIR]
|
||
asgiref==3.7.2
|
||
# via django
|
||
django==5.0b1
|
||
sqlparse==0.4.4
|
||
# via django
|
||
|
||
----- stderr -----
|
||
Resolved 3 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a package from a `requirements.in` file, with a `constraints.txt` file.
|
||
#[test]
|
||
fn compile_constraints_txt() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("django==5.0b1")?;
|
||
|
||
let constraints_txt = temp_dir.child("constraints.txt");
|
||
constraints_txt.write_str("sqlparse<0.4.4")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--constraint")
|
||
.arg("constraints.txt")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --constraint constraints.txt --cache-dir [CACHE_DIR]
|
||
asgiref==3.7.2
|
||
# via django
|
||
django==5.0b1
|
||
sqlparse==0.4.3
|
||
# via django
|
||
|
||
----- stderr -----
|
||
Resolved 3 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a package from a `requirements.in` file, with an inline constraint.
|
||
#[test]
|
||
fn compile_constraints_inline() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("django==5.0b1")?;
|
||
requirements_in.write_str("-c constraints.txt")?;
|
||
|
||
let constraints_txt = temp_dir.child("constraints.txt");
|
||
constraints_txt.write_str("sqlparse<0.4.4")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
|
||
----- stderr -----
|
||
Resolved 0 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a package from a `requirements.in` file, with a `constraints.txt` file that
|
||
/// uses markers.
|
||
#[test]
|
||
fn compile_constraints_markers() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("anyio")?;
|
||
|
||
// Constrain a transitive dependency based on the Python version
|
||
let constraints_txt = temp_dir.child("constraints.txt");
|
||
// If constraints are ignored, these will conflict
|
||
constraints_txt.write_str("sniffio==1.2.0;python_version<='3.7'")?;
|
||
constraints_txt.write_str("sniffio==1.3.0;python_version>'3.7'")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--constraint")
|
||
.arg("constraints.txt")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --constraint constraints.txt --cache-dir [CACHE_DIR]
|
||
anyio==4.0.0
|
||
idna==3.4
|
||
# via anyio
|
||
sniffio==1.3.0
|
||
# via anyio
|
||
|
||
----- stderr -----
|
||
Resolved 3 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a package from an optional dependency group in a `pyproject.toml` file.
|
||
#[test]
|
||
fn compile_pyproject_toml_extra() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = []
|
||
optional-dependencies.foo = [
|
||
"django==5.0b1",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--extra")
|
||
.arg("foo")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile pyproject.toml --extra foo --cache-dir [CACHE_DIR]
|
||
asgiref==3.7.2
|
||
# via django
|
||
django==5.0b1
|
||
sqlparse==0.4.4
|
||
# via django
|
||
|
||
----- stderr -----
|
||
Resolved 3 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a package from an extra with unnormalized names in a `pyproject.toml` file.
|
||
#[test]
|
||
fn compile_pyproject_toml_extra_name_normalization() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = []
|
||
optional-dependencies."FrIeNdLy-._.-bArD" = [
|
||
"django==5.0b1",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--extra")
|
||
.arg("FRiENDlY-...-_-BARd")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile pyproject.toml --extra FRiENDlY-...-_-BARd --cache-dir [CACHE_DIR]
|
||
asgiref==3.7.2
|
||
# via django
|
||
django==5.0b1
|
||
sqlparse==0.4.4
|
||
# via django
|
||
|
||
----- stderr -----
|
||
Resolved 3 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request an extra that does not exist as a dependency group in a `pyproject.toml` file.
|
||
#[test]
|
||
fn compile_pyproject_toml_extra_missing() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = []
|
||
optional-dependencies.foo = [
|
||
"django==5.0b1",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--extra")
|
||
.arg("bar")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: Requested extra not found: bar
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request multiple extras that do not exist as a dependency group in a `pyproject.toml` file.
|
||
#[test]
|
||
fn compile_pyproject_toml_extras_missing() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = []
|
||
optional-dependencies.foo = [
|
||
"django==5.0b1",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--extra")
|
||
.arg("foo")
|
||
.arg("--extra")
|
||
.arg("bar")
|
||
.arg("--extra")
|
||
.arg("foobar")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: Requested extras not found: bar, foobar
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request extras when using a `requirements.in` file which does not support extras.
|
||
#[test]
|
||
fn compile_requirements_file_extra() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("django==5.0b1")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.arg("--all-extras")
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir),
|
||
@r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: Requesting extras requires a pyproject.toml input file.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request an extra with a name that does not conform to the specification.
|
||
#[test]
|
||
fn invalid_extra_name() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = []
|
||
optional-dependencies.foo = [
|
||
"django==5.0b1",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--extra")
|
||
.arg("invalid name!")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: invalid value 'invalid name!' for '--extra <EXTRA>': Extra names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters
|
||
|
||
For more information, try '--help'.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific version of Black at Python 3.12.
|
||
#[test]
|
||
fn compile_python_312() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("black==23.10.1")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--python-version")
|
||
.arg("3.12")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --python-version 3.12 --cache-dir [CACHE_DIR]
|
||
black==23.10.1
|
||
click==8.1.7
|
||
# via black
|
||
mypy-extensions==1.0.0
|
||
# via black
|
||
packaging==23.2
|
||
# via black
|
||
pathspec==0.11.2
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific version of Black at Python 3.7.
|
||
#[test]
|
||
fn compile_python_37() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("black==23.10.1")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--python-version")
|
||
.arg("3.7")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because there is no version of Python available matching >=3.8 and
|
||
black==23.10.1 depends on Python>=3.8, black==23.10.1 is forbidden.
|
||
And because root depends on black==23.10.1, version solving failed.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific version of Black against an invalid Python version.
|
||
#[test]
|
||
fn compile_python_invalid_version() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("black==23.10.1")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--python-version")
|
||
.arg("3.7.x")
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: invalid value '3.7.x' for '--python-version <PYTHON_VERSION>': after parsing 3.7, found ".x" after it, which is not part of a valid version
|
||
|
||
For more information, try '--help'.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific version of Black against an invalid Python version.
|
||
#[test]
|
||
fn compile_python_dev_version() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("black==23.10.1")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--python-version")
|
||
.arg("3.7-dev")
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: invalid value '3.7-dev' for '--python-version <PYTHON_VERSION>': Python version 3.7-dev is a development release
|
||
|
||
For more information, try '--help'.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Test that we select the last 3.8 compatible numpy version instead of trying to compile an
|
||
/// incompatible sdist <https://github.com/astral-sh/puffin/issues/388>
|
||
#[test]
|
||
fn compile_numpy_py38() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = temp_dir.child(".venv");
|
||
|
||
Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("venv")
|
||
.arg(venv.as_os_str())
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--python")
|
||
.arg("python3.8")
|
||
.current_dir(&temp_dir)
|
||
.assert()
|
||
.success();
|
||
venv.assert(predicates::path::is_dir());
|
||
let venv = venv.to_path_buf();
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("numpy")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.arg("--no-build")
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
numpy==1.24.4
|
||
|
||
----- stderr -----
|
||
Resolved 1 package in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask wheel via a URL dependency.
|
||
#[test]
|
||
fn compile_wheel_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask source distribution via a URL dependency.
|
||
#[test]
|
||
fn compile_sdist_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask source distribution via a Git HTTPS dependency.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_https_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ git+https://github.com/pallets/flask.git")?;
|
||
|
||
// In addition to the standard filters, remove the `main` commit, which will change frequently.
|
||
let filters: Vec<_> = iter::once((r"@(\d|\w){40}", "@[COMMIT]"))
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ git+https://github.com/pallets/flask.git@[COMMIT]
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask branch via a Git HTTPS dependency.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_branch_https_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ git+https://github.com/pallets/flask.git@1.0.x")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ git+https://github.com/pallets/flask.git@d92b64aa275841b0c9aea3903aba72fbc4275d91
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask tag via a Git HTTPS dependency.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_tag_https_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ git+https://github.com/pallets/flask.git@3.0.0")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ git+https://github.com/pallets/flask.git@735a4701d6d5e848241e7d7535db898efb62d400
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask commit via a Git HTTPS dependency.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_long_commit_https_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str(
|
||
"flask @ git+https://github.com/pallets/flask.git@d92b64aa275841b0c9aea3903aba72fbc4275d91",
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ git+https://github.com/pallets/flask.git@d92b64aa275841b0c9aea3903aba72fbc4275d91
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask commit via a Git HTTPS dependency.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_short_commit_https_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ git+https://github.com/pallets/flask.git@d92b64a")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ git+https://github.com/pallets/flask.git@d92b64aa275841b0c9aea3903aba72fbc4275d91
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Flask ref via a Git HTTPS dependency.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_refs_https_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in
|
||
.write_str("flask @ git+https://github.com/pallets/flask.git@refs/pull/5313/head")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ git+https://github.com/pallets/flask.git@7af0271f4703a71beef8e26d1f5f6f8da04100e6
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a specific Git dependency with a subdirectory.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_subdirectory_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("example-pkg-a @ git+https://github.com/pypa/sample-namespace-packages.git@df7530eeb8fa0cb7dbb8ecb28363e8e36bfa2f45#subdirectory=pkg_resources/pkg_a")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
example-pkg-a @ git+https://github.com/pypa/sample-namespace-packages.git@df7530eeb8fa0cb7dbb8ecb28363e8e36bfa2f45#subdirectory=pkg_resources/pkg_a
|
||
|
||
----- stderr -----
|
||
Resolved 1 package in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve two packages from a `requirements.in` file with the same Git HTTPS dependency.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_concurrent_access() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in
|
||
.write_str("example-pkg-a @ git+https://github.com/pypa/sample-namespace-packages.git@df7530eeb8fa0cb7dbb8ecb28363e8e36bfa2f45#subdirectory=pkg_resources/pkg_a\nexample-pkg-b @ git+https://github.com/pypa/sample-namespace-packages.git@df7530eeb8fa0cb7dbb8ecb28363e8e36bfa2f45#subdirectory=pkg_resources/pkg_b")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
example-pkg-a @ git+https://github.com/pypa/sample-namespace-packages.git@df7530eeb8fa0cb7dbb8ecb28363e8e36bfa2f45#subdirectory=pkg_resources/pkg_a
|
||
example-pkg-b @ git+https://github.com/pypa/sample-namespace-packages.git@df7530eeb8fa0cb7dbb8ecb28363e8e36bfa2f45#subdirectory=pkg_resources/pkg_b
|
||
|
||
----- stderr -----
|
||
Resolved 2 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a Git dependency with a declared name that differs from the true name of the package.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn compile_git_mismatched_name() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in
|
||
.write_str("flask @ git+https://github.com/pallets/flask.git@2.0.0\ndask @ git+https://github.com/pallets/flask.git@3.0.0")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: Failed to download and build: dask @ git+https://github.com/pallets/flask.git@3.0.0
|
||
Caused by: Package metadata name `flask` does not match given name `dask`
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request Flask, but include a URL dependency for Werkzeug, which should avoid adding a
|
||
/// duplicate dependency from `PyPI`.
|
||
#[test]
|
||
fn mixed_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask==3.0.0\nwerkzeug @ https://files.pythonhosted.org/packages/c3/fc/254c3e9b5feb89ff5b9076a23218dafbc99c96ac5941e900b71206e6313b/werkzeug-3.0.1-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask==3.0.0
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug @ https://files.pythonhosted.org/packages/c3/fc/254c3e9b5feb89ff5b9076a23218dafbc99c96ac5941e900b71206e6313b/werkzeug-3.0.1-py3-none-any.whl
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request Werkzeug via both a version and a URL dependency at a _different_ version, which
|
||
/// should result in a conflict.
|
||
#[test]
|
||
fn conflicting_direct_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("werkzeug==3.0.0\nwerkzeug @ https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because there is no version of werkzeug available matching ==3.0.0 and
|
||
root depends on werkzeug==3.0.0, version solving failed.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request Werkzeug via both a version and a URL dependency at _the same_ version, which
|
||
/// should prefer the direct URL dependency.
|
||
#[test]
|
||
fn compatible_direct_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("werkzeug==2.0.0\nwerkzeug @ https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
werkzeug @ https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl
|
||
|
||
----- stderr -----
|
||
Resolved 1 package in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request Werkzeug via two different URLs at different versions, which should result in a conflict.
|
||
#[test]
|
||
fn conflicting_repeated_url_dependency_version_mismatch() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("werkzeug @ https://files.pythonhosted.org/packages/bd/24/11c3ea5a7e866bf2d97f0501d0b4b1c9bbeade102bb4b588f0d2919a5212/Werkzeug-2.0.1-py3-none-any.whl\nwerkzeug @ https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ root dependencies are unusable: Conflicting URLs for package `werkzeug`:
|
||
- https://files.pythonhosted.org/packages/bd/24/11c3ea5a7e866bf2d97f0501d0b4b1c9bbeade102bb4b588f0d2919a5212/Werkzeug-2.0.1-py3-none-any.whl
|
||
- https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request Werkzeug via two different URLs at the same version. Despite mapping to the same
|
||
/// version, it should still result in a conflict.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn conflicting_repeated_url_dependency_version_match() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("werkzeug @ git+https://github.com/pallets/werkzeug.git@2.0.0\nwerkzeug @ https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ root dependencies are unusable: Conflicting URLs for package `werkzeug`:
|
||
- git+https://github.com/pallets/werkzeug.git@2.0.0
|
||
- https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request Flask, but include a URL dependency for a conflicting version of Werkzeug.
|
||
#[test]
|
||
fn conflicting_transitive_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask==3.0.0\nwerkzeug @ https://files.pythonhosted.org/packages/ff/1d/960bb4017c68674a1cb099534840f18d3def3ce44aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because flask==3.0.0 depends on werkzeug>=3.0.0 and there is no version
|
||
of werkzeug available matching >=3.0.0, flask==3.0.0 is forbidden.
|
||
And because root depends on flask==3.0.0, version solving failed.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request `transitive_url_dependency`, which depends on `git+https://github.com/pallets/werkzeug@2.0.0`.
|
||
/// Since this URL isn't declared upfront, we should reject it.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn disallowed_transitive_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("transitive_url_dependency @ https://github.com/astral-sh/ruff/files/13257454/transitive_url_dependency.zip")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: Package `werkzeug` attempted to resolve via URL: git+https://github.com/pallets/werkzeug@2.0.0. URL dependencies must be expressed as direct requirements or constraints. Consider adding `werkzeug @ git+https://github.com/pallets/werkzeug@2.0.0` to your dependencies or constraints file.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request `transitive_url_dependency`, which depends on `git+https://github.com/pallets/werkzeug@2.0.0`.
|
||
/// Since this URL is declared as a constraint, we should accept it.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn allowed_transitive_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("transitive_url_dependency @ https://github.com/astral-sh/ruff/files/13257454/transitive_url_dependency.zip")?;
|
||
|
||
let constraints_txt = temp_dir.child("constraints.txt");
|
||
constraints_txt.write_str("werkzeug @ git+https://github.com/pallets/werkzeug@2.0.0")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--constraint")
|
||
.arg("constraints.txt")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --constraint constraints.txt --cache-dir [CACHE_DIR]
|
||
transitive-url-dependency @ https://github.com/astral-sh/ruff/files/13257454/transitive_url_dependency.zip
|
||
werkzeug @ git+https://github.com/pallets/werkzeug@af160e0b6b7ddd81c22f1652c728ff5ac72d5c74
|
||
# via transitive-url-dependency
|
||
|
||
----- stderr -----
|
||
Resolved 2 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request `transitive_url_dependency`, which depends on `git+https://github.com/pallets/werkzeug@2.0.0`.
|
||
/// Since this `git+https://github.com/pallets/werkzeug@2.0.0.git` is declared as a constraint, and
|
||
/// those map to the same canonical URL, we should accept it.
|
||
#[test]
|
||
#[cfg(feature = "git")]
|
||
fn allowed_transitive_canonical_url_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("transitive_url_dependency @ https://github.com/astral-sh/ruff/files/13257454/transitive_url_dependency.zip")?;
|
||
|
||
let constraints_txt = temp_dir.child("constraints.txt");
|
||
constraints_txt.write_str("werkzeug @ git+https://github.com/pallets/werkzeug.git@2.0.0")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--constraint")
|
||
.arg("constraints.txt")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --constraint constraints.txt --cache-dir [CACHE_DIR]
|
||
transitive-url-dependency @ https://github.com/astral-sh/ruff/files/13257454/transitive_url_dependency.zip
|
||
werkzeug @ git+https://github.com/pallets/werkzeug@af160e0b6b7ddd81c22f1652c728ff5ac72d5c74
|
||
# via transitive-url-dependency
|
||
|
||
----- stderr -----
|
||
Resolved 2 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve packages from all optional dependency groups in a `pyproject.toml` file.
|
||
#[test]
|
||
fn compile_pyproject_toml_all_extras() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = ["django==5.0b1"]
|
||
optional-dependencies.foo = [
|
||
"anyio==4.0.0",
|
||
]
|
||
optional-dependencies.bar = [
|
||
"httpcore==0.18.0",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--all-extras")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile pyproject.toml --all-extras --cache-dir [CACHE_DIR]
|
||
anyio==4.0.0
|
||
# via httpcore
|
||
asgiref==3.7.2
|
||
# via django
|
||
certifi==2023.11.17
|
||
# via httpcore
|
||
django==5.0b1
|
||
h11==0.14.0
|
||
# via httpcore
|
||
httpcore==0.18.0
|
||
idna==3.4
|
||
# via anyio
|
||
sniffio==1.3.0
|
||
# via
|
||
# anyio
|
||
# httpcore
|
||
sqlparse==0.4.4
|
||
# via django
|
||
|
||
----- stderr -----
|
||
Resolved 9 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve packages from all optional dependency groups in a `pyproject.toml` file.
|
||
#[test]
|
||
fn compile_does_not_allow_both_extra_and_all_extras() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "project"
|
||
dependencies = ["django==5.0b1"]
|
||
optional-dependencies.foo = [
|
||
"anyio==4.0.0",
|
||
]
|
||
optional-dependencies.bar = [
|
||
"httpcore==0.18.0",
|
||
]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--all-extras")
|
||
.arg("--extra")
|
||
.arg("foo")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir),
|
||
@r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: the argument '--all-extras' cannot be used with '--extra <EXTRA>'
|
||
|
||
Usage: puffin pip-compile --all-extras --cache-dir [CACHE_DIR]
|
||
|
||
For more information, try '--help'.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Compile requirements that cannot be solved due to conflict in a `pyproject.toml` fil;e.
|
||
#[test]
|
||
fn compile_unsolvable_requirements() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "my-project"
|
||
dependencies = ["django==5.0b1", "django==5.0a1"]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ my-project dependencies are unusable: Conflicting versions for `django`:
|
||
`django==5.0b1` does not intersect with `django==5.0a1`
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Compile requirements in a `pyproject.toml` file that cannot be resolved due to
|
||
/// a requirement with a version that is not available online.
|
||
#[test]
|
||
fn compile_unsolvable_requirements_version_not_available() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let pyproject_toml = temp_dir.child("pyproject.toml");
|
||
pyproject_toml.write_str(
|
||
r#"[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
|
||
[project]
|
||
name = "my-project"
|
||
dependencies = ["django==300.1.4"]
|
||
"#,
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("pyproject.toml")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because there is no version of django available matching ==300.1.4 and
|
||
my-project depends on django==300.1.4, version solving failed.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve at a specific time in the past
|
||
#[test]
|
||
fn compile_exclude_newer() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("tqdm")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--exclude-newer")
|
||
// 4.64.0: 2022-04-04T01:48:46.194635Z1
|
||
// 4.64.1: 2022-09-03T11:10:27.148080Z
|
||
.arg("2022-04-04T12:00:00Z")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --exclude-newer 2022-04-04T12:00:00Z --cache-dir [CACHE_DIR]
|
||
tqdm==4.64.0
|
||
|
||
----- stderr -----
|
||
Resolved 1 package in [TIME]
|
||
"###);
|
||
});
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
// Use a date as input instead.
|
||
// We interpret a date as including this day
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--exclude-newer")
|
||
.arg("2022-04-04")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --exclude-newer 2022-04-04 --cache-dir [CACHE_DIR]
|
||
tqdm==4.64.0
|
||
|
||
----- stderr -----
|
||
Resolved 1 package in [TIME]
|
||
"###);
|
||
});
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
// Check the error message for invalid datetime
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--exclude-newer")
|
||
.arg("2022-04-04+02:00")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: invalid value '2022-04-04+02:00' for '--exclude-newer <EXCLUDE_NEWER>': Neither a valid date (trailing input) not a valid datetime (input contains invalid characters)
|
||
|
||
For more information, try '--help'.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a local path dependency on a specific wheel.
|
||
#[test]
|
||
fn compile_wheel_path_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
// Download a wheel.
|
||
let response = reqwest::blocking::get("https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl")?;
|
||
let flask_wheel = temp_dir.child("flask-3.0.0-py3-none-any.whl");
|
||
let mut flask_wheel_file = std::fs::File::create(&flask_wheel)?;
|
||
std::io::copy(&mut response.bytes()?.as_ref(), &mut flask_wheel_file)?;
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str(&format!("flask @ file://{}", flask_wheel.path().display()))?;
|
||
|
||
// In addition to the standard filters, remove the temporary directory from the snapshot.
|
||
let filters: Vec<_> = iter::once((r"file://.*/", "file://[TEMP_DIR]/"))
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ file://[TEMP_DIR]/flask-3.0.0-py3-none-any.whl
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a local path dependency on a specific source distribution.
|
||
#[test]
|
||
fn compile_source_distribution_path_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
// Download a source distribution.
|
||
let response = reqwest::blocking::get("https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz")?;
|
||
let flask_wheel = temp_dir.child("flask-3.0.0.tar.gz");
|
||
let mut flask_wheel_file = std::fs::File::create(&flask_wheel)?;
|
||
std::io::copy(&mut response.bytes()?.as_ref(), &mut flask_wheel_file)?;
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str(&format!("flask @ file://{}", flask_wheel.path().display()))?;
|
||
|
||
// In addition to the standard filters, remove the temporary directory from the snapshot.
|
||
let filters: Vec<_> = iter::once((r"file://.*/", "file://[TEMP_DIR]/"))
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ file://[TEMP_DIR]/flask-3.0.0.tar.gz
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a local path dependency to a non-existent file.
|
||
#[test]
|
||
fn compile_wheel_path_dependency_missing() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ file:///path/to/flask-3.0.0-py3-none-any.whl")?;
|
||
|
||
// In addition to the standard filters, remove the temporary directory from the snapshot.
|
||
let filters: Vec<_> = iter::once((r"file://.*/", "file://[TEMP_DIR]/"))
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 2
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
error: Unable to locate distribution at: file://[TEMP_DIR]/flask-3.0.0-py3-none-any.whl
|
||
Caused by: No such file or directory (os error 2)
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a yanked version of `attrs` by specifying the version directly.
|
||
#[test]
|
||
fn compile_yanked_version_direct() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("attrs==21.1.0")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
attrs==21.1.0
|
||
|
||
----- stderr -----
|
||
Resolved 1 package in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Fail to resolve `attrs` due to the indirect use of a yanked version (`21.1.0`).
|
||
#[test]
|
||
fn compile_yanked_version_indirect() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("attrs>20.3.0,<21.2.0")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because there is no version of attrs available matching >20.3.0, <21.2.0
|
||
and root depends on attrs>20.3.0, <21.2.0, version solving failed.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Flask==3.0.0 depends on Werkzeug>=3.0.0. Demonstrate that we can override this
|
||
/// requirement with an incompatible version.
|
||
#[test]
|
||
fn override_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask==3.0.0")?;
|
||
|
||
let overrides_txt = temp_dir.child("overrides.txt");
|
||
overrides_txt.write_str("werkzeug==2.3.0")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--override")
|
||
.arg("overrides.txt")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --override overrides.txt --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask==3.0.0
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==2.3.0
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Black==23.10.1 depends on tomli>=1.1.0 for Python versions below 3.11. Demonstrate that we can
|
||
/// override it with a multi-line override.
|
||
#[test]
|
||
fn override_multi_dependency() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("black==23.10.1")?;
|
||
|
||
let overrides_txt = temp_dir.child("overrides.txt");
|
||
overrides_txt.write_str(
|
||
"tomli>=1.1.0; python_version >= '3.11'\ntomli<1.0.0; python_version < '3.11'",
|
||
)?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--override")
|
||
.arg("overrides.txt")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --override overrides.txt --cache-dir [CACHE_DIR]
|
||
black==23.10.1
|
||
click==8.1.7
|
||
# via black
|
||
mypy-extensions==1.0.0
|
||
# via black
|
||
packaging==23.2
|
||
# via black
|
||
pathspec==0.11.2
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
tomli==2.0.1
|
||
# via black
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request an extra that doesn't exist on the specified package.
|
||
#[test]
|
||
fn missing_registry_extra() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("black[tensorboard]==23.10.1")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
black==23.10.1
|
||
click==8.1.7
|
||
# via black
|
||
mypy-extensions==1.0.0
|
||
# via black
|
||
packaging==23.2
|
||
# via black
|
||
pathspec==0.11.2
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
warning: The package `black==23.10.1` does not have an extra named `tensorboard`.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Request an extra that doesn't exist on the specified package.
|
||
#[test]
|
||
fn missing_url_extra() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask[tensorboard] @ https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
warning: The package `flask @ https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl` does not have an extra named `tensorboard`.
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a dependency from a URL, preserving the exact casing of the URL as specified in the
|
||
/// requirements file.
|
||
#[test]
|
||
fn preserve_url() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ https://files.PYTHONHOSTED.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ https://files.PYTHONHOSTED.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a dependency from a URL, preserving the unexpanded environment variable as specified in
|
||
/// the requirements file.
|
||
#[test]
|
||
fn preserve_env_var() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
// Download a wheel.
|
||
let response = reqwest::blocking::get("https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl")?;
|
||
let flask_wheel = temp_dir.child("flask-3.0.0-py3-none-any.whl");
|
||
let mut flask_wheel_file = std::fs::File::create(flask_wheel)?;
|
||
std::io::copy(&mut response.bytes()?.as_ref(), &mut flask_wheel_file)?;
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ file://${PROJECT_ROOT}/flask-3.0.0-py3-none-any.whl")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0
|
||
# via flask
|
||
click==8.1.7
|
||
# via flask
|
||
flask @ file://${PROJECT_ROOT}/flask-3.0.0-py3-none-any.whl
|
||
itsdangerous==2.1.2
|
||
# via flask
|
||
jinja2==3.1.2
|
||
# via flask
|
||
markupsafe==2.1.3
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
#[cfg(feature = "maturin")]
|
||
fn compile_editable() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str(indoc! {r"
|
||
-e ../../scripts/editable-installs/poetry_editable
|
||
-e ../../scripts/editable-installs/maturin_editable
|
||
boltons # normal dependency for comparison
|
||
"
|
||
})?;
|
||
|
||
let filter_path = requirements_in.display().to_string();
|
||
let filters = INSTA_FILTERS
|
||
.iter()
|
||
.chain(&[(filter_path.as_str(), "requirements.in")])
|
||
.copied()
|
||
.collect::<Vec<_>>();
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg(requirements_in.path())
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("VIRTUAL_ENV", venv.as_os_str()), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
boltons==23.1.1
|
||
-e ../../scripts/editable-installs/maturin_editable
|
||
numpy==1.26.2
|
||
# via poetry-editable
|
||
-e ../../scripts/editable-installs/poetry_editable
|
||
|
||
----- stderr -----
|
||
Built 2 editables in [TIME]
|
||
Resolved 4 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
#[ignore]
|
||
fn cache_errors_are_non_fatal() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
// No git dep, git has its own locking strategy
|
||
requirements_in.write_str(indoc! {r"
|
||
# pypi wheel
|
||
pandas
|
||
# url wheel
|
||
flask @ https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||
# url source dist
|
||
werkzeug @ https://files.pythonhosted.org/packages/0d/cc/ff1904eb5eb4b455e442834dabf9427331ac0fa02853bf83db817a7dd53d/werkzeug-3.0.1.tar.gz
|
||
"
|
||
})?;
|
||
|
||
// Pick a file from each kind of cache
|
||
let interpreter_cache = cache_dir
|
||
.path()
|
||
.join("interpreter-v0")
|
||
.read_dir()?
|
||
.next()
|
||
.context("Expected a python interpreter cache file")??
|
||
.path();
|
||
let cache_files = [
|
||
PathBuf::from("simple-v0/pypi/numpy.msgpack"),
|
||
PathBuf::from(
|
||
"wheels-v0/pypi/python-dateutil/python_dateutil-2.8.2-py2.py3-none-any.msgpack",
|
||
),
|
||
PathBuf::from("wheels-v0/url/4b8be67c801a7ecb/flask/flask-3.0.0-py3-none-any.msgpack"),
|
||
PathBuf::from("built-wheels-v0/url/6781bd6440ae72c2/werkzeug/metadata.msgpack"),
|
||
interpreter_cache,
|
||
];
|
||
|
||
let check = || {
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg(requirements_in.path())
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
// It's sufficient to check that we resolve to a fix number of packages
|
||
.stdout(std::process::Stdio::null())
|
||
.env("VIRTUAL_ENV", venv.as_os_str()), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Resolved 13 packages in [TIME]
|
||
"###);
|
||
});
|
||
};
|
||
|
||
insta::allow_duplicates! {
|
||
check();
|
||
|
||
// Replace some cache files with invalid contents
|
||
for file in &cache_files {
|
||
let file = cache_dir.join(file);
|
||
if !file.is_file() {
|
||
bail!("Missing cache file {}", file.display());
|
||
}
|
||
fs_err::write(file, "I borken you cache")?;
|
||
}
|
||
|
||
check();
|
||
|
||
#[cfg(unix)]
|
||
{
|
||
use fs_err::os::unix::fs::OpenOptionsExt;
|
||
|
||
// Make some files unreadable, so that the read instead of the deserialization will fail
|
||
for file in cache_files {
|
||
let file = cache_dir.join(file);
|
||
if !file.is_file() {
|
||
bail!("Missing cache file {}", file.display());
|
||
}
|
||
|
||
fs_err::OpenOptions::new()
|
||
.create(true)
|
||
.write(true)
|
||
.mode(0o000)
|
||
.open(file)?;
|
||
}
|
||
}
|
||
|
||
check();
|
||
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
/// Resolve a distribution from an HTML-only registry.
|
||
#[test]
|
||
fn compile_html() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("jinja2<=3.1.2")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--index-url")
|
||
.arg("https://download.pytorch.org/whl")
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
jinja2==3.1.2
|
||
markupsafe==2.1.3
|
||
# via jinja2
|
||
|
||
----- stderr -----
|
||
Resolved 2 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a distribution from a registry with and without a trailing slash.
|
||
#[test]
|
||
fn trailing_slash() -> Result<()> {
|
||
let temp_dir = TempDir::new()?;
|
||
let cache_dir = TempDir::new()?;
|
||
let venv = create_venv_py312(&temp_dir, &cache_dir);
|
||
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("jinja2")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--index-url")
|
||
.arg("https://test.pypi.org/simple")
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
jinja2==3.1.2
|
||
markupsafe==2.1.3
|
||
# via jinja2
|
||
|
||
----- stderr -----
|
||
Resolved 2 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip-compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--index-url")
|
||
.arg("https://test.pypi.org/simple/")
|
||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||
jinja2==3.1.2
|
||
markupsafe==2.1.3
|
||
# via jinja2
|
||
|
||
----- stderr -----
|
||
Resolved 2 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|