Fix transparent Python upgrades in project environments (#19890)

## Summary

Store the `<maj>.<min>` Python version in project environments when an
upgradeable Python request is used so that the environment can actually
be re-used when the referenced python interpreter is upgraded.

Previously we'd always write the full version which meant that we
considered the environment to always be out of date even if the symlink
had been upgraded.

## Test Plan

Added a new test specifically for this, and adjusted an existing test to
ensure that non-upgradeable environments didn't regress.

Verified that dropping the fix would trigger every one of the new
regression test's checks.
This commit is contained in:
Tomasz Kramkowski
2026-06-17 20:39:55 +01:00
committed by GitHub
parent 28a98e187a
commit 18898c9821
2 changed files with 88 additions and 2 deletions
+5 -1
View File
@@ -528,7 +528,11 @@ pub(crate) fn create(
("uv".to_string(), version().to_string()),
(
"version_info".to_string(),
interpreter.markers().python_full_version().string.clone(),
if using_minor_version_link {
interpreter.python_minor_version().to_string()
} else {
interpreter.markers().python_full_version().string.clone()
},
),
(
"include-system-site-packages".to_string(),
+83 -1
View File
@@ -2,6 +2,7 @@ use anyhow::Result;
use assert_cmd::assert::OutputAssertExt;
use assert_fs::fixture::{FileTouch, FileWriteStr};
use assert_fs::prelude::PathChild;
use insta::assert_snapshot;
use uv_python::managed::platform_key_from_env;
use uv_static::EnvVars;
use uv_test::{LATEST_PYTHON_3_12, uv_snapshot};
@@ -839,6 +840,73 @@ fn python_upgrade_build_version() {
");
}
// A project environment should survive a transparent Python patch upgrade.
#[test]
fn python_sync_transparent_patch_upgrade_reuses_environment() -> Result<()> {
let context = uv_test::test_context_with_versions!(&[])
.with_python_download_cache()
.with_filtered_python_keys()
.with_filtered_exe_suffix()
.with_managed_python_dirs()
.with_filtered_latest_python_versions()
.with_pyvenv_cfg_filters();
context.temp_dir.child("pyproject.toml").write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.10"
"#,
)?;
context.python_install().arg("3.10.17").assert().success();
context
.sync()
.arg("--python")
.arg("3.10")
.assert()
.success();
// The minor-only `version_info` allows compatibility checks to accept a newer patch.
let pyvenv_cfg = fs_err::read_to_string(context.venv.child("pyvenv.cfg"))?;
insta::with_settings!({ filters => context.filters() }, {
assert_snapshot!(pyvenv_cfg, @r"
home = [PYTHON_HOME]
implementation = CPython
uv = [UV_VERSION]
version_info = 3.10
include-system-site-packages = false
prompt = project
");
});
context.python_upgrade().arg("3.10").assert().success();
// The sync should not report removing or creating the environment.
uv_snapshot!(context.filters(), context.sync().arg("--python").arg("3.10"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Checked in [TIME]
");
// The reused environment should run the upgraded Python patch.
uv_snapshot!(context.filters(), context.python_command().arg("--version"), @r"
success: true
exit_code: 0
----- stdout -----
Python 3.10.[LATEST]
----- stderr -----
");
Ok(())
}
// Regression test for <https://github.com/astral-sh/uv/issues/19100>.
//
// A virtual environment created by `uv sync` should pin to a patch version
@@ -852,7 +920,8 @@ fn python_sync_honors_pinned_patch_version() -> Result<()> {
.with_filtered_python_keys()
.with_filtered_exe_suffix()
.with_managed_python_dirs()
.with_filtered_latest_python_versions();
.with_filtered_latest_python_versions()
.with_pyvenv_cfg_filters();
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
@@ -898,6 +967,19 @@ fn python_sync_honors_pinned_patch_version() -> Result<()> {
Checked in [TIME]
");
// An exact-patch environment must retain the patch in `version_info`.
let pyvenv_cfg = fs_err::read_to_string(context.venv.child("pyvenv.cfg"))?;
insta::with_settings!({ filters => context.filters() }, {
assert_snapshot!(pyvenv_cfg, @r"
home = [PYTHON_HOME]
implementation = CPython
uv = [UV_VERSION]
version_info = 3.10.17
include-system-site-packages = false
prompt = project
");
});
// Install a newer patch version, which moves the mutable `cpython-3.10`
// minor-version link to the newer release.
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @r"