mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
3841 lines
126 KiB
Rust
3841 lines
126 KiB
Rust
#![cfg(all(feature = "python", feature = "pypi"))]
|
||
|
||
use std::path::PathBuf;
|
||
use std::process::Command;
|
||
use std::{fs, iter};
|
||
|
||
use anyhow::{bail, Context, Result};
|
||
use assert_fs::prelude::*;
|
||
use assert_fs::TempDir;
|
||
use indoc::indoc;
|
||
use insta::assert_snapshot;
|
||
use insta_cmd::_macro_support::insta;
|
||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||
use itertools::Itertools;
|
||
use url::Url;
|
||
|
||
use crate::common::create_venv;
|
||
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")
|
||
.arg("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 v[VERSION] 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");
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("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");
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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")
|
||
.arg("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")
|
||
.arg("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")
|
||
.arg("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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 -----
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:114] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.8",
|
||
),
|
||
Unbounded,
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:116] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.8",
|
||
),
|
||
Unbounded,
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:114] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"23.10.1",
|
||
),
|
||
Included(
|
||
"23.10.1",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:116] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"23.10.1",
|
||
),
|
||
Included(
|
||
"23.10.1",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because the requested Python version (3.7) does not satisfy Python>=3.8
|
||
and black==23.10.1 depends on Python>=3.8, we can conclude that
|
||
black==23.10.1 cannot be used.
|
||
And because you require black==23.10.1, we can conclude that the
|
||
requirements are unsatisfiable.
|
||
"###);
|
||
});
|
||
|
||
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")
|
||
.arg("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")
|
||
.arg("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 = create_venv(&temp_dir, &cache_dir, "3.8");
|
||
|
||
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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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.
|
||
///
|
||
/// Exercises the `prepare_metadata_for_build_wheel` hooks.
|
||
#[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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 -----
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:114] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:116] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because there is no version of werkzeug==3.0.0 and you require
|
||
werkzeug==3.0.0, we can conclude that the requirements are
|
||
unsatisfiable.
|
||
"###);
|
||
});
|
||
|
||
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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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:
|
||
╰─▶ your requirements cannot be used because there are 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")
|
||
.arg("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:
|
||
╰─▶ your requirements cannot be used because there are 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")
|
||
.arg("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 -----
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:114] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
Unbounded,
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:116] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
Unbounded,
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:114] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:116] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
Included(
|
||
"3.0.0",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because flask==3.0.0 depends on werkzeug>=3.0.0 and only werkzeug<3.0.0
|
||
is available, we can conclude that flask==3.0.0 cannot be used.
|
||
And because you require flask==3.0.0, we can conclude that the
|
||
requirements are unsatisfiable.
|
||
"###);
|
||
});
|
||
|
||
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")
|
||
.arg("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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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")
|
||
.arg("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 cannot be used because there are 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")
|
||
.arg("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 -----
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:114] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"300.1.4",
|
||
),
|
||
Included(
|
||
"300.1.4",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:116] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Included(
|
||
"300.1.4",
|
||
),
|
||
Included(
|
||
"300.1.4",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because there is no version of django==300.1.4 and my-project
|
||
depends on django==300.1.4, we can conclude that the requirements are
|
||
unsatisfiable.
|
||
"###);
|
||
});
|
||
|
||
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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 @ {}",
|
||
Url::from_file_path(flask_wheel.path()).unwrap()
|
||
))?;
|
||
|
||
// 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")
|
||
.arg("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 v[VERSION] 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]
|
||
"###);
|
||
});
|
||
|
||
// Run the same operation, but this time with a relative path.
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ file: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")
|
||
.arg("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 v[VERSION] 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: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]
|
||
"###);
|
||
});
|
||
|
||
// Run the same operation, but this time with a relative path.
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ file://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")
|
||
.arg("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 v[VERSION] 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://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]
|
||
"###);
|
||
});
|
||
|
||
// Run the same operation, but this time with a relative path.
|
||
let requirements_in = temp_dir.child("requirements.in");
|
||
requirements_in.write_str("flask @ ./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")
|
||
.arg("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 v[VERSION] 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 @ ./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 @ {}",
|
||
Url::from_file_path(flask_wheel.path()).unwrap()
|
||
))?;
|
||
|
||
// 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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: Distribution not found at: file://[TEMP_DIR]/flask-3.0.0-py3-none-any.whl
|
||
"###);
|
||
});
|
||
|
||
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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 -----
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:114] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Excluded(
|
||
"20.3.0",
|
||
),
|
||
Excluded(
|
||
"21.2.0",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
[crates/puffin-resolver/src/pubgrub/report.rs:116] &dependency_set = Range {
|
||
segments: [
|
||
(
|
||
Excluded(
|
||
"20.3.0",
|
||
),
|
||
Excluded(
|
||
"21.2.0",
|
||
),
|
||
),
|
||
],
|
||
}
|
||
× No solution found when resolving dependencies:
|
||
╰─▶ Because there are no versions of attrs that satisfy attrs>20.3.0,<21.2.0
|
||
and you require attrs>20.3.0,<21.2.0, we can conclude that the
|
||
requirements are unsatisfiable.
|
||
"###);
|
||
});
|
||
|
||
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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("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 v[VERSION] 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 ${PROJECT_ROOT}/../../scripts/editable-installs/maturin_editable
|
||
-e file://../../scripts/editable-installs/black_editable
|
||
boltons # normal dependency for comparison
|
||
"
|
||
})?;
|
||
|
||
let filter_path = regex::escape(&requirements_in.display().to_string());
|
||
let filters: Vec<_> = iter::once((filter_path.as_str(), "requirements.in"))
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("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 v[VERSION] via the following command:
|
||
# puffin pip compile requirements.in --cache-dir [CACHE_DIR]
|
||
-e file://../../scripts/editable-installs/black_editable
|
||
boltons==23.1.1
|
||
-e ${PROJECT_ROOT}/../../scripts/editable-installs/maturin_editable
|
||
numpy==1.26.2
|
||
# via poetry-editable
|
||
-e ../../scripts/editable-installs/poetry_editable
|
||
|
||
----- stderr -----
|
||
Built 3 editables in [TIME]
|
||
Resolved 5 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")
|
||
.arg("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")
|
||
.arg("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 v[VERSION] 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")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--index-url")
|
||
.arg("https://test.pypi.org/simple")
|
||
.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 v[VERSION] 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")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--index-url")
|
||
.arg("https://test.pypi.org/simple/")
|
||
.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 v[VERSION] 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 project without a `pyproject.toml`, using the PEP 517 build backend (default).
|
||
#[test]
|
||
fn compile_legacy_sdist_pep_517() -> 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("flake8 @ https://files.pythonhosted.org/packages/66/53/3ad4a3b74d609b3b9008a10075c40e7c8909eae60af53623c3888f7a529a/flake8-6.0.0.tar.gz")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("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 v[VERSION] via the following command:
|
||
# puffin pip compile requirements.in --cache-dir [CACHE_DIR]
|
||
flake8 @ https://files.pythonhosted.org/packages/66/53/3ad4a3b74d609b3b9008a10075c40e7c8909eae60af53623c3888f7a529a/flake8-6.0.0.tar.gz
|
||
mccabe==0.7.0
|
||
# via flake8
|
||
pycodestyle==2.10.0
|
||
# via flake8
|
||
pyflakes==3.0.1
|
||
# via flake8
|
||
|
||
----- stderr -----
|
||
Resolved 4 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Resolve a project without a `pyproject.toml`, using `setuptools` directly.
|
||
#[test]
|
||
fn compile_legacy_sdist_setuptools() -> 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("flake8 @ https://files.pythonhosted.org/packages/66/53/3ad4a3b74d609b3b9008a10075c40e7c8909eae60af53623c3888f7a529a/flake8-6.0.0.tar.gz")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--legacy-setup-py")
|
||
.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 v[VERSION] via the following command:
|
||
# puffin pip compile requirements.in --legacy-setup-py --cache-dir [CACHE_DIR]
|
||
flake8 @ https://files.pythonhosted.org/packages/66/53/3ad4a3b74d609b3b9008a10075c40e7c8909eae60af53623c3888f7a529a/flake8-6.0.0.tar.gz
|
||
mccabe==0.7.0
|
||
# via flake8
|
||
pycodestyle==2.10.0
|
||
# via flake8
|
||
pyflakes==3.0.1
|
||
# via flake8
|
||
|
||
----- stderr -----
|
||
Resolved 4 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Include hashes in the generated output.
|
||
#[test]
|
||
fn generate_hashes() -> 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")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--generate-hashes")
|
||
.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 v[VERSION] via the following command:
|
||
# puffin pip compile requirements.in --generate-hashes --cache-dir [CACHE_DIR]
|
||
blinker==1.7.0 \
|
||
--hash=sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9 \
|
||
--hash=sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182
|
||
# via flask
|
||
click==8.1.7 \
|
||
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
|
||
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
|
||
# via flask
|
||
flask==3.0.0 \
|
||
--hash=sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638 \
|
||
--hash=sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58
|
||
itsdangerous==2.1.2 \
|
||
--hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \
|
||
--hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a
|
||
# via flask
|
||
jinja2==3.1.2 \
|
||
--hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
|
||
--hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
|
||
# via flask
|
||
markupsafe==2.1.3 \
|
||
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
|
||
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
|
||
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
|
||
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
|
||
--hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
|
||
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
|
||
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
|
||
--hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
|
||
--hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
|
||
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
|
||
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
|
||
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
|
||
--hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \
|
||
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
|
||
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
|
||
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
|
||
--hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
|
||
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
|
||
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
|
||
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
|
||
--hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \
|
||
--hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \
|
||
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
|
||
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
|
||
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
|
||
--hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
|
||
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
|
||
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
|
||
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
|
||
--hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \
|
||
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
|
||
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
|
||
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
|
||
--hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
|
||
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
|
||
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
|
||
--hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
|
||
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
|
||
--hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
|
||
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
|
||
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
|
||
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
|
||
--hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \
|
||
--hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \
|
||
--hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \
|
||
--hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \
|
||
--hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \
|
||
--hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \
|
||
--hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \
|
||
--hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \
|
||
--hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \
|
||
--hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \
|
||
--hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \
|
||
--hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \
|
||
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
|
||
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
|
||
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
|
||
--hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
|
||
--hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
|
||
--hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
|
||
# via
|
||
# jinja2
|
||
# werkzeug
|
||
werkzeug==3.0.1 \
|
||
--hash=sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc \
|
||
--hash=sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10
|
||
# via flask
|
||
|
||
----- stderr -----
|
||
Resolved 7 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Compile using `--find-links` with a local directory.
|
||
#[test]
|
||
fn find_links_directory() -> 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"
|
||
tqdm
|
||
numpy
|
||
werkzeug @ https://files.pythonhosted.org/packages/c3/fc/254c3e9b5feb89ff5b9076a23218dafbc99c96ac5941e900b71206e6313b/werkzeug-3.0.1-py3-none-any.whl
|
||
"})?;
|
||
|
||
let project_root = fs_err::canonicalize(std::env::current_dir()?.join("../.."))?;
|
||
let project_root_string = regex::escape(&project_root.display().to_string());
|
||
let filters: Vec<_> = iter::once((project_root_string.as_str(), "[PROJECT_ROOT]"))
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--find-links")
|
||
.arg(project_root.join("scripts/wheels/"))
|
||
.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 v[VERSION] via the following command:
|
||
# puffin pip compile requirements.in --find-links [PROJECT_ROOT]/scripts/wheels/ --cache-dir [CACHE_DIR]
|
||
markupsafe==2.1.3
|
||
# via werkzeug
|
||
numpy==1.26.2
|
||
tqdm==1000.0.0
|
||
werkzeug @ https://files.pythonhosted.org/packages/c3/fc/254c3e9b5feb89ff5b9076a23218dafbc99c96ac5941e900b71206e6313b/werkzeug-3.0.1-py3-none-any.whl
|
||
|
||
----- stderr -----
|
||
Resolved 4 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Compile using `--find-links` with a URL by resolving `tqdm` from the `PyTorch` wheels index.
|
||
#[test]
|
||
fn find_links_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("tqdm")?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--no-index")
|
||
.arg("--find-links")
|
||
.arg("https://download.pytorch.org/whl/torch_stable.html")
|
||
.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 v[VERSION] via the following command:
|
||
# puffin pip compile requirements.in --no-index --find-links https://download.pytorch.org/whl/torch_stable.html --cache-dir [CACHE_DIR]
|
||
tqdm==4.64.1
|
||
|
||
----- stderr -----
|
||
Resolved 1 package in [TIME]
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Use an existing resolution for `black==23.10.1`, with stale versions of `click` and `pathspec`.
|
||
/// Nothing should change.
|
||
#[test]
|
||
fn upgrade_none() -> 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 requirements_txt = temp_dir.child("requirements.txt");
|
||
requirements_txt.write_str(indoc! {r"
|
||
black==23.10.1
|
||
click==8.1.2
|
||
# via black
|
||
mypy-extensions==1.0.0
|
||
# via black
|
||
packaging==23.2
|
||
# via black
|
||
pathspec==0.11.0
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
"})?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--output-file")
|
||
.arg("requirements.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 -----
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
// Read the output requirements, but skip the header.
|
||
let resolution = fs::read_to_string(requirements_txt.path())?
|
||
.lines()
|
||
.skip_while(|line| line.trim_start().starts_with('#'))
|
||
.join("\n");
|
||
assert_snapshot!(resolution, @r###"
|
||
black==23.10.1
|
||
click==8.1.2
|
||
# via black
|
||
mypy-extensions==1.0.0
|
||
# via black
|
||
packaging==23.2
|
||
# via black
|
||
pathspec==0.11.0
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
"###);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Use an existing resolution for `black==23.10.1`, with stale versions of `click` and `pathspec`.
|
||
/// Both packages should be upgraded.
|
||
#[test]
|
||
fn upgrade_all() -> 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 requirements_txt = temp_dir.child("requirements.txt");
|
||
requirements_txt.write_str(indoc! {r"
|
||
# 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.2
|
||
# via black
|
||
mypy-extensions==1.0.0
|
||
# via black
|
||
packaging==23.2
|
||
# via black
|
||
pathspec==0.11.0
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
"})?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--output-file")
|
||
.arg("requirements.txt")
|
||
.arg("--upgrade")
|
||
.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 -----
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
// Read the output requirements, but skip the header.
|
||
let resolution = fs::read_to_string(requirements_txt.path())?
|
||
.lines()
|
||
.skip_while(|line| line.trim_start().starts_with('#'))
|
||
.join("\n");
|
||
assert_snapshot!(resolution, @r###"
|
||
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
|
||
"###);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Use an existing resolution for `black==23.10.1`, with stale versions of `click` and `pathspec`.
|
||
/// Only `click` should be upgraded.
|
||
#[test]
|
||
fn upgrade_package() -> 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 requirements_txt = temp_dir.child("requirements.txt");
|
||
requirements_txt.write_str(indoc! {r"
|
||
# 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.2
|
||
# via black
|
||
mypy-extensions==1.0.0
|
||
# via black
|
||
packaging==23.2
|
||
# via black
|
||
pathspec==0.11.0
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
"})?;
|
||
|
||
insta::with_settings!({
|
||
filters => INSTA_FILTERS.to_vec()
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("compile")
|
||
.arg("requirements.in")
|
||
.arg("--output-file")
|
||
.arg("requirements.txt")
|
||
.arg("--upgrade-package")
|
||
.arg("click")
|
||
.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 -----
|
||
|
||
----- stderr -----
|
||
Resolved 6 packages in [TIME]
|
||
"###);
|
||
});
|
||
|
||
// Read the output requirements, but skip the header.
|
||
let resolution = fs::read_to_string(requirements_txt.path())?
|
||
.lines()
|
||
.skip_while(|line| line.trim_start().starts_with('#'))
|
||
.join("\n");
|
||
assert_snapshot!(resolution, @r###"
|
||
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.0
|
||
# via black
|
||
platformdirs==4.0.0
|
||
# via black
|
||
"###
|
||
);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Attempt to resolve a requirement at a path that doesn't exist.
|
||
#[test]
|
||
fn missing_path_requirement() -> 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 @ file:///tmp/django-3.2.8.tar.gz")?;
|
||
|
||
let filters: Vec<_> = [(r"/[A-Z]:/", "/")]
|
||
.into_iter()
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("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: Distribution not found at: file:///tmp/django-3.2.8.tar.gz
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Attempt to resolve an editable requirement at a path that doesn't exist.
|
||
#[test]
|
||
fn missing_editable_requirement() -> 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("-e ../tmp/django-3.2.8.tar.gz")?;
|
||
|
||
// File url, absolute Unix path or absolute Windows path
|
||
let filters: Vec<_> = [
|
||
(r" file://.*/", " file://[TEMP_DIR]/"),
|
||
(r" /.*/", " /[TEMP_DIR]/"),
|
||
(r" [A-Z]:\\.*\\", " /[TEMP_DIR]/"),
|
||
]
|
||
.into_iter()
|
||
.chain(INSTA_FILTERS.to_vec())
|
||
.collect::<Vec<_>>();
|
||
|
||
insta::with_settings!({
|
||
filters => filters
|
||
}, {
|
||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||
.arg("pip")
|
||
.arg("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 build editables
|
||
Caused by: Failed to build editable: file://[TEMP_DIR]/django-3.2.8.tar.gz
|
||
Caused by: Source distribution not found at: /[TEMP_DIR]/django-3.2.8.tar.gz
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Attempt to resolve a URL requirement without a package name.
|
||
#[test]
|
||
fn missing_package_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("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")
|
||
.arg("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: Unsupported requirement in requirements.in position 0 to 135
|
||
Caused by: URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ https://...`).
|
||
https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
"###);
|
||
});
|
||
|
||
Ok(())
|
||
}
|