From 5cc4e5d31eb15b3a1c4fd083361806e9f4d93b20 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 26 Jan 2024 12:38:24 -0600 Subject: [PATCH] Add `pip compile` test where specific Python versions are available on the system (#1111) Extends https://github.com/astral-sh/puffin/pull/1106 with the scenario from https://github.com/zanieb/packse/pull/95 which tests that `pip compile` will use the matching system Python version for builds when available --- crates/puffin/tests/pip_compile_scenarios.rs | 125 +++++++++++++++++-- crates/puffin/tests/pip_install_scenarios.rs | 2 +- scripts/scenarios/templates/compile.mustache | 30 ++++- scripts/scenarios/update.py | 2 +- 4 files changed, 144 insertions(+), 15 deletions(-) diff --git a/crates/puffin/tests/pip_compile_scenarios.rs b/crates/puffin/tests/pip_compile_scenarios.rs index be8e727a1..97fc72de5 100644 --- a/crates/puffin/tests/pip_compile_scenarios.rs +++ b/crates/puffin/tests/pip_compile_scenarios.rs @@ -1,21 +1,43 @@ //! DO NOT EDIT //! //! Generated with ./scripts/scenarios/update.py -//! Scenarios from +//! Scenarios from //! #![cfg(all(feature = "python", feature = "pypi"))] +use std::path::PathBuf; use std::process::Command; use anyhow::Result; use assert_fs::fixture::{FileWriteStr, PathChild}; +use common::{create_venv, BIN_NAME, INSTA_FILTERS}; +#[cfg(unix)] +use fs_err::os::unix::fs::symlink as symlink_file; +#[cfg(windows)] +use fs_err::os::windows::fs::symlink_file; use insta_cmd::_macro_support::insta; use insta_cmd::{assert_cmd_snapshot, get_cargo_bin}; - -use common::{create_venv, BIN_NAME, INSTA_FILTERS}; +use puffin_interpreter::find_requested_python; mod common; +/// Create a directory with the requested Python binaries available. +pub(crate) fn create_bin_with_executables( + temp_dir: &assert_fs::TempDir, + python: Vec<&str>, +) -> Result { + let bin = temp_dir.child("bin"); + fs_err::create_dir(&bin)?; + for request in python { + let executable = find_requested_python(request)?; + let name = executable + .file_name() + .expect("Discovered executable must have a filename"); + symlink_file(&executable, bin.child(name))?; + } + Ok(bin.canonicalize()?) +} + /// requires-incompatible-python-version-compatible-override /// /// The user requires a package which requires a Python version greater than the @@ -38,6 +60,8 @@ fn requires_incompatible_python_version_compatible_override() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "3.9"); + let python_versions = vec![]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -61,7 +85,7 @@ fn requires_incompatible_python_version_compatible_override() -> Result<()> { .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" success: true exit_code: 0 @@ -100,6 +124,8 @@ fn requires_compatible_python_version_incompatible_override() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "3.11"); + let python_versions = vec![]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -123,7 +149,7 @@ fn requires_compatible_python_version_incompatible_override() -> Result<()> { .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" success: false exit_code: 1 @@ -162,6 +188,8 @@ fn requires_incompatible_python_version_compatible_override_no_wheels() -> Resul let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "3.9"); + let python_versions = vec![]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -188,7 +216,7 @@ fn requires_incompatible_python_version_compatible_override_no_wheels() -> Resul .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" success: false exit_code: 1 @@ -205,6 +233,75 @@ fn requires_incompatible_python_version_compatible_override_no_wheels() -> Resul Ok(()) } +/// requires-incompatible-python-version-compatible-override-no-wheels-available-system +/// +/// The user requires a package which requires a incompatible Python version, but +/// they request a compatible Python version for package resolution. There are only +/// source distributions available for the package. The user has a compatible Python +/// version installed elsewhere on their system. +/// +/// ```text +/// ae5b2665 +/// ├── environment +/// │ ├── python3.11 +/// │ └── python3.9 (active) +/// ├── root +/// │ └── requires a==1.0.0 +/// │ └── satisfied by a-1.0.0 +/// └── a +/// └── a-1.0.0 +/// └── requires python>=3.10 (incompatible with environment) +/// ``` +#[test] +fn requires_incompatible_python_version_compatible_override_no_wheels_available_system( +) -> Result<()> { + let temp_dir = assert_fs::TempDir::new()?; + let cache_dir = assert_fs::TempDir::new()?; + let venv = create_venv(&temp_dir, &cache_dir, "3.9"); + let python_versions = vec!["3.11"]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; + + // In addition to the standard filters, swap out package names for more realistic messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"a-ae5b2665", "albatross")); + filters.push((r"-ae5b2665", "")); + + let requirements_in = temp_dir.child("requirements.in"); + requirements_in.write_str("a-ae5b2665==1.0.0")?; + + // Since there is a compatible Python version available on the system, it should be + // used to build the source distributions. + insta::with_settings!({ + filters => filters + }, { + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) + .arg("pip") + .arg("compile") + .arg("requirements.in") + .arg("--python-version=3.11") + .arg("--extra-index-url") + .arg("https://test.pypi.org/simple") + .arg("--cache-dir") + .arg(cache_dir.path()) + .env("VIRTUAL_ENV", venv.as_os_str()) + .env("PUFFIN_NO_WRAP", "1") + .env("PUFFIN_PYTHON_PATH", bin) + .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.11 --extra-index-url https://test.pypi.org/simple --cache-dir [CACHE_DIR] + albatross==1.0.0 + + ----- stderr ----- + Resolved 1 package in [TIME] + "###); + }); + + Ok(()) +} + /// requires-incompatible-python-version-compatible-override-no-compatible-wheels /// /// The user requires a package which requires a incompatible Python version, but @@ -227,6 +324,8 @@ fn requires_incompatible_python_version_compatible_override_no_compatible_wheels let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "3.9"); + let python_versions = vec![]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -253,7 +352,7 @@ fn requires_incompatible_python_version_compatible_override_no_compatible_wheels .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" success: false exit_code: 1 @@ -296,6 +395,8 @@ fn requires_incompatible_python_version_compatible_override_other_wheel() -> Res let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "3.9"); + let python_versions = vec![]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -323,7 +424,7 @@ fn requires_incompatible_python_version_compatible_override_other_wheel() -> Res .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" success: false exit_code: 1 @@ -369,6 +470,8 @@ fn requires_python_patch_version_override_no_patch() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "3.8.18"); + let python_versions = vec![]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -394,7 +497,7 @@ fn requires_python_patch_version_override_no_patch() -> Result<()> { .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" success: false exit_code: 1 @@ -431,6 +534,8 @@ fn requires_python_patch_version_override_patch_compatible() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "3.8.18"); + let python_versions = vec![]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -454,7 +559,7 @@ fn requires_python_patch_version_override_patch_compatible() -> Result<()> { .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" success: true exit_code: 0 diff --git a/crates/puffin/tests/pip_install_scenarios.rs b/crates/puffin/tests/pip_install_scenarios.rs index 1257f63cc..563aa5c72 100644 --- a/crates/puffin/tests/pip_install_scenarios.rs +++ b/crates/puffin/tests/pip_install_scenarios.rs @@ -1,7 +1,7 @@ //! DO NOT EDIT //! //! Generated with ./scripts/scenarios/update.py -//! Scenarios from +//! Scenarios from //! #![cfg(all(feature = "python", feature = "pypi"))] diff --git a/scripts/scenarios/templates/compile.mustache b/scripts/scenarios/templates/compile.mustache index 45aab4b08..d0d37d71c 100644 --- a/scripts/scenarios/templates/compile.mustache +++ b/scripts/scenarios/templates/compile.mustache @@ -5,16 +5,38 @@ //! #![cfg(all(feature = "python", feature = "pypi"))] +use std::path::PathBuf; use std::process::Command; use anyhow::Result; use assert_fs::fixture::{FileWriteStr, PathChild}; +use common::{create_venv, BIN_NAME, INSTA_FILTERS}; +#[cfg(unix)] +use fs_err::os::unix::fs::symlink as symlink_file; +#[cfg(windows)] +use fs_err::os::windows::fs::symlink_file; use insta_cmd::_macro_support::insta; use insta_cmd::{assert_cmd_snapshot, get_cargo_bin}; - -use common::{create_venv, BIN_NAME, INSTA_FILTERS}; +use puffin_interpreter::find_requested_python; mod common; + +/// Create a directory with the requested Python binaries available. +pub(crate) fn create_bin_with_executables( + temp_dir: &assert_fs::TempDir, + python: Vec<&str>, +) -> Result { + let bin = temp_dir.child("bin"); + fs_err::create_dir(&bin)?; + for request in python { + let executable = find_requested_python(request)?; + let name = executable + .file_name() + .expect("Discovered executable must have a filename"); + symlink(&executable, bin.child(name))?; + } + Ok(bin.canonicalize()?) +} {{#scenarios}} /// {{name}} @@ -34,6 +56,8 @@ fn {{module_name}}() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; let cache_dir = assert_fs::TempDir::new()?; let venv = create_venv(&temp_dir, &cache_dir, "{{environment.python}}"); + let python_versions = vec![{{#environment.additional_python}}"{{.}}", {{/environment.additional_python}}]; + let bin = create_bin_with_executables(&temp_dir, python_versions)?; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); @@ -69,7 +93,7 @@ fn {{module_name}}() -> Result<()> { .arg(cache_dir.path()) .env("VIRTUAL_ENV", venv.as_os_str()) .env("PUFFIN_NO_WRAP", "1") - .env("PUFFIN_PYTHON_PATH", "") + .env("PUFFIN_PYTHON_PATH", bin) .current_dir(&temp_dir), @r###" "###); }); diff --git a/scripts/scenarios/update.py b/scripts/scenarios/update.py index be7b73c4e..f754dc02b 100755 --- a/scripts/scenarios/update.py +++ b/scripts/scenarios/update.py @@ -45,7 +45,7 @@ import textwrap from pathlib import Path -PACKSE_COMMIT = "e944cb4c8f5d68457d0462ee19106509f63b8d34" +PACKSE_COMMIT = "a451c95105db0c2897d0ad1bde0703ba0fc40d3a" TOOL_ROOT = Path(__file__).parent TEMPLATES = TOOL_ROOT / "templates" INSTALL_TEMPLATE = TEMPLATES / "install.mustache"