From 1cdadbdec83ae226471663b189e72431ae63f39e Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Tue, 9 Apr 2024 10:04:28 -0500 Subject: [PATCH] Add filtering of patch Python versions unless explicitly requested (#2930) Elides Python patch versions from the test suite unless the test specifically requests a patch version. This reduces some toil when not using our bootstrapped Python versions. Partially addresses https://github.com/astral-sh/uv/issues/2165 though we'll need changes to the scenario tests to really support their case. --- crates/uv/tests/common/mod.rs | 18 ++++++++- crates/uv/tests/pip_compile.rs | 8 ++-- crates/uv/tests/pip_compile_scenarios.rs | 16 ++++---- crates/uv/tests/pip_install.rs | 8 ++-- crates/uv/tests/pip_install_scenarios.rs | 14 +++---- crates/uv/tests/pip_sync.rs | 6 +-- crates/uv/tests/venv.rs | 50 ++++++++++++++++++------ 7 files changed, 82 insertions(+), 38 deletions(-) diff --git a/crates/uv/tests/common/mod.rs b/crates/uv/tests/common/mod.rs index da179ea4b..5a46a956f 100644 --- a/crates/uv/tests/common/mod.rs +++ b/crates/uv/tests/common/mod.rs @@ -15,10 +15,11 @@ use std::env; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::Output; +use std::str::FromStr; use uv_fs::Simplified; use uv_cache::Cache; -use uv_interpreter::find_requested_python; +use uv_interpreter::{find_requested_python, PythonVersion}; // Exclude any packages uploaded after this date. pub static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z"; @@ -69,6 +70,9 @@ impl TestContext { let site_packages = site_packages_path(&venv, format!("python{python_version}")); + let python_version = + PythonVersion::from_str(python_version).expect("Tests must use valid Python versions"); + let mut filters = Vec::new(); filters.extend( Self::path_patterns(&cache_dir) @@ -123,6 +127,18 @@ impl TestContext { // Destroy any remaining UNC prefixes (Windows only) filters.push((r"\\\\\?\\".to_string(), String::new())); + // Add Python patch version filtering unless explicitly requested to ensure + // snapshots are patch version agnostic when it is not a part of the test. + if python_version.patch().is_none() { + filters.push(( + format!( + r"({})\.\d+", + regex::escape(python_version.to_string().as_str()) + ), + "$1.[X]".to_string(), + )); + } + Self { temp_dir, cache_dir, diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index befa69456..292d4507b 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -5779,14 +5779,14 @@ requires-python = "<=3.8" let requirements_in = context.temp_dir.child("requirements.in"); requirements_in.write_str(&format!("-e {}", editable_dir.path().display()))?; - uv_snapshot!(context.compile() + uv_snapshot!(context.filters(), context.compile() .arg("requirements.in"), @r###" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - error: Editable `example` requires Python <=3.8, but resolution targets Python 3.12.1 + error: Editable `example` requires Python <=3.8, but resolution targets Python 3.12.[X] "### ); @@ -6056,7 +6056,7 @@ requires-python = "<=3.8" let requirements_in = context.temp_dir.child("requirements.in"); requirements_in.write_str(&format!("example @ {}", editable_dir.path().display()))?; - uv_snapshot!(context.compile() + uv_snapshot!(context.filters(), context.compile() .arg("requirements.in"), @r###" success: false exit_code: 1 @@ -6064,7 +6064,7 @@ requires-python = "<=3.8" ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.12.1) does not satisfy Python<=3.8 and example==0.0.0 depends on Python<=3.8, we can conclude that example==0.0.0 cannot be used. + ╰─▶ Because the current Python version (3.12.[X]) does not satisfy Python<=3.8 and example==0.0.0 depends on Python<=3.8, we can conclude that example==0.0.0 cannot be used. And because only example==0.0.0 is available and you require example, we can conclude that the requirements are unsatisfiable. "### ); diff --git a/crates/uv/tests/pip_compile_scenarios.rs b/crates/uv/tests/pip_compile_scenarios.rs index eb9c8d1e6..222467a19 100644 --- a/crates/uv/tests/pip_compile_scenarios.rs +++ b/crates/uv/tests/pip_compile_scenarios.rs @@ -84,7 +84,7 @@ fn incompatible_python_compatible_override() -> Result<()> { package-a==1.0.0 ----- stderr ----- - warning: The requested Python version 3.11 is not available; 3.9.18 will be used to build dependencies instead. + warning: The requested Python version 3.11 is not available; 3.9.[X] will be used to build dependencies instead. Resolved 1 package in [TIME] "### ); @@ -130,7 +130,7 @@ fn compatible_python_incompatible_override() -> Result<()> { ----- stdout ----- ----- stderr ----- - warning: The requested Python version 3.9 is not available; 3.11.7 will be used to build dependencies instead. + warning: The requested Python version 3.9 is not available; 3.11.[X] will be used to build dependencies instead. × No solution found when resolving dependencies: ╰─▶ Because the requested Python version (3.9) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. And because you require package-a==1.0.0, we can conclude that the requirements are unsatisfiable. @@ -184,9 +184,9 @@ fn incompatible_python_compatible_override_unavailable_no_wheels() -> Result<()> ----- stdout ----- ----- stderr ----- - warning: The requested Python version 3.11 is not available; 3.9.18 will be used to build dependencies instead. + warning: The requested Python version 3.11 is not available; 3.9.[X] will be used to build dependencies instead. × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.18) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. And because you require package-a==1.0.0, we can conclude that the requirements are unsatisfiable. "### ); @@ -295,9 +295,9 @@ fn incompatible_python_compatible_override_no_compatible_wheels() -> Result<()> ----- stdout ----- ----- stderr ----- - warning: The requested Python version 3.11 is not available; 3.9.18 will be used to build dependencies instead. + warning: The requested Python version 3.11 is not available; 3.9.[X] will be used to build dependencies instead. × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.18) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. And because you require package-a==1.0.0, we can conclude that the requirements are unsatisfiable. "### ); @@ -353,9 +353,9 @@ fn incompatible_python_compatible_override_other_wheel() -> Result<()> { ----- stdout ----- ----- stderr ----- - warning: The requested Python version 3.11 is not available; 3.9.18 will be used to build dependencies instead. + warning: The requested Python version 3.11 is not available; 3.9.[X] will be used to build dependencies instead. × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.18) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. And because only the following versions of package-a are available: package-a==1.0.0 package-a==2.0.0 diff --git a/crates/uv/tests/pip_install.rs b/crates/uv/tests/pip_install.rs index 7a153913c..85a890dc1 100644 --- a/crates/uv/tests/pip_install.rs +++ b/crates/uv/tests/pip_install.rs @@ -2415,7 +2415,7 @@ requires-python = "<=3.8" "#, )?; - uv_snapshot!(context.install() + uv_snapshot!(context.filters(), context.install() .arg("--editable") .arg(editable_dir.path()), @r###" success: false @@ -2423,7 +2423,7 @@ requires-python = "<=3.8" ----- stdout ----- ----- stderr ----- - error: Editable `example` requires Python <=3.8, but 3.12.1 is installed + error: Editable `example` requires Python <=3.8, but 3.12.[X] is installed "### ); @@ -2864,7 +2864,7 @@ requires-python = "<=3.8" "#, )?; - uv_snapshot!(context.install() + uv_snapshot!(context.filters(), context.install() .arg(format!("example @ {}", editable_dir.path().display())), @r###" success: false exit_code: 1 @@ -2872,7 +2872,7 @@ requires-python = "<=3.8" ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.12.1) does not satisfy Python<=3.8 and example==0.0.0 depends on Python<=3.8, we can conclude that example==0.0.0 cannot be used. + ╰─▶ Because the current Python version (3.12.[X]) does not satisfy Python<=3.8 and example==0.0.0 depends on Python<=3.8, we can conclude that example==0.0.0 cannot be used. And because only example==0.0.0 is available and you require example, we can conclude that the requirements are unsatisfiable. "### ); diff --git a/crates/uv/tests/pip_install_scenarios.rs b/crates/uv/tests/pip_install_scenarios.rs index 0b5ab479c..0f17c4612 100644 --- a/crates/uv/tests/pip_install_scenarios.rs +++ b/crates/uv/tests/pip_install_scenarios.rs @@ -3671,7 +3671,7 @@ fn python_version_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.8.18) does not satisfy Python>=3.30 and package-a==1.0.0 depends on Python>=3.30, we can conclude that package-a==1.0.0 cannot be used. + ╰─▶ Because the current Python version (3.8.[X]) does not satisfy Python>=3.30 and package-a==1.0.0 depends on Python>=3.30, we can conclude that package-a==1.0.0 cannot be used. And because you require package-a==1.0.0, we can conclude that the requirements are unsatisfiable. "###); @@ -3713,7 +3713,7 @@ fn python_less_than_current() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.18) does not satisfy Python<=3.8 and package-a==1.0.0 depends on Python<=3.8, we can conclude that package-a==1.0.0 cannot be used. + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python<=3.8 and package-a==1.0.0 depends on Python<=3.8, we can conclude that package-a==1.0.0 cannot be used. And because you require package-a==1.0.0, we can conclude that the requirements are unsatisfiable. "###); @@ -3755,7 +3755,7 @@ fn python_greater_than_current() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.18) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. And because you require package-a==1.0.0, we can conclude that the requirements are unsatisfiable. "###); @@ -3961,22 +3961,22 @@ fn python_greater_than_current_excluded() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.18) does not satisfy Python>=3.10,<3.11 and the current Python version (3.9.18) does not satisfy Python>=3.12, we can conclude that any of: + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10,<3.11 and the current Python version (3.9.[X]) does not satisfy Python>=3.12, we can conclude that any of: Python>=3.10,<3.11 Python>=3.12 are incompatible. - And because the current Python version (3.9.18) does not satisfy Python>=3.11,<3.12, we can conclude that Python>=3.10 are incompatible. + And because the current Python version (3.9.[X]) does not satisfy Python>=3.11,<3.12, we can conclude that Python>=3.10 are incompatible. And because package-a==2.0.0 depends on Python>=3.10 and only the following versions of package-a are available: package-a<=2.0.0 package-a==3.0.0 package-a==4.0.0 we can conclude that package-a>=2.0.0,<3.0.0 cannot be used. (1) - Because the current Python version (3.9.18) does not satisfy Python>=3.11,<3.12 and the current Python version (3.9.18) does not satisfy Python>=3.12, we can conclude that Python>=3.11 are incompatible. + Because the current Python version (3.9.[X]) does not satisfy Python>=3.11,<3.12 and the current Python version (3.9.[X]) does not satisfy Python>=3.12, we can conclude that Python>=3.11 are incompatible. And because package-a==3.0.0 depends on Python>=3.11, we can conclude that package-a==3.0.0 cannot be used. And because we know from (1) that package-a>=2.0.0,<3.0.0 cannot be used, we can conclude that package-a>=2.0.0,<4.0.0 cannot be used. (2) - Because the current Python version (3.9.18) does not satisfy Python>=3.12 and package-a==4.0.0 depends on Python>=3.12, we can conclude that package-a==4.0.0 cannot be used. + Because the current Python version (3.9.[X]) does not satisfy Python>=3.12 and package-a==4.0.0 depends on Python>=3.12, we can conclude that package-a==4.0.0 cannot be used. And because we know from (2) that package-a>=2.0.0,<4.0.0 cannot be used, we can conclude that package-a>=2.0.0 cannot be used. And because you require package-a>=2.0.0, we can conclude that the requirements are unsatisfiable. "###); diff --git a/crates/uv/tests/pip_sync.rs b/crates/uv/tests/pip_sync.rs index a874e85df..b387eb9ac 100644 --- a/crates/uv/tests/pip_sync.rs +++ b/crates/uv/tests/pip_sync.rs @@ -2995,14 +2995,14 @@ requires-python = "<=3.5" let requirements_in = context.temp_dir.child("requirements.in"); requirements_in.write_str(&format!("-e {}", editable_dir.path().display()))?; - uv_snapshot!(command(&context) + uv_snapshot!(context.filters(), command(&context) .arg("requirements.in"), @r###" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - error: Editable `example` requires Python <=3.5, but 3.12.1 is installed + error: Editable `example` requires Python <=3.5, but 3.12.[X] is installed "### ); @@ -3071,7 +3071,7 @@ requires-python = "<=3.5" ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.12.1) does not satisfy Python<=3.5 and example==0.0.0 depends on Python<=3.5, we can conclude that example==0.0.0 cannot be used. + ╰─▶ Because the current Python version (3.12.[X]) does not satisfy Python<=3.5 and example==0.0.0 depends on Python<=3.5, we can conclude that example==0.0.0 cannot be used. And because only example==0.0.0 is available and you require example, we can conclude that the requirements are unsatisfiable. "### ); diff --git a/crates/uv/tests/venv.rs b/crates/uv/tests/venv.rs index 1e195708e..282ad0370 100644 --- a/crates/uv/tests/venv.rs +++ b/crates/uv/tests/venv.rs @@ -1,7 +1,7 @@ #![cfg(feature = "python")] -use std::ffi::OsString; use std::process::Command; +use std::{ffi::OsString, str::FromStr}; use anyhow::Result; use assert_cmd::prelude::*; @@ -9,6 +9,7 @@ use assert_fs::fixture::ChildPath; use assert_fs::prelude::*; use fs_err::PathExt; use uv_fs::Simplified; +use uv_interpreter::PythonVersion; use crate::common::{ create_bin_with_executables, get_bin, uv_snapshot, TestContext, EXCLUDE_NEWER, @@ -21,6 +22,7 @@ struct VenvTestContext { temp_dir: assert_fs::TempDir, venv: ChildPath, bin: OsString, + python_versions: Vec, } impl VenvTestContext { @@ -29,11 +31,18 @@ impl VenvTestContext { let bin = create_bin_with_executables(&temp_dir, python_versions) .expect("Failed to create bin dir"); let venv = temp_dir.child(".venv"); + let python_versions = python_versions + .iter() + .map(|version| { + PythonVersion::from_str(version).expect("Tests should use valid Python versions") + }) + .collect::>(); Self { cache_dir: assert_fs::TempDir::new().unwrap(), temp_dir, venv, bin, + python_versions, } } @@ -70,6 +79,25 @@ impl VenvTestContext { r"Activate with: (?:.*)\\Scripts\\activate".to_string(), "Activate with: source .venv/bin/activate".to_string(), )); + + // Add Python patch version filtering unless one was explicitly requested to ensure + // snapshots are patch version agnostic when it is not a part of the test. + if self + .python_versions + .iter() + .all(|version| version.patch().is_none()) + { + for python_version in &self.python_versions { + filters.push(( + format!( + r"({})\.\d+", + regex::escape(python_version.to_string().as_str()) + ), + "$1.[X]".to_string(), + )); + } + } + filters } } @@ -88,7 +116,7 @@ fn create_venv() { ----- stdout ----- ----- stderr ----- - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv Activate with: source .venv/bin/activate "### @@ -107,7 +135,7 @@ fn create_venv() { ----- stdout ----- ----- stderr ----- - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv Activate with: source .venv/bin/activate "### @@ -128,7 +156,7 @@ fn create_venv_defaults_to_cwd() { ----- stdout ----- ----- stderr ----- - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv Activate with: source .venv/bin/activate "### @@ -151,7 +179,7 @@ fn seed() { ----- stdout ----- ----- stderr ----- - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv + pip==24.0 Activate with: source .venv/bin/activate @@ -175,7 +203,7 @@ fn seed_older_python_version() { ----- stdout ----- ----- stderr ----- - Using Python 3.10.13 interpreter at: [PATH] + Using Python 3.10.[X] interpreter at: [PATH] Creating virtualenv at: .venv + pip==24.0 + setuptools==69.2.0 @@ -293,7 +321,7 @@ fn file_exists() -> Result<()> { ----- stdout ----- ----- stderr ----- - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv uv::venv::creation @@ -321,7 +349,7 @@ fn empty_dir_exists() -> Result<()> { ----- stdout ----- ----- stderr ----- - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv Activate with: source .venv/bin/activate "### @@ -350,7 +378,7 @@ fn non_empty_dir_exists() -> Result<()> { ----- stdout ----- ----- stderr ----- - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv uv::venv::creation @@ -395,7 +423,7 @@ fn windows_shims() -> Result<()> { ----- stderr ----- warning: virtualenv's `--clear` has no effect (uv always clears the virtual environment). - Using Python 3.8.12 interpreter at: [PATH] + Using Python 3.8.[X] interpreter at: [PATH] Creating virtualenv at: .venv Activate with: source .venv/bin/activate "### @@ -422,7 +450,7 @@ fn virtualenv_compatibility() { ----- stderr ----- warning: virtualenv's `--clear` has no effect (uv always clears the virtual environment). - Using Python 3.12.1 interpreter at: [PATH] + Using Python 3.12.[X] interpreter at: [PATH] Creating virtualenv at: .venv Activate with: source .venv/bin/activate "###