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