mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-30 23:37:24 +00:00
Refactor os, arch, and libc information into a shared Platform
type (#15027)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
Addresses this outstanding item from a previous review https://github.com/astral-sh/uv/pull/13724#discussion_r2114867288 I'm interested in this now for consolidating some logic in #12731
This commit is contained in:
parent
323aa8f332
commit
78c8c711fa
8 changed files with 698 additions and 172 deletions
|
@ -350,7 +350,7 @@ fn python_executables_from_installed<'a>(
|
|||
debug!("Skipping managed installation `{installation}`: does not satisfy `{version}`");
|
||||
return false;
|
||||
}
|
||||
if !platform.matches(installation.key()) {
|
||||
if !platform.matches(installation.platform()) {
|
||||
debug!("Skipping managed installation `{installation}`: does not satisfy `{platform}`");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use uv_client::{BaseClient, WrappedReqwestError, is_extended_transient_error};
|
|||
use uv_distribution_filename::{ExtensionError, SourceDistExtension};
|
||||
use uv_extract::hash::Hasher;
|
||||
use uv_fs::{Simplified, rename_with_retry};
|
||||
use uv_platform::{self as platform, Arch, Libc, Os};
|
||||
use uv_platform::{self as platform, Arch, Libc, Os, Platform};
|
||||
use uv_pypi_types::{HashAlgorithm, HashDigest};
|
||||
use uv_redacted::DisplaySafeUrl;
|
||||
use uv_static::EnvVars;
|
||||
|
@ -171,22 +171,22 @@ pub struct PlatformRequest {
|
|||
}
|
||||
|
||||
impl PlatformRequest {
|
||||
/// Check if this platform request is satisfied by an installation key.
|
||||
pub fn matches(&self, key: &PythonInstallationKey) -> bool {
|
||||
/// Check if this platform request is satisfied by a platform.
|
||||
pub fn matches(&self, platform: &Platform) -> bool {
|
||||
if let Some(os) = self.os {
|
||||
if key.os != os {
|
||||
if platform.os != os {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(arch) = self.arch {
|
||||
if !arch.satisfied_by(key.arch) {
|
||||
if !arch.satisfied_by(platform) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(libc) = self.libc {
|
||||
if key.libc != libc {
|
||||
if platform.libc != libc {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -220,10 +220,14 @@ impl Display for ArchRequest {
|
|||
}
|
||||
|
||||
impl ArchRequest {
|
||||
pub(crate) fn satisfied_by(self, arch: Arch) -> bool {
|
||||
pub(crate) fn satisfied_by(self, platform: &Platform) -> bool {
|
||||
match self {
|
||||
Self::Explicit(request) => request == arch,
|
||||
Self::Environment(env) => env.supports(arch),
|
||||
Self::Explicit(request) => request == platform.arch,
|
||||
Self::Environment(env) => {
|
||||
// Check if the environment's platform can run the target platform
|
||||
let env_platform = Platform::new(platform.os, env, platform.libc);
|
||||
env_platform.supports(platform)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,14 +331,15 @@ impl PythonDownloadRequest {
|
|||
///
|
||||
/// Platform information is pulled from the environment.
|
||||
pub fn fill_platform(mut self) -> Result<Self, Error> {
|
||||
let platform = Platform::from_env()?;
|
||||
if self.arch.is_none() {
|
||||
self.arch = Some(ArchRequest::Environment(Arch::from_env()));
|
||||
self.arch = Some(ArchRequest::Environment(platform.arch));
|
||||
}
|
||||
if self.os.is_none() {
|
||||
self.os = Some(Os::from_env());
|
||||
self.os = Some(platform.os);
|
||||
}
|
||||
if self.libc.is_none() {
|
||||
self.libc = Some(Libc::from_env()?);
|
||||
self.libc = Some(platform.libc);
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -378,23 +383,16 @@ impl PythonDownloadRequest {
|
|||
|
||||
/// Whether this request is satisfied by an installation key.
|
||||
pub fn satisfied_by_key(&self, key: &PythonInstallationKey) -> bool {
|
||||
if let Some(os) = &self.os {
|
||||
if key.os != *os {
|
||||
return false;
|
||||
}
|
||||
// Check platform requirements
|
||||
let request = PlatformRequest {
|
||||
os: self.os,
|
||||
arch: self.arch,
|
||||
libc: self.libc,
|
||||
};
|
||||
if !request.matches(key.platform()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(arch) = &self.arch {
|
||||
if !arch.satisfied_by(key.arch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(libc) = &self.libc {
|
||||
if key.libc != *libc {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(implementation) = &self.implementation {
|
||||
if key.implementation != LenientImplementationName::from(*implementation) {
|
||||
return false;
|
||||
|
@ -453,19 +451,20 @@ impl PythonDownloadRequest {
|
|||
}
|
||||
}
|
||||
if let Some(os) = self.os() {
|
||||
let interpreter_os = Os::from(interpreter.platform().os());
|
||||
if &interpreter_os != os {
|
||||
if &interpreter.os() != os {
|
||||
debug!(
|
||||
"Skipping interpreter at `{executable}`: operating system `{interpreter_os}` does not match request `{os}`"
|
||||
"Skipping interpreter at `{executable}`: operating system `{}` does not match request `{os}`",
|
||||
interpreter.os()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(arch) = self.arch() {
|
||||
let interpreter_arch = Arch::from(&interpreter.platform().arch());
|
||||
if !arch.satisfied_by(interpreter_arch) {
|
||||
let interpreter_platform = Platform::from(interpreter.platform());
|
||||
if !arch.satisfied_by(&interpreter_platform) {
|
||||
debug!(
|
||||
"Skipping interpreter at `{executable}`: architecture `{interpreter_arch}` does not match request `{arch}`"
|
||||
"Skipping interpreter at `{executable}`: architecture `{}` does not match request `{arch}`",
|
||||
interpreter.arch()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
@ -482,10 +481,10 @@ impl PythonDownloadRequest {
|
|||
}
|
||||
}
|
||||
if let Some(libc) = self.libc() {
|
||||
let interpreter_libc = Libc::from(interpreter.platform().os());
|
||||
if &interpreter_libc != libc {
|
||||
if &interpreter.libc() != libc {
|
||||
debug!(
|
||||
"Skipping interpreter at `{executable}`: libc `{interpreter_libc}` does not match request `{libc}`"
|
||||
"Skipping interpreter at `{executable}`: libc `{}` does not match request `{libc}`",
|
||||
interpreter.libc()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
@ -514,9 +513,9 @@ impl From<&ManagedPythonInstallation> for PythonDownloadRequest {
|
|||
"Managed Python installations are expected to always have known implementation names, found {name}"
|
||||
),
|
||||
},
|
||||
Some(ArchRequest::Explicit(key.arch)),
|
||||
Some(key.os),
|
||||
Some(key.libc),
|
||||
Some(ArchRequest::Explicit(*key.arch())),
|
||||
Some(*key.os()),
|
||||
Some(*key.libc()),
|
||||
Some(key.prerelease.is_some()),
|
||||
)
|
||||
}
|
||||
|
@ -1184,9 +1183,7 @@ fn parse_json_downloads(
|
|||
key: PythonInstallationKey::new_from_version(
|
||||
implementation,
|
||||
&version,
|
||||
os,
|
||||
arch,
|
||||
libc,
|
||||
Platform::new(os, arch, libc),
|
||||
variant,
|
||||
),
|
||||
url,
|
||||
|
|
|
@ -10,7 +10,7 @@ use uv_cache::Cache;
|
|||
use uv_client::BaseClientBuilder;
|
||||
use uv_configuration::Preview;
|
||||
use uv_pep440::{Prerelease, Version};
|
||||
use uv_platform::{Arch, Libc, Os};
|
||||
use uv_platform::{Arch, Libc, Os, Platform};
|
||||
|
||||
use crate::discovery::{
|
||||
EnvironmentPreference, PythonRequest, find_best_python_installation, find_python_installation,
|
||||
|
@ -352,9 +352,7 @@ pub struct PythonInstallationKey {
|
|||
pub(crate) minor: u8,
|
||||
pub(crate) patch: u8,
|
||||
pub(crate) prerelease: Option<Prerelease>,
|
||||
pub(crate) os: Os,
|
||||
pub(crate) arch: Arch,
|
||||
pub(crate) libc: Libc,
|
||||
pub(crate) platform: Platform,
|
||||
pub(crate) variant: PythonVariant,
|
||||
}
|
||||
|
||||
|
@ -365,9 +363,7 @@ impl PythonInstallationKey {
|
|||
minor: u8,
|
||||
patch: u8,
|
||||
prerelease: Option<Prerelease>,
|
||||
os: Os,
|
||||
arch: Arch,
|
||||
libc: Libc,
|
||||
platform: Platform,
|
||||
variant: PythonVariant,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -376,9 +372,7 @@ impl PythonInstallationKey {
|
|||
minor,
|
||||
patch,
|
||||
prerelease,
|
||||
os,
|
||||
arch,
|
||||
libc,
|
||||
platform,
|
||||
variant,
|
||||
}
|
||||
}
|
||||
|
@ -386,9 +380,7 @@ impl PythonInstallationKey {
|
|||
pub fn new_from_version(
|
||||
implementation: LenientImplementationName,
|
||||
version: &PythonVersion,
|
||||
os: Os,
|
||||
arch: Arch,
|
||||
libc: Libc,
|
||||
platform: Platform,
|
||||
variant: PythonVariant,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -397,9 +389,7 @@ impl PythonInstallationKey {
|
|||
minor: version.minor(),
|
||||
patch: version.patch().unwrap_or_default(),
|
||||
prerelease: version.pre(),
|
||||
os,
|
||||
arch,
|
||||
libc,
|
||||
platform,
|
||||
variant,
|
||||
}
|
||||
}
|
||||
|
@ -434,16 +424,20 @@ impl PythonInstallationKey {
|
|||
self.minor
|
||||
}
|
||||
|
||||
pub fn platform(&self) -> &Platform {
|
||||
&self.platform
|
||||
}
|
||||
|
||||
pub fn arch(&self) -> &Arch {
|
||||
&self.arch
|
||||
&self.platform.arch
|
||||
}
|
||||
|
||||
pub fn os(&self) -> &Os {
|
||||
&self.os
|
||||
&self.platform.os
|
||||
}
|
||||
|
||||
pub fn libc(&self) -> &Libc {
|
||||
&self.libc
|
||||
&self.platform.libc
|
||||
}
|
||||
|
||||
pub fn variant(&self) -> &PythonVariant {
|
||||
|
@ -489,7 +483,7 @@ impl fmt::Display for PythonInstallationKey {
|
|||
};
|
||||
write!(
|
||||
f,
|
||||
"{}-{}.{}.{}{}{}-{}-{}-{}",
|
||||
"{}-{}.{}.{}{}{}-{}",
|
||||
self.implementation,
|
||||
self.major,
|
||||
self.minor,
|
||||
|
@ -498,9 +492,7 @@ impl fmt::Display for PythonInstallationKey {
|
|||
.map(|pre| pre.to_string())
|
||||
.unwrap_or_default(),
|
||||
variant,
|
||||
self.os,
|
||||
self.arch,
|
||||
self.libc
|
||||
self.platform
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -510,31 +502,25 @@ impl FromStr for PythonInstallationKey {
|
|||
|
||||
fn from_str(key: &str) -> Result<Self, Self::Err> {
|
||||
let parts = key.split('-').collect::<Vec<_>>();
|
||||
let [implementation, version, os, arch, libc] = parts.as_slice() else {
|
||||
|
||||
// We need exactly implementation-version-os-arch-libc
|
||||
if parts.len() != 5 {
|
||||
return Err(PythonInstallationKeyError::ParseError(
|
||||
key.to_string(),
|
||||
"not enough `-`-separated values".to_string(),
|
||||
format!(
|
||||
"expected exactly 5 `-`-separated values, got {}",
|
||||
parts.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
let [implementation_str, version_str, os, arch, libc] = parts.as_slice() else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let implementation = LenientImplementationName::from(*implementation);
|
||||
let implementation = LenientImplementationName::from(*implementation_str);
|
||||
|
||||
let os = Os::from_str(os).map_err(|err| {
|
||||
PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid OS: {err}"))
|
||||
})?;
|
||||
|
||||
let arch = Arch::from_str(arch).map_err(|err| {
|
||||
PythonInstallationKeyError::ParseError(
|
||||
key.to_string(),
|
||||
format!("invalid architecture: {err}"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let libc = Libc::from_str(libc).map_err(|err| {
|
||||
PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid libc: {err}"))
|
||||
})?;
|
||||
|
||||
let (version, variant) = match version.split_once('+') {
|
||||
let (version, variant) = match version_str.split_once('+') {
|
||||
Some((version, variant)) => {
|
||||
let variant = PythonVariant::from_str(variant).map_err(|()| {
|
||||
PythonInstallationKeyError::ParseError(
|
||||
|
@ -544,7 +530,7 @@ impl FromStr for PythonInstallationKey {
|
|||
})?;
|
||||
(version, variant)
|
||||
}
|
||||
None => (*version, PythonVariant::Default),
|
||||
None => (*version_str, PythonVariant::Default),
|
||||
};
|
||||
|
||||
let version = PythonVersion::from_str(version).map_err(|err| {
|
||||
|
@ -554,14 +540,22 @@ impl FromStr for PythonInstallationKey {
|
|||
)
|
||||
})?;
|
||||
|
||||
Ok(Self::new_from_version(
|
||||
let platform = Platform::from_parts(os, arch, libc).map_err(|err| {
|
||||
PythonInstallationKeyError::ParseError(
|
||||
key.to_string(),
|
||||
format!("invalid platform: {err}"),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
implementation,
|
||||
&version,
|
||||
os,
|
||||
arch,
|
||||
libc,
|
||||
major: version.major(),
|
||||
minor: version.minor(),
|
||||
patch: version.patch().unwrap_or_default(),
|
||||
prerelease: version.pre(),
|
||||
platform,
|
||||
variant,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,10 +570,8 @@ impl Ord for PythonInstallationKey {
|
|||
self.implementation
|
||||
.cmp(&other.implementation)
|
||||
.then_with(|| self.version().cmp(&other.version()))
|
||||
.then_with(|| self.os.to_string().cmp(&other.os.to_string()))
|
||||
// Architectures are sorted in preferred order, with native architectures first
|
||||
.then_with(|| self.arch.cmp(&other.arch).reverse())
|
||||
.then_with(|| self.libc.to_string().cmp(&other.libc.to_string()))
|
||||
// Platforms are sorted in preferred order for the target
|
||||
.then_with(|| self.platform.cmp(&other.platform).reverse())
|
||||
// Python variants are sorted in preferred order, with `Default` first
|
||||
.then_with(|| self.variant.cmp(&other.variant).reverse())
|
||||
}
|
||||
|
@ -632,14 +624,8 @@ impl fmt::Display for PythonInstallationMinorVersionKey {
|
|||
};
|
||||
write!(
|
||||
f,
|
||||
"{}-{}.{}{}-{}-{}-{}",
|
||||
self.0.implementation,
|
||||
self.0.major,
|
||||
self.0.minor,
|
||||
variant,
|
||||
self.0.os,
|
||||
self.0.arch,
|
||||
self.0.libc,
|
||||
"{}-{}.{}{}-{}",
|
||||
self.0.implementation, self.0.major, self.0.minor, variant, self.0.platform,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -653,9 +639,9 @@ impl fmt::Debug for PythonInstallationMinorVersionKey {
|
|||
.field("major", &self.0.major)
|
||||
.field("minor", &self.0.minor)
|
||||
.field("variant", &self.0.variant)
|
||||
.field("os", &self.0.os)
|
||||
.field("arch", &self.0.arch)
|
||||
.field("libc", &self.0.libc)
|
||||
.field("os", &self.0.platform.os)
|
||||
.field("arch", &self.0.platform.arch)
|
||||
.field("libc", &self.0.platform.libc)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -667,9 +653,7 @@ impl PartialEq for PythonInstallationMinorVersionKey {
|
|||
self.0.implementation == other.0.implementation
|
||||
&& self.0.major == other.0.major
|
||||
&& self.0.minor == other.0.minor
|
||||
&& self.0.os == other.0.os
|
||||
&& self.0.arch == other.0.arch
|
||||
&& self.0.libc == other.0.libc
|
||||
&& self.0.platform == other.0.platform
|
||||
&& self.0.variant == other.0.variant
|
||||
}
|
||||
}
|
||||
|
@ -681,9 +665,7 @@ impl Hash for PythonInstallationMinorVersionKey {
|
|||
self.0.implementation.hash(state);
|
||||
self.0.major.hash(state);
|
||||
self.0.minor.hash(state);
|
||||
self.0.os.hash(state);
|
||||
self.0.arch.hash(state);
|
||||
self.0.libc.hash(state);
|
||||
self.0.platform.hash(state);
|
||||
self.0.variant.hash(state);
|
||||
}
|
||||
}
|
||||
|
@ -693,3 +675,113 @@ impl From<PythonInstallationKey> for PythonInstallationMinorVersionKey {
|
|||
Self(key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use uv_platform::ArchVariant;
|
||||
|
||||
#[test]
|
||||
fn test_python_installation_key_from_str() {
|
||||
// Test basic parsing
|
||||
let key = PythonInstallationKey::from_str("cpython-3.12.0-linux-x86_64-gnu").unwrap();
|
||||
assert_eq!(
|
||||
key.implementation,
|
||||
LenientImplementationName::Known(ImplementationName::CPython)
|
||||
);
|
||||
assert_eq!(key.major, 3);
|
||||
assert_eq!(key.minor, 12);
|
||||
assert_eq!(key.patch, 0);
|
||||
assert_eq!(
|
||||
key.platform.os,
|
||||
Os::new(target_lexicon::OperatingSystem::Linux)
|
||||
);
|
||||
assert_eq!(
|
||||
key.platform.arch,
|
||||
Arch::new(target_lexicon::Architecture::X86_64, None)
|
||||
);
|
||||
assert_eq!(
|
||||
key.platform.libc,
|
||||
Libc::Some(target_lexicon::Environment::Gnu)
|
||||
);
|
||||
|
||||
// Test with architecture variant
|
||||
let key = PythonInstallationKey::from_str("cpython-3.11.2-linux-x86_64_v3-musl").unwrap();
|
||||
assert_eq!(
|
||||
key.implementation,
|
||||
LenientImplementationName::Known(ImplementationName::CPython)
|
||||
);
|
||||
assert_eq!(key.major, 3);
|
||||
assert_eq!(key.minor, 11);
|
||||
assert_eq!(key.patch, 2);
|
||||
assert_eq!(
|
||||
key.platform.os,
|
||||
Os::new(target_lexicon::OperatingSystem::Linux)
|
||||
);
|
||||
assert_eq!(
|
||||
key.platform.arch,
|
||||
Arch::new(target_lexicon::Architecture::X86_64, Some(ArchVariant::V3))
|
||||
);
|
||||
assert_eq!(
|
||||
key.platform.libc,
|
||||
Libc::Some(target_lexicon::Environment::Musl)
|
||||
);
|
||||
|
||||
// Test with Python variant (freethreaded)
|
||||
let key = PythonInstallationKey::from_str("cpython-3.13.0+freethreaded-macos-aarch64-none")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
key.implementation,
|
||||
LenientImplementationName::Known(ImplementationName::CPython)
|
||||
);
|
||||
assert_eq!(key.major, 3);
|
||||
assert_eq!(key.minor, 13);
|
||||
assert_eq!(key.patch, 0);
|
||||
assert_eq!(key.variant, PythonVariant::Freethreaded);
|
||||
assert_eq!(
|
||||
key.platform.os,
|
||||
Os::new(target_lexicon::OperatingSystem::Darwin(None))
|
||||
);
|
||||
assert_eq!(
|
||||
key.platform.arch,
|
||||
Arch::new(
|
||||
target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
|
||||
None
|
||||
)
|
||||
);
|
||||
assert_eq!(key.platform.libc, Libc::None);
|
||||
|
||||
// Test error cases
|
||||
assert!(PythonInstallationKey::from_str("cpython-3.12.0-linux-x86_64").is_err());
|
||||
assert!(PythonInstallationKey::from_str("cpython-3.12.0").is_err());
|
||||
assert!(PythonInstallationKey::from_str("cpython").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_python_installation_key_display() {
|
||||
let key = PythonInstallationKey {
|
||||
implementation: LenientImplementationName::from("cpython"),
|
||||
major: 3,
|
||||
minor: 12,
|
||||
patch: 0,
|
||||
prerelease: None,
|
||||
platform: Platform::from_str("linux-x86_64-gnu").unwrap(),
|
||||
variant: PythonVariant::Default,
|
||||
};
|
||||
assert_eq!(key.to_string(), "cpython-3.12.0-linux-x86_64-gnu");
|
||||
|
||||
let key_with_variant = PythonInstallationKey {
|
||||
implementation: LenientImplementationName::from("cpython"),
|
||||
major: 3,
|
||||
minor: 13,
|
||||
patch: 0,
|
||||
prerelease: None,
|
||||
platform: Platform::from_str("macos-aarch64-none").unwrap(),
|
||||
variant: PythonVariant::Freethreaded,
|
||||
};
|
||||
assert_eq!(
|
||||
key_with_variant.to_string(),
|
||||
"cpython-3.13.0+freethreaded-macos-aarch64-none"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@ use uv_install_wheel::Layout;
|
|||
use uv_pep440::Version;
|
||||
use uv_pep508::{MarkerEnvironment, StringVersion};
|
||||
use uv_platform::{Arch, Libc, Os};
|
||||
use uv_platform_tags::Platform;
|
||||
use uv_platform_tags::{Tags, TagsError};
|
||||
use uv_platform_tags::{Platform, Tags, TagsError};
|
||||
use uv_pypi_types::{ResolverMarkerEnvironment, Scheme};
|
||||
|
||||
use crate::implementation::LenientImplementationName;
|
||||
|
@ -205,9 +204,7 @@ impl Interpreter {
|
|||
self.python_minor(),
|
||||
self.python_patch(),
|
||||
self.python_version().pre(),
|
||||
self.os(),
|
||||
self.arch(),
|
||||
self.libc(),
|
||||
uv_platform::Platform::new(self.os(), self.arch(), self.libc()),
|
||||
self.variant(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
|||
|
||||
use uv_fs::{LockedFile, Simplified, replace_symlink, symlink_or_copy_file};
|
||||
use uv_platform::Error as PlatformError;
|
||||
use uv_platform::{Arch, Libc, LibcDetectionError, Os};
|
||||
use uv_platform::{LibcDetectionError, Platform};
|
||||
use uv_state::{StateBucket, StateStore};
|
||||
use uv_static::EnvVars;
|
||||
use uv_trampoline_builder::{Launcher, windows_python_launcher};
|
||||
|
@ -259,20 +259,11 @@ impl ManagedPythonInstallations {
|
|||
pub fn find_matching_current_platform(
|
||||
&self,
|
||||
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation> + use<>, Error> {
|
||||
let os = Os::from_env();
|
||||
let arch = Arch::from_env();
|
||||
let libc = Libc::from_env()?;
|
||||
let platform = Platform::from_env()?;
|
||||
|
||||
let iter = Self::from_settings(None)?
|
||||
.find_all()?
|
||||
.filter(move |installation| {
|
||||
installation.key.os == os
|
||||
&& (arch.supports(installation.key.arch)
|
||||
// TODO(zanieb): Allow inequal variants, as `Arch::supports` does not
|
||||
// implement this yet. See https://github.com/astral-sh/uv/pull/9788
|
||||
|| arch.family() == installation.key.arch.family())
|
||||
&& installation.key.libc == libc
|
||||
});
|
||||
.filter(move |installation| platform.supports(installation.platform()));
|
||||
|
||||
Ok(iter)
|
||||
}
|
||||
|
@ -451,6 +442,10 @@ impl ManagedPythonInstallation {
|
|||
&self.key
|
||||
}
|
||||
|
||||
pub fn platform(&self) -> &Platform {
|
||||
self.key.platform()
|
||||
}
|
||||
|
||||
pub fn minor_version_key(&self) -> &PythonInstallationMinorVersionKey {
|
||||
PythonInstallationMinorVersionKey::ref_cast(&self.key)
|
||||
}
|
||||
|
@ -544,7 +539,7 @@ impl ManagedPythonInstallation {
|
|||
/// standard `EXTERNALLY-MANAGED` file.
|
||||
pub fn ensure_externally_managed(&self) -> Result<(), Error> {
|
||||
// Construct the path to the `stdlib` directory.
|
||||
let stdlib = if self.key.os.is_windows() {
|
||||
let stdlib = if self.key.os().is_windows() {
|
||||
self.python_dir().join("Lib")
|
||||
} else {
|
||||
let lib_suffix = self.key.variant.suffix();
|
||||
|
@ -588,7 +583,7 @@ impl ManagedPythonInstallation {
|
|||
/// See <https://github.com/astral-sh/uv/issues/10598> for more information.
|
||||
pub fn ensure_dylib_patched(&self) -> Result<(), macos_dylib::Error> {
|
||||
if cfg!(target_os = "macos") {
|
||||
if self.key().os.is_like_darwin() {
|
||||
if self.key().os().is_like_darwin() {
|
||||
if *self.implementation() == ImplementationName::CPython {
|
||||
let dylib_path = self.python_dir().join("lib").join(format!(
|
||||
"{}python{}{}{}",
|
||||
|
@ -890,10 +885,7 @@ pub fn create_link_to_executable(link: &Path, executable: &Path) -> Result<(), E
|
|||
// TODO(zanieb): Only used in tests now.
|
||||
/// Generate a platform portion of a key from the environment.
|
||||
pub fn platform_key_from_env() -> Result<String, Error> {
|
||||
let os = Os::from_env();
|
||||
let arch = Arch::from_env();
|
||||
let libc = Libc::from_env()?;
|
||||
Ok(format!("{os}-{arch}-{libc}").to_lowercase())
|
||||
Ok(Platform::from_env()?.to_string().to_lowercase())
|
||||
}
|
||||
|
||||
impl fmt::Display for ManagedPythonInstallation {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue