Do not match cpython 3.10 for cpython-3.1 (#17972)

<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
bug fix, in find version 3.1 should not match 3.10

## Test Plan

<!-- How was it tested? -->

---------

Co-authored-by: konstin <konstin@mailbox.org>
Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
cui
2026-02-13 01:46:40 +08:00
committed by GitHub
parent 33a8132b87
commit 4ce3853c52
2 changed files with 41 additions and 8 deletions
+9 -1
View File
@@ -27,7 +27,7 @@ use which::{which, which_all};
use crate::downloads::{ManagedPythonDownloadList, PlatformRequest, PythonDownloadRequest};
use crate::implementation::ImplementationName;
use crate::installation::PythonInstallation;
use crate::installation::{PythonInstallation, PythonInstallationKey};
use crate::interpreter::Error as InterpreterError;
use crate::interpreter::{StatusCodeError, UnexpectedResponseError};
use crate::managed::{ManagedPythonInstallations, PythonMinorVersionLink};
@@ -2975,6 +2975,14 @@ impl VersionRequest {
}
}
/// Check if a [`PythonInstallationKey`] is compatible with the request.
///
/// WARNING: Use [`VersionRequest::matches_interpreter`] too. This method is only suitable to
/// avoid querying interpreters if it's clear it cannot fulfill the request.
pub(crate) fn matches_installation_key(&self, key: &PythonInstallationKey) -> bool {
self.matches_major_minor_patch_prerelease(key.major, key.minor, key.patch, key.prerelease())
}
/// Whether a patch version segment is present in the request.
fn has_patch(&self) -> bool {
match self {
+32 -7
View File
@@ -24,6 +24,7 @@ use uv_state::{StateBucket, StateStore};
use uv_static::EnvVars;
use uv_trampoline_builder::{Launcher, LauncherKind};
use crate::discovery::VersionRequest;
use crate::downloads::{Error as DownloadError, ManagedPythonDownload};
use crate::implementation::{
Error as ImplementationError, ImplementationName, LenientImplementationName,
@@ -305,15 +306,10 @@ impl ManagedPythonInstallations {
&'a self,
version: &'a PythonVersion,
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation> + 'a, Error> {
let request = VersionRequest::from(version);
Ok(self
.find_matching_current_platform()?
.filter(move |installation| {
installation
.path
.file_name()
.map(OsStr::to_string_lossy)
.is_some_and(|filename| filename.starts_with(&format!("cpython-{version}")))
}))
.filter(move |installation| request.matches_installation_key(installation.key())))
}
pub fn root(&self) -> &Path {
@@ -1399,4 +1395,33 @@ mod tests {
// Older patch version should not be an upgrade even with newer build
assert!(!older_patch_newer_build.is_upgrade_of(&newer_patch_older_build));
}
#[test]
fn test_find_version_matching() {
use crate::PythonVersion;
let platform = Platform::from_env().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
// Create mock installation directories
fs::create_dir(temp_dir.path().join(format!("cpython-3.10.0-{platform}"))).unwrap();
temp_env::with_var(
uv_static::EnvVars::UV_PYTHON_INSTALL_DIR,
Some(temp_dir.path()),
|| {
let installations = ManagedPythonInstallations::from_settings(None).unwrap();
// Version 3.1 should NOT match 3.10
let v3_1 = PythonVersion::from_str("3.1").unwrap();
let matched: Vec<_> = installations.find_version(&v3_1).unwrap().collect();
assert_eq!(matched.len(), 0);
// Check that 3.10 matches
let v3_10 = PythonVersion::from_str("3.10").unwrap();
let matched: Vec<_> = installations.find_version(&v3_10).unwrap().collect();
assert_eq!(matched.len(), 1);
},
);
}
}