Update packse and remove Python 3.9.20 from test requirements (#17881)

Requires the packse PR to land first.
This commit is contained in:
konsti
2026-02-18 19:35:30 +01:00
committed by GitHub
parent 4cf70d6d87
commit 585dac12a9
10 changed files with 255 additions and 43 deletions
-2
View File
@@ -15,8 +15,6 @@
3.10.16 3.10.16
3.9.21 3.9.21
3.8.20 3.8.20
# The following are required for packse scenarios
3.9.20
# The following is needed for `==3.13` request tests # The following is needed for `==3.13` request tests
3.13.0 3.13.0
# A pre-release version required for testing # A pre-release version required for testing
+1 -1
View File
@@ -36,7 +36,7 @@ use uv_static::EnvVars;
// Exclude any packages uploaded after this date. // Exclude any packages uploaded after this date.
static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z"; static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z";
pub const PACKSE_VERSION: &str = "0.3.53"; pub const PACKSE_VERSION: &str = "0.3.59";
pub const DEFAULT_PYTHON_VERSION: &str = "3.12"; pub const DEFAULT_PYTHON_VERSION: &str = "3.12";
// The expected latest patch version for each Python minor version. // The expected latest patch version for each Python minor version.
+101 -1
View File
@@ -1,7 +1,7 @@
//! DO NOT EDIT //! DO NOT EDIT
//! //!
//! Generated with `./scripts/sync_scenarios.sh` //! Generated with `./scripts/sync_scenarios.sh`
//! Scenarios from <https://github.com/astral-sh/packse/tree/0.3.53/scenarios> //! Scenarios from <https://github.com/astral-sh/packse/tree/0.3.59/scenarios>
//! //!
#![cfg(all(feature = "test-python", feature = "test-pypi"))] #![cfg(all(feature = "test-python", feature = "test-pypi"))]
#![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_raw_string_hashes)]
@@ -5347,6 +5347,106 @@ fn virtual_package_extra_priorities() -> Result<()> {
Ok(()) Ok(())
} }
/// While both Linux and Windows are required and `win-only` has only a Windows wheel, `win-only` is also used only on Windows.
///
/// ```text
/// requires-python-subset
/// ├── environment
/// │ └── python3.12
/// ├── root
/// │ └── requires win-only; sys_platform == "win32"
/// │ └── satisfied by win-only-1.0.0
/// └── win-only
/// └── win-only-1.0.0
/// ```
#[test]
fn requires_python_subset() -> Result<()> {
let context = uv_test::test_context!("3.12");
// In addition to the standard filters, swap out package names for shorter messages
let mut filters = context.filters();
filters.push((r"requires-python-subset-", "package-"));
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r###"
[project]
name = "project"
version = "0.1.0"
dependencies = [
'''requires-python-subset-win-only; sys_platform == "win32"''',
]
requires-python = ">=3.12"
[tool.uv]
required-environments = [
'''sys_platform == "linux"''',
'''sys_platform == "win32"''',
]
"###,
)?;
let mut cmd = context.lock();
cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER);
cmd.arg("--index-url").arg(packse_index_url());
uv_snapshot!(filters, cmd, @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
"
);
let lock = context.read("uv.lock");
insta::with_settings!({
filters => filters,
}, {
assert_snapshot!(
lock, @r#"
version = 1
revision = 3
requires-python = ">=3.12"
required-markers = [
"sys_platform == 'linux'",
"sys_platform == 'win32'",
]
[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "package-win-only", marker = "sys_platform == 'win32'" },
]
[package.metadata]
requires-dist = [{ name = "package-win-only", marker = "sys_platform == 'win32'" }]
[[package]]
name = "package-win-only"
version = "1.0.0"
source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }
wheels = [
{ url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/requires_python_subset_win_only-1.0.0-cp312-abi3-win_amd64.whl", hash = "sha256:91d59021b1c4aad7449e315ae1248c5c588a7e84cb7592671a41453012302711" },
]
"#
);
});
// Assert the idempotence of `uv lock` when resolving from the lockfile (`--locked`).
context
.lock()
.arg("--locked")
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
.arg("--index-url")
.arg(packse_index_url())
.assert()
.success();
Ok(())
}
/// When a dependency is only required on a specific platform (like x86_64), omit wheels that target other platforms (like aarch64). /// When a dependency is only required on a specific platform (like x86_64), omit wheels that target other platforms (like aarch64).
/// ///
/// ```text /// ```text
+28 -27
View File
@@ -1,10 +1,11 @@
//! DO NOT EDIT //! DO NOT EDIT
//! //!
//! Generated with `./scripts/sync_scenarios.sh` //! Generated with `./scripts/sync_scenarios.sh`
//! Scenarios from <https://github.com/astral-sh/packse/tree/0.3.53/scenarios> //! Scenarios from <https://github.com/astral-sh/packse/tree/0.3.59/scenarios>
//! //!
#![cfg(all(feature = "test-python", feature = "test-pypi", unix))] #![cfg(all(feature = "test-python", feature = "test-pypi", unix))]
use std::env;
use std::process::Command; use std::process::Command;
use anyhow::Result; use anyhow::Result;
@@ -352,7 +353,7 @@ fn incompatible_python_compatible_override() -> Result<()> {
/// ```text /// ```text
/// python-patch-override-no-patch /// python-patch-override-no-patch
/// ├── environment /// ├── environment
/// │ └── python3.9.20 /// │ └── python3.9.21
/// ├── root /// ├── root
/// │ └── requires a==1.0.0 /// │ └── requires a==1.0.0
/// │ └── satisfied by a-1.0.0 /// │ └── satisfied by a-1.0.0
@@ -363,7 +364,7 @@ fn incompatible_python_compatible_override() -> Result<()> {
#[cfg(feature = "test-python-patch")] #[cfg(feature = "test-python-patch")]
#[test] #[test]
fn python_patch_override_no_patch() -> Result<()> { fn python_patch_override_no_patch() -> Result<()> {
let context = uv_test::test_context!("3.9.20"); let context = uv_test::test_context!("3.9.21");
let python_versions = &[]; let python_versions = &[];
// In addition to the standard filters, swap out package names for shorter messages // In addition to the standard filters, swap out package names for shorter messages
@@ -376,18 +377,18 @@ fn python_patch_override_no_patch() -> Result<()> {
// Since the resolver is asked to solve with 3.9, the minimum compatible Python requirement is treated as 3.9.0. // Since the resolver is asked to solve with 3.9, the minimum compatible Python requirement is treated as 3.9.0.
let output = uv_snapshot!(filters, command(&context, python_versions) let output = uv_snapshot!(filters, command(&context, python_versions)
.arg("--python-version=3.9") .arg("--python-version=3.9")
, @r" , @"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
× No solution found when resolving dependencies: × No solution found when resolving dependencies:
╰─▶ Because the requested Python version (>=3.9) does not satisfy Python>=3.9.4 and package-a==1.0.0 depends on Python>=3.9.4, we can conclude that package-a==1.0.0 cannot be used. ╰─▶ Because the requested Python version (>=3.9) does not satisfy Python>=3.9.4 and package-a==1.0.0 depends on Python>=3.9.4, 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 your requirements are unsatisfiable. And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable.
hint: The `--python-version` value (>=3.9) includes Python versions that are not supported by your dependencies (e.g., package-a==1.0.0 only supports >=3.9.4). Consider using a higher `--python-version` value. hint: The `--python-version` value (>=3.9) includes Python versions that are not supported by your dependencies (e.g., package-a==1.0.0 only supports >=3.9.4). Consider using a higher `--python-version` value.
" "
); );
output.assert().failure(); output.assert().failure();
@@ -400,7 +401,7 @@ fn python_patch_override_no_patch() -> Result<()> {
/// ```text /// ```text
/// python-patch-override-patch-compatible /// python-patch-override-patch-compatible
/// ├── environment /// ├── environment
/// │ └── python3.9.20 /// │ └── python3.9.21
/// ├── root /// ├── root
/// │ └── requires a==1.0.0 /// │ └── requires a==1.0.0
/// │ └── satisfied by a-1.0.0 /// │ └── satisfied by a-1.0.0
@@ -411,7 +412,7 @@ fn python_patch_override_no_patch() -> Result<()> {
#[cfg(feature = "test-python-patch")] #[cfg(feature = "test-python-patch")]
#[test] #[test]
fn python_patch_override_patch_compatible() -> Result<()> { fn python_patch_override_patch_compatible() -> Result<()> {
let context = uv_test::test_context!("3.9.20"); let context = uv_test::test_context!("3.9.21");
let python_versions = &[]; let python_versions = &[];
// In addition to the standard filters, swap out package names for shorter messages // In addition to the standard filters, swap out package names for shorter messages
@@ -423,19 +424,19 @@ fn python_patch_override_patch_compatible() -> Result<()> {
let output = uv_snapshot!(filters, command(&context, python_versions) let output = uv_snapshot!(filters, command(&context, python_versions)
.arg("--python-version=3.9.0") .arg("--python-version=3.9.0")
, @r" , @"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
# This file was autogenerated by uv via the following command: # This file was autogenerated by uv via the following command:
# uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.9.0 # uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.9.0
package-a==1.0.0 package-a==1.0.0
# via -r requirements.in # via -r requirements.in
----- stderr ----- ----- stderr -----
warning: The requested Python version 3.9.0 is not available; 3.9.20 will be used to build dependencies instead. warning: The requested Python version 3.9.0 is not available; 3.9.21 will be used to build dependencies instead.
Resolved 1 package in [TIME] Resolved 1 package in [TIME]
" "
); );
output.assert().success().stdout(predicate::str::contains( output.assert().success().stdout(predicate::str::contains(
+115 -2
View File
@@ -1,7 +1,7 @@
//! DO NOT EDIT //! DO NOT EDIT
//! //!
//! Generated with `./scripts/sync_scenarios.sh` //! Generated with `./scripts/sync_scenarios.sh`
//! Scenarios from <https://github.com/astral-sh/packse/tree/0.3.53/scenarios> //! Scenarios from <https://github.com/astral-sh/packse/tree/0.3.59/scenarios>
//! //!
#![cfg(all(feature = "test-python", feature = "test-pypi", unix))] #![cfg(all(feature = "test-python", feature = "test-pypi", unix))]
@@ -23,6 +23,119 @@ fn command(context: &TestContext) -> Command {
command command
} }
/// There are two packages, `a` and `b`. All versions of `b` require a specific
/// version of `a`, but that version requires a package `c` that does not exist. The resolver
/// must backtrack through all versions of `b` and eventually fail because no solution exists.
///
/// ```text
/// backtrack-to-missing-package
/// ├── environment
/// │ └── python3.12
/// ├── root
/// │ ├── requires a
/// │ │ ├── satisfied by a-2.0.0
/// │ │ └── satisfied by a-1.0.0
/// │ └── requires b
/// │ ├── satisfied by b-1.0.0
/// │ ├── satisfied by b-2.0.0
/// │ └── satisfied by b-3.0.0
/// ├── a
/// │ ├── a-2.0.0
/// │ └── a-1.0.0
/// │ └── requires c
/// │ └── unsatisfied: no versions for package
/// └── b
/// ├── b-1.0.0
/// │ └── requires a==1.0.0
/// │ └── satisfied by a-1.0.0
/// ├── b-2.0.0
/// │ └── requires a==1.0.0
/// │ └── satisfied by a-1.0.0
/// └── b-3.0.0
/// └── requires a==1.0.0
/// └── satisfied by a-1.0.0
/// ```
#[test]
fn backtrack_to_missing_package() {
let context = uv_test::test_context!("3.12");
// In addition to the standard filters, swap out package names for shorter messages
let mut filters = context.filters();
filters.push((r"backtrack-to-missing-package-", "package-"));
uv_snapshot!(filters, command(&context)
.arg("backtrack-to-missing-package-a")
.arg("backtrack-to-missing-package-b")
, @"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-c was not found in the package registry and package-a==1.0.0 depends on package-c, we can conclude that package-a==1.0.0 cannot be used.
And because all versions of package-b depend on package-a==1.0.0 and you require package-b, we can conclude that your requirements are unsatisfiable.
");
context.assert_not_installed("backtrack_to_missing_package_a");
context.assert_not_installed("backtrack_to_missing_package_b");
}
/// There are two packages, `a` and `b`. The latest version of `b` requires
/// a specific version of `a`. The older version of `b` requires a package `c` that does not
/// exist. The resolver should backtrack on `a` (not `b`) to find a solution without needing
/// to try `b==1.0.0` which would fail due to the missing package.
///
/// ```text
/// backtrack-with-missing-package
/// ├── environment
/// │ └── python3.12
/// ├── root
/// │ ├── requires a
/// │ │ ├── satisfied by a-1.0.0
/// │ │ └── satisfied by a-2.0.0
/// │ └── requires b
/// │ ├── satisfied by b-1.0.0
/// │ └── satisfied by b-2.0.0
/// ├── a
/// │ ├── a-1.0.0
/// │ └── a-2.0.0
/// └── b
/// ├── b-1.0.0
/// │ └── requires c
/// │ └── unsatisfied: no versions for package
/// └── b-2.0.0
/// └── requires a==1.0.0
/// └── satisfied by a-1.0.0
/// ```
#[test]
fn backtrack_with_missing_package() {
let context = uv_test::test_context!("3.12");
// In addition to the standard filters, swap out package names for shorter messages
let mut filters = context.filters();
filters.push((r"backtrack-with-missing-package-", "package-"));
uv_snapshot!(filters, command(&context)
.arg("backtrack-with-missing-package-a")
.arg("backtrack-with-missing-package-b")
, @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ package-a==1.0.0
+ package-b==2.0.0
");
context.assert_installed("backtrack_with_missing_package_a", "1.0.0");
context.assert_installed("backtrack_with_missing_package_b", "2.0.0");
}
/// The user requires an exact version of package `a` but only other versions exist /// The user requires an exact version of package `a` but only other versions exist
/// ///
/// ```text /// ```text
@@ -3408,7 +3521,7 @@ fn python_greater_than_current_patch() {
uv_snapshot!(filters, command(&context) uv_snapshot!(filters, command(&context)
.arg("python-greater-than-current-patch-a==1.0.0") .arg("python-greater-than-current-patch-a==1.0.0")
, @r" , @"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
+3 -3
View File
@@ -51,7 +51,7 @@ PACKSE = TOOL_ROOT / "packse-scenarios"
REQUIREMENTS = TOOL_ROOT / "pylock.toml" REQUIREMENTS = TOOL_ROOT / "pylock.toml"
PROJECT_ROOT = TOOL_ROOT.parent.parent PROJECT_ROOT = TOOL_ROOT.parent.parent
TESTS = PROJECT_ROOT / "crates" / "uv" / "tests" / "it" TESTS = PROJECT_ROOT / "crates" / "uv" / "tests" / "it"
TESTS_COMMON_MOD_RS = TESTS / "common" / "mod.rs" TESTS_COMMON_MOD_RS = PROJECT_ROOT / "crates" / "uv-test" / "src" / "lib.rs"
try: try:
import packse import packse
@@ -185,7 +185,7 @@ def main(
resolver_options = scenario["resolver_options"] or {} resolver_options = scenario["resolver_options"] or {}
# Avoid writing the empty `required-environments = []` # Avoid writing the empty `required-environments = []`
resolver_options["has_required_environments"] = bool( resolver_options["has_required_environments"] = bool(
resolver_options["required_environments"] resolver_options.get("required_environments", [])
) )
if resolver_options.get("universal"): if resolver_options.get("universal"):
lock_scenarios.append(scenario) lock_scenarios.append(scenario)
@@ -259,7 +259,7 @@ def main(
"insta", "insta",
"test", "test",
"--features", "--features",
"pypi,python,python-patch", "test-pypi,test-python,test-python-patch",
"--accept", "--accept",
"--test-runner", "--test-runner",
"nextest", "nextest",
+3 -3
View File
@@ -66,9 +66,9 @@ wheels = [{ url = "https://files.pythonhosted.org/packages/20/12/38679034af33278
[[packages]] [[packages]]
name = "packse" name = "packse"
version = "0.3.53" version = "0.3.59"
sdist = { url = "https://files.pythonhosted.org/packages/52/58/373b6281bb741e875893dc351ac5f180c3fdce18a3b889f773725ff964b2/packse-0.3.53.tar.gz", upload-time = 2025-09-16T09:37:55Z, size = 5879063, hashes = { sha256 = "fcdbbb60f8ad4af94901891699a95ade4f15b9e769b4d8f443a2f3ef7aa74067" } } sdist = { url = "https://files.pythonhosted.org/packages/16/90/51404d8933506bd9554f607f5054f4715e0a5e1d34e4c6542580553e8b75/packse-0.3.59.tar.gz", upload-time = 2026-02-18T17:44:17Z, size = 5880109, hashes = { sha256 = "718bcca5dd1e9321f5c2918d5975ffd772f0c3b150cf29b03979677a003d73d2" } }
wheels = [{ url = "https://files.pythonhosted.org/packages/fc/86/d5482bb2933fe47d282b1dae74cc9084b094f28229848ba8ea01a77fe0da/packse-0.3.53-py3-none-any.whl", upload-time = 2025-09-16T09:37:53Z, size = 34039, hashes = { sha256 = "78cf05f5e0b916f4070a66f04f3b371e2d4ac0c3917f38cb692a33fa6e9d764b" } }] wheels = [{ url = "https://files.pythonhosted.org/packages/8d/95/b4a997b57a1f46f0f7575c1c021ee15e8fc1470b6ce74010239079d1d9aa/packse-0.3.59-py3-none-any.whl", upload-time = 2026-02-18T17:44:16Z, size = 34107, hashes = { sha256 = "10a3689ce0c00805cd7f1589862ab2d8f9fd4ab38c117342c351bfb13daa5c69" } }]
[[packages]] [[packages]]
name = "pathspec" name = "pathspec"
+1 -1
View File
@@ -1,5 +1,5 @@
[dependency-groups] [dependency-groups]
packse = [ packse = [
"chevron-blue", "chevron-blue",
"packse>=0.3.53" "packse>=0.3.59"
] ]
+1 -1
View File
@@ -24,7 +24,7 @@ use uv_test::{
fn command(context: &TestContext, python_versions: &[&str]) -> Command { fn command(context: &TestContext, python_versions: &[&str]) -> Command {
let python_path = python_path_with_versions(&context.temp_dir, python_versions) let python_path = python_path_with_versions(&context.temp_dir, python_versions)
.expect("Failed to create Python test path"); .expect("Failed to create Python test path");
let mut command = Command::new(get_bin()); let mut command = Command::new(get_bin!());
command command
.arg("pip") .arg("pip")
.arg("compile") .arg("compile")
+2 -2
View File
@@ -2,7 +2,7 @@
This script reads the download-metadata.json file and extracts the latest This script reads the download-metadata.json file and extracts the latest
patch version for each minor version (3.15, 3.14, 3.13, 3.12, 3.11, 3.10). patch version for each minor version (3.15, 3.14, 3.13, 3.12, 3.11, 3.10).
It then updates the LATEST_PYTHON_X_Y constants in crates/uv/tests/it/common/mod.rs. It then updates the LATEST_PYTHON_X_Y constants in crates/uv-test/src/lib.rs.
For minor versions with stable releases, it uses the latest stable version. For minor versions with stable releases, it uses the latest stable version.
For minor versions with only prereleases, it uses the latest prerelease. For minor versions with only prereleases, it uses the latest prerelease.
@@ -70,7 +70,7 @@ def main() -> None:
if minor not in latest_versions: if minor not in latest_versions:
latest_versions[minor] = prerelease_versions[minor] latest_versions[minor] = prerelease_versions[minor]
# Update the constants in common/mod.rs # Update the constants in uv-test/src/lib.rs
lib_path = ROOT / "crates" / "uv-test" / "src" / "lib.rs" lib_path = ROOT / "crates" / "uv-test" / "src" / "lib.rs"
content = lib_path.read_text() content = lib_path.read_text()