mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-30 23:37:24 +00:00
Add support for managed Python 3.13 and update CPython versions (#7263)
Adds support for CPython 3.13.0rc2 Also bumps to the latest patch version of all the other CPython minor versions we support.
This commit is contained in:
parent
0dc1f5db21
commit
0e9870078e
12 changed files with 2989 additions and 70 deletions
|
@ -1409,6 +1409,12 @@ impl std::fmt::Display for PrereleaseKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Prerelease {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}{}", self.kind, self.number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A part of the [local version identifier](<https://peps.python.org/pep-0440/#local-version-identifiers>)
|
/// A part of the [local version identifier](<https://peps.python.org/pep-0440/#local-version-identifiers>)
|
||||||
///
|
///
|
||||||
/// Local versions are a mess:
|
/// Local versions are a mess:
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -82,14 +82,23 @@ class Version(NamedTuple):
|
||||||
major: int
|
major: int
|
||||||
minor: int
|
minor: int
|
||||||
patch: int
|
patch: int
|
||||||
|
prerelease: str = ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_str(cls, version: str) -> Self:
|
def from_str(cls, version: str) -> Self:
|
||||||
major, minor, patch = version.split(".", 3)
|
major, minor, patch = version.split(".", 3)
|
||||||
return cls(int(major), int(minor), int(patch))
|
prerelease = ""
|
||||||
|
for prerelease_kind in ("a", "b", "rc"):
|
||||||
|
parts = patch.split(prerelease_kind, 1)
|
||||||
|
if len(parts) == 2:
|
||||||
|
patch = parts[0]
|
||||||
|
prerelease = prerelease_kind + parts[1]
|
||||||
|
break
|
||||||
|
|
||||||
|
return cls(int(major), int(minor), int(patch), prerelease)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.major}.{self.minor}.{self.patch}"
|
return f"{self.major}.{self.minor}.{self.patch}{self.prerelease}"
|
||||||
|
|
||||||
|
|
||||||
class ImplementationName(StrEnum):
|
class ImplementationName(StrEnum):
|
||||||
|
@ -158,7 +167,7 @@ class CPythonFinder(Finder):
|
||||||
_filename_re = re.compile(
|
_filename_re = re.compile(
|
||||||
r"""(?x)
|
r"""(?x)
|
||||||
^
|
^
|
||||||
cpython-(?P<ver>\d+\.\d+\.\d+?)
|
cpython-(?P<ver>\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?)
|
||||||
(?:\+\d+)?
|
(?:\+\d+)?
|
||||||
-(?P<triple>.*?)
|
-(?P<triple>.*?)
|
||||||
(?:-[\dT]+)?\.tar\.(?:gz|zst)
|
(?:-[\dT]+)?\.tar\.(?:gz|zst)
|
||||||
|
@ -455,7 +464,9 @@ def render(downloads: list[PythonDownload]) -> None:
|
||||||
results = {}
|
results = {}
|
||||||
for download in downloads:
|
for download in downloads:
|
||||||
key = download.key()
|
key = download.key()
|
||||||
logging.info("Found %s (%s)", key, download.flavor)
|
logging.info(
|
||||||
|
"Found %s%s", key, (" (%s)" % download.flavor) if download.flavor else ""
|
||||||
|
)
|
||||||
results[key] = {
|
results[key] = {
|
||||||
"name": download.implementation,
|
"name": download.implementation,
|
||||||
"arch": download.triple.arch,
|
"arch": download.triple.arch,
|
||||||
|
@ -464,6 +475,7 @@ def render(downloads: list[PythonDownload]) -> None:
|
||||||
"major": download.version.major,
|
"major": download.version.major,
|
||||||
"minor": download.version.minor,
|
"minor": download.version.minor,
|
||||||
"patch": download.version.patch,
|
"patch": download.version.patch,
|
||||||
|
"prerelease": download.version.prerelease,
|
||||||
"url": download.url,
|
"url": download.url,
|
||||||
"sha256": download.sha256,
|
"sha256": download.sha256,
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,8 @@
|
||||||
// Generated with `{{generated_with}}`
|
// Generated with `{{generated_with}}`
|
||||||
// From template at `{{generated_from}}`
|
// From template at `{{generated_from}}`
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
|
pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
|
||||||
{{#versions}}
|
{{#versions}}
|
||||||
ManagedPythonDownload {
|
ManagedPythonDownload {
|
||||||
|
@ -10,6 +12,7 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
|
||||||
major: {{value.major}},
|
major: {{value.major}},
|
||||||
minor: {{value.minor}},
|
minor: {{value.minor}},
|
||||||
patch: {{value.patch}},
|
patch: {{value.patch}},
|
||||||
|
prerelease: Cow::Borrowed("{{value.prerelease}}"),
|
||||||
implementation: LenientImplementationName::Known(ImplementationName::{{value.name}}),
|
implementation: LenientImplementationName::Known(ImplementationName::{{value.name}}),
|
||||||
arch: Arch(target_lexicon::Architecture::{{value.arch}}),
|
arch: Arch(target_lexicon::Architecture::{{value.arch}}),
|
||||||
os: Os(target_lexicon::OperatingSystem::{{value.os}}),
|
os: Os(target_lexicon::OperatingSystem::{{value.os}}),
|
||||||
|
|
|
@ -529,9 +529,7 @@ impl ManagedPythonDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn python_version(&self) -> PythonVersion {
|
pub fn python_version(&self) -> PythonVersion {
|
||||||
self.key
|
self.key.version()
|
||||||
.version()
|
|
||||||
.expect("Managed Python downloads should always have valid versions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`Url`] to use when downloading the distribution. If a mirror is set via the
|
/// Return the [`Url`] to use when downloading the distribution. If a mirror is set via the
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -212,6 +213,7 @@ pub struct PythonInstallationKey {
|
||||||
pub(crate) major: u8,
|
pub(crate) major: u8,
|
||||||
pub(crate) minor: u8,
|
pub(crate) minor: u8,
|
||||||
pub(crate) patch: u8,
|
pub(crate) patch: u8,
|
||||||
|
pub(crate) prerelease: Cow<'static, str>,
|
||||||
pub(crate) os: Os,
|
pub(crate) os: Os,
|
||||||
pub(crate) arch: Arch,
|
pub(crate) arch: Arch,
|
||||||
pub(crate) libc: Libc,
|
pub(crate) libc: Libc,
|
||||||
|
@ -223,6 +225,7 @@ impl PythonInstallationKey {
|
||||||
major: u8,
|
major: u8,
|
||||||
minor: u8,
|
minor: u8,
|
||||||
patch: u8,
|
patch: u8,
|
||||||
|
prerelease: String,
|
||||||
os: Os,
|
os: Os,
|
||||||
arch: Arch,
|
arch: Arch,
|
||||||
libc: Libc,
|
libc: Libc,
|
||||||
|
@ -232,6 +235,26 @@ impl PythonInstallationKey {
|
||||||
major,
|
major,
|
||||||
minor,
|
minor,
|
||||||
patch,
|
patch,
|
||||||
|
prerelease: Cow::Owned(prerelease),
|
||||||
|
os,
|
||||||
|
arch,
|
||||||
|
libc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_version(
|
||||||
|
implementation: LenientImplementationName,
|
||||||
|
version: &PythonVersion,
|
||||||
|
os: Os,
|
||||||
|
arch: Arch,
|
||||||
|
libc: Libc,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
implementation,
|
||||||
|
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()),
|
||||||
os,
|
os,
|
||||||
arch,
|
arch,
|
||||||
libc,
|
libc,
|
||||||
|
@ -242,8 +265,12 @@ impl PythonInstallationKey {
|
||||||
&self.implementation
|
&self.implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&self) -> Result<PythonVersion, String> {
|
pub fn version(&self) -> PythonVersion {
|
||||||
PythonVersion::from_str(&format!("{}.{}.{}", self.major, self.minor, self.patch))
|
PythonVersion::from_str(&format!(
|
||||||
|
"{}.{}.{}{}",
|
||||||
|
self.major, self.minor, self.patch, self.prerelease
|
||||||
|
))
|
||||||
|
.expect("Python installation keys must have valid Python versions")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arch(&self) -> &Arch {
|
pub fn arch(&self) -> &Arch {
|
||||||
|
@ -263,8 +290,15 @@ impl fmt::Display for PythonInstallationKey {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}-{}.{}.{}-{}-{}-{}",
|
"{}-{}.{}.{}{}-{}-{}-{}",
|
||||||
self.implementation, self.major, self.minor, self.patch, self.os, self.arch, self.libc
|
self.implementation,
|
||||||
|
self.major,
|
||||||
|
self.minor,
|
||||||
|
self.patch,
|
||||||
|
self.prerelease,
|
||||||
|
self.os,
|
||||||
|
self.arch,
|
||||||
|
self.libc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,28 +332,16 @@ impl FromStr for PythonInstallationKey {
|
||||||
PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid libc: {err}"))
|
PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid libc: {err}"))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let [major, minor, patch] = version
|
let version = PythonVersion::from_str(version).map_err(|err| {
|
||||||
.splitn(3, '.')
|
PythonInstallationKeyError::ParseError(
|
||||||
.map(str::parse::<u8>)
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|err| {
|
|
||||||
PythonInstallationKeyError::ParseError(
|
|
||||||
key.to_string(),
|
|
||||||
format!("invalid Python version: {err}"),
|
|
||||||
)
|
|
||||||
})?[..]
|
|
||||||
else {
|
|
||||||
return Err(PythonInstallationKeyError::ParseError(
|
|
||||||
key.to_string(),
|
key.to_string(),
|
||||||
"invalid Python version: expected `<major>.<minor>.<patch>`".to_string(),
|
format!("invalid Python version: {err}"),
|
||||||
));
|
)
|
||||||
};
|
})?;
|
||||||
|
|
||||||
Ok(Self::new(
|
Ok(Self::new_from_version(
|
||||||
implementation,
|
implementation,
|
||||||
major,
|
&version,
|
||||||
minor,
|
|
||||||
patch,
|
|
||||||
os,
|
os,
|
||||||
arch,
|
arch,
|
||||||
libc,
|
libc,
|
||||||
|
@ -337,12 +359,7 @@ impl Ord for PythonInstallationKey {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.implementation
|
self.implementation
|
||||||
.cmp(&other.implementation)
|
.cmp(&other.implementation)
|
||||||
.then_with(|| {
|
.then_with(|| self.version().cmp(&other.version()))
|
||||||
self.major
|
|
||||||
.cmp(&other.major)
|
|
||||||
.then_with(|| self.minor.cmp(&other.minor))
|
|
||||||
.then_with(|| self.patch.cmp(&other.patch))
|
|
||||||
})
|
|
||||||
.then_with(|| self.os.to_string().cmp(&other.os.to_string()))
|
.then_with(|| self.os.to_string().cmp(&other.os.to_string()))
|
||||||
.then_with(|| self.arch.to_string().cmp(&other.arch.to_string()))
|
.then_with(|| self.arch.to_string().cmp(&other.arch.to_string()))
|
||||||
.then_with(|| self.libc.to_string().cmp(&other.libc.to_string()))
|
.then_with(|| self.libc.to_string().cmp(&other.libc.to_string()))
|
||||||
|
|
|
@ -155,6 +155,10 @@ impl Interpreter {
|
||||||
self.python_major(),
|
self.python_major(),
|
||||||
self.python_minor(),
|
self.python_minor(),
|
||||||
self.python_patch(),
|
self.python_patch(),
|
||||||
|
self.python_version()
|
||||||
|
.pre()
|
||||||
|
.map(|pre| pre.to_string())
|
||||||
|
.unwrap_or_default(),
|
||||||
self.os(),
|
self.os(),
|
||||||
self.arch(),
|
self.arch(),
|
||||||
self.libc(),
|
self.libc(),
|
||||||
|
|
|
@ -282,9 +282,7 @@ impl ManagedPythonInstallation {
|
||||||
|
|
||||||
/// The [`PythonVersion`] of the toolchain.
|
/// The [`PythonVersion`] of the toolchain.
|
||||||
pub fn version(&self) -> PythonVersion {
|
pub fn version(&self) -> PythonVersion {
|
||||||
self.key
|
self.key.version()
|
||||||
.version()
|
|
||||||
.expect("Managed Python installations should always have valid versions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn implementation(&self) -> &ImplementationName {
|
pub fn implementation(&self) -> &ImplementationName {
|
||||||
|
@ -331,17 +329,13 @@ impl ManagedPythonInstallation {
|
||||||
let stdlib = if matches!(self.key.os, Os(target_lexicon::OperatingSystem::Windows)) {
|
let stdlib = if matches!(self.key.os, Os(target_lexicon::OperatingSystem::Windows)) {
|
||||||
self.python_dir().join("Lib")
|
self.python_dir().join("Lib")
|
||||||
} else {
|
} else {
|
||||||
let version = self
|
|
||||||
.key
|
|
||||||
.version()
|
|
||||||
.expect("Managed Python installations should always have valid versions");
|
|
||||||
let python = if matches!(
|
let python = if matches!(
|
||||||
self.key.implementation,
|
self.key.implementation,
|
||||||
LenientImplementationName::Known(ImplementationName::PyPy)
|
LenientImplementationName::Known(ImplementationName::PyPy)
|
||||||
) {
|
) {
|
||||||
format!("pypy{}", version.python_version())
|
format!("pypy{}", self.key.version().python_version())
|
||||||
} else {
|
} else {
|
||||||
format!("python{}", version.python_version())
|
format!("python{}", self.key.version().python_version())
|
||||||
};
|
};
|
||||||
self.python_dir().join("lib").join(python)
|
self.python_dir().join("lib").join(python)
|
||||||
};
|
};
|
||||||
|
|
|
@ -178,13 +178,7 @@ pub(crate) async fn install(
|
||||||
"{}",
|
"{}",
|
||||||
format!(
|
format!(
|
||||||
"Installed {} {}",
|
"Installed {} {}",
|
||||||
format!(
|
format!("Python {}", installed.version()).bold(),
|
||||||
"Python {}",
|
|
||||||
installed.version().expect(
|
|
||||||
"Managed Python installations should always have valid versions"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.bold(),
|
|
||||||
format!("in {}", elapsed(start.elapsed())).dimmed()
|
format!("in {}", elapsed(start.elapsed())).dimmed()
|
||||||
)
|
)
|
||||||
.dimmed()
|
.dimmed()
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::fmt::Write;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use tracing::warn;
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_python::downloads::PythonDownloadRequest;
|
use uv_python::downloads::PythonDownloadRequest;
|
||||||
|
@ -107,17 +106,9 @@ pub(crate) async fn list(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = match key.version() {
|
|
||||||
Err(err) => {
|
|
||||||
warn!("Excluding {key} due to invalid Python version: {err}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Ok(version) => version,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only show the latest patch version for each download unless all were requested
|
// Only show the latest patch version for each download unless all were requested
|
||||||
if !matches!(kind, Kind::System) {
|
if !matches!(kind, Kind::System) {
|
||||||
if let [major, minor, ..] = version.release() {
|
if let [major, minor, ..] = key.version().release() {
|
||||||
if !seen_minor.insert((
|
if !seen_minor.insert((
|
||||||
*key.os(),
|
*key.os(),
|
||||||
*major,
|
*major,
|
||||||
|
@ -131,7 +122,7 @@ pub(crate) async fn list(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let [major, minor, patch] = version.release() {
|
if let [major, minor, patch] = key.version().release() {
|
||||||
if !seen_patch.insert((
|
if !seen_patch.insert((
|
||||||
*key.os(),
|
*key.os(),
|
||||||
*major,
|
*major,
|
||||||
|
|
|
@ -148,13 +148,7 @@ async fn do_uninstall(
|
||||||
"{}",
|
"{}",
|
||||||
format!(
|
format!(
|
||||||
"Uninstalled {} {}",
|
"Uninstalled {} {}",
|
||||||
format!(
|
format!("Python {}", uninstalled.version()).bold(),
|
||||||
"Python {}",
|
|
||||||
uninstalled.version().expect(
|
|
||||||
"Managed Python installations should always have valid versions"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.bold(),
|
|
||||||
format!("in {}", elapsed(start.elapsed())).dimmed()
|
format!("in {}", elapsed(start.elapsed())).dimmed()
|
||||||
)
|
)
|
||||||
.dimmed()
|
.dimmed()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue