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

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:
Zanie Blue 2025-08-13 09:02:55 -05:00 committed by GitHub
parent 323aa8f332
commit 78c8c711fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 698 additions and 172 deletions

View file

@ -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;
}

View file

@ -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,

View file

@ -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"
);
}
}

View file

@ -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(),
)
}

View file

@ -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 {