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.
This commit is contained in:
Zanie Blue 2024-04-09 10:04:28 -05:00 committed by GitHub
parent d7ff8d93c0
commit 1cdadbdec8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 82 additions and 38 deletions

View file

@ -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,

View file

@ -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.
"###
);

View file

@ -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

View file

@ -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.
"###
);

View file

@ -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.
"###);

View file

@ -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.
"###
);

View file

@ -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<PythonVersion>,
}
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::<Vec<_>>();
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
"###