mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-03 02:22:19 +00:00
Add prerelease compatibility check (#8020)
## Summary Closes #7977. Makes `PythonDownloadRequest` account for the prerelease part if allowed. Also stores the prerelease in `PythonInstallationKey` directly as a `Prerelease` rather than a string. ## Test Plan Correctly picks the relevant prerelease (rather than picking the most recent one): ``` λ cargo run python install 3.13.0rc2 Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s Running `target/debug/uv python install 3.13.0rc2` Searching for Python versions matching: Python 3.13rc2 cpython-3.13.0rc2-macos-aarch64-none ------------------------------ 457.81 KiB/14.73 MiB ^C λ cargo run python install 3.13.0rc3 Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s Running `target/debug/uv python install 3.13.0rc3` Searching for Python versions matching: Python 3.13rc3 Found existing installation for Python 3.13rc3: cpython-3.13.0rc3-macos-aarch64-none ```
This commit is contained in:
parent
cfaa834dee
commit
37273cb4bc
7 changed files with 794 additions and 768 deletions
|
@ -1869,7 +1869,13 @@ impl VersionRequest {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn matches_major_minor_patch(&self, major: u8, minor: u8, patch: u8) -> bool {
|
||||
pub(crate) fn matches_major_minor_patch_prerelease(
|
||||
&self,
|
||||
major: u8,
|
||||
minor: u8,
|
||||
patch: u8,
|
||||
prerelease: Option<Prerelease>,
|
||||
) -> bool {
|
||||
match self {
|
||||
Self::Any | Self::Default => true,
|
||||
Self::Major(self_major, _) => *self_major == major,
|
||||
|
@ -1879,14 +1885,14 @@ impl VersionRequest {
|
|||
Self::MajorMinorPatch(self_major, self_minor, self_patch, _) => {
|
||||
(*self_major, *self_minor, *self_patch) == (major, minor, patch)
|
||||
}
|
||||
Self::Range(specifiers, _) => specifiers.contains(&Version::new([
|
||||
u64::from(major),
|
||||
u64::from(minor),
|
||||
u64::from(patch),
|
||||
])),
|
||||
Self::MajorMinorPrerelease(self_major, self_minor, _, _) => {
|
||||
Self::Range(specifiers, _) => specifiers.contains(
|
||||
&Version::new([u64::from(major), u64::from(minor), u64::from(patch)])
|
||||
.with_pre(prerelease),
|
||||
),
|
||||
Self::MajorMinorPrerelease(self_major, self_minor, self_prerelease, _) => {
|
||||
// Pre-releases of Python versions are always for the zero patch version
|
||||
(*self_major, *self_minor, 0) == (major, minor, patch)
|
||||
&& prerelease.map_or(true, |pre| *self_prerelease == pre)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,7 @@
|
|||
// Generated with `{{generated_with}}`
|
||||
// From template at `{{generated_from}}`
|
||||
|
||||
use std::borrow::Cow;
|
||||
use uv_pep440::{Prerelease, PrereleaseKind};
|
||||
|
||||
pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
|
||||
{{#versions}}
|
||||
|
@ -12,7 +12,7 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
|
|||
major: {{value.major}},
|
||||
minor: {{value.minor}},
|
||||
patch: {{value.patch}},
|
||||
prerelease: Cow::Borrowed("{{value.prerelease}}"),
|
||||
prerelease: {{value.prerelease}},
|
||||
implementation: LenientImplementationName::Known(ImplementationName::{{value.name}}),
|
||||
arch: Arch(target_lexicon::Architecture::{{value.arch}}),
|
||||
os: Os(target_lexicon::OperatingSystem::{{value.os}}),
|
||||
|
|
|
@ -260,8 +260,17 @@ impl PythonDownloadRequest {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
// If we don't allow pre-releases, don't match a key with a pre-release tag
|
||||
if !self.allows_prereleases() && key.prerelease.is_some() {
|
||||
return false;
|
||||
}
|
||||
if let Some(version) = &self.version {
|
||||
if !version.matches_major_minor_patch(key.major, key.minor, key.patch) {
|
||||
if !version.matches_major_minor_patch_prerelease(
|
||||
key.major,
|
||||
key.minor,
|
||||
key.patch,
|
||||
key.prerelease,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if version.is_freethreaded() {
|
||||
|
@ -269,10 +278,6 @@ impl PythonDownloadRequest {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
// If we don't allow pre-releases, don't match a key with a pre-release tag
|
||||
if !self.allows_prereleases() && !key.prerelease.is_empty() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -6,7 +5,7 @@ use tracing::{debug, info};
|
|||
|
||||
use uv_cache::Cache;
|
||||
use uv_client::BaseClientBuilder;
|
||||
use uv_pep440::Version;
|
||||
use uv_pep440::{Prerelease, Version};
|
||||
|
||||
use crate::discovery::{
|
||||
find_best_python_installation, find_python_installation, EnvironmentPreference, PythonRequest,
|
||||
|
@ -224,7 +223,7 @@ pub struct PythonInstallationKey {
|
|||
pub(crate) major: u8,
|
||||
pub(crate) minor: u8,
|
||||
pub(crate) patch: u8,
|
||||
pub(crate) prerelease: Cow<'static, str>,
|
||||
pub(crate) prerelease: Option<Prerelease>,
|
||||
pub(crate) os: Os,
|
||||
pub(crate) arch: Arch,
|
||||
pub(crate) libc: Libc,
|
||||
|
@ -236,7 +235,7 @@ impl PythonInstallationKey {
|
|||
major: u8,
|
||||
minor: u8,
|
||||
patch: u8,
|
||||
prerelease: String,
|
||||
prerelease: Option<Prerelease>,
|
||||
os: Os,
|
||||
arch: Arch,
|
||||
libc: Libc,
|
||||
|
@ -246,7 +245,7 @@ impl PythonInstallationKey {
|
|||
major,
|
||||
minor,
|
||||
patch,
|
||||
prerelease: Cow::Owned(prerelease),
|
||||
prerelease,
|
||||
os,
|
||||
arch,
|
||||
libc,
|
||||
|
@ -265,7 +264,7 @@ impl PythonInstallationKey {
|
|||
major: version.major(),
|
||||
minor: version.minor(),
|
||||
patch: version.patch().unwrap_or_default(),
|
||||
prerelease: Cow::Owned(version.pre().map(|pre| pre.to_string()).unwrap_or_default()),
|
||||
prerelease: version.pre(),
|
||||
os,
|
||||
arch,
|
||||
libc,
|
||||
|
@ -279,7 +278,12 @@ impl PythonInstallationKey {
|
|||
pub fn version(&self) -> PythonVersion {
|
||||
PythonVersion::from_str(&format!(
|
||||
"{}.{}.{}{}",
|
||||
self.major, self.minor, self.patch, self.prerelease
|
||||
self.major,
|
||||
self.minor,
|
||||
self.patch,
|
||||
self.prerelease
|
||||
.map(|pre| pre.to_string())
|
||||
.unwrap_or_default()
|
||||
))
|
||||
.expect("Python installation keys must have valid Python versions")
|
||||
}
|
||||
|
@ -306,7 +310,9 @@ impl fmt::Display for PythonInstallationKey {
|
|||
self.major,
|
||||
self.minor,
|
||||
self.patch,
|
||||
self.prerelease,
|
||||
self.prerelease
|
||||
.map(|pre| pre.to_string())
|
||||
.unwrap_or_default(),
|
||||
self.os,
|
||||
self.arch,
|
||||
self.libc
|
||||
|
|
|
@ -157,10 +157,7 @@ impl Interpreter {
|
|||
self.python_major(),
|
||||
self.python_minor(),
|
||||
self.python_patch(),
|
||||
self.python_version()
|
||||
.pre()
|
||||
.map(|pre| pre.to_string())
|
||||
.unwrap_or_default(),
|
||||
self.python_version().pre(),
|
||||
self.os(),
|
||||
self.arch(),
|
||||
self.libc(),
|
||||
|
|
|
@ -17,6 +17,7 @@ Usage:
|
|||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
@ -29,6 +30,7 @@ WORKSPACE_ROOT = CRATE_ROOT.parent.parent
|
|||
VERSION_METADATA = CRATE_ROOT / "download-metadata.json"
|
||||
TEMPLATE = CRATE_ROOT / "src" / "downloads.inc.mustache"
|
||||
TARGET = TEMPLATE.with_suffix("")
|
||||
PRERELEASE_PATTERN = re.compile(r"(a|b|rc)(\d+)")
|
||||
|
||||
|
||||
def prepare_name(name: str) -> str:
|
||||
|
@ -61,11 +63,21 @@ def prepare_arch(arch: str) -> str:
|
|||
return arch.capitalize()
|
||||
|
||||
|
||||
def prepare_prerelease(prerelease: str) -> str:
|
||||
if not prerelease:
|
||||
return "None"
|
||||
if not (match := PRERELEASE_PATTERN.match(prerelease)):
|
||||
raise ValueError(f"Invalid prerelease: {prerelease!r}")
|
||||
kind, number = match.groups()
|
||||
return f"Some(Prerelease {{ kind: PrereleaseKind::{kind.capitalize()}, number: {number} }})"
|
||||
|
||||
|
||||
def prepare_value(value: dict) -> dict:
|
||||
value["os"] = value["os"].title()
|
||||
value["arch"] = prepare_arch(value["arch"])
|
||||
value["name"] = prepare_name(value["name"])
|
||||
value["libc"] = prepare_libc(value["libc"])
|
||||
value["prerelease"] = prepare_prerelease(value["prerelease"])
|
||||
return value
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue