Skip to content

Commit cb2dc54

Browse files
committed
Add an ArchMode to filter displayed architectures in uv python list
1 parent 5620348 commit cb2dc54

File tree

5 files changed

+85
-10
lines changed

5 files changed

+85
-10
lines changed

crates/uv-python/src/discovery.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,7 +2799,8 @@ mod tests {
27992799
arch: None,
28002800
os: None,
28012801
libc: None,
2802-
prereleases: None
2802+
prereleases: None,
2803+
arch_mode: None,
28032804
})
28042805
);
28052806
assert_eq!(
@@ -2818,7 +2819,8 @@ mod tests {
28182819
}),
28192820
os: Some(Os(target_lexicon::OperatingSystem::Darwin(None))),
28202821
libc: Some(Libc::None),
2821-
prereleases: None
2822+
prereleases: None,
2823+
arch_mode: None,
28222824
})
28232825
);
28242826
assert_eq!(
@@ -2834,7 +2836,8 @@ mod tests {
28342836
arch: None,
28352837
os: None,
28362838
libc: None,
2837-
prereleases: None
2839+
prereleases: None,
2840+
arch_mode: None,
28382841
})
28392842
);
28402843
assert_eq!(
@@ -2853,7 +2856,8 @@ mod tests {
28532856
}),
28542857
os: None,
28552858
libc: None,
2856-
prereleases: None
2859+
prereleases: None,
2860+
arch_mode: None,
28572861
})
28582862
);
28592863

crates/uv-python/src/downloads.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use crate::implementation::{
3636
use crate::installation::PythonInstallationKey;
3737
use crate::libc::LibcDetectionError;
3838
use crate::managed::ManagedPythonInstallation;
39-
use crate::platform::{self, Arch, Libc, Os};
39+
use crate::platform::{self, Arch, ArchMode, Libc, Os};
4040
use crate::{Interpreter, PythonRequest, PythonVersion, VersionRequest};
4141

4242
#[derive(Error, Debug)]
@@ -125,6 +125,8 @@ pub struct PythonDownloadRequest {
125125
/// Whether to allow pre-releases or not. If not set, defaults to true if [`Self::version`] is
126126
/// not None, and false otherwise.
127127
pub(crate) prereleases: Option<bool>,
128+
129+
pub(crate) arch_mode: Option<ArchMode>,
128130
}
129131

130132
impl PythonDownloadRequest {
@@ -135,6 +137,7 @@ impl PythonDownloadRequest {
135137
os: Option<Os>,
136138
libc: Option<Libc>,
137139
prereleases: Option<bool>,
140+
arch_mode: Option<ArchMode>,
138141
) -> Self {
139142
Self {
140143
version,
@@ -143,6 +146,7 @@ impl PythonDownloadRequest {
143146
os,
144147
libc,
145148
prereleases,
149+
arch_mode,
146150
}
147151
}
148152

@@ -188,6 +192,12 @@ impl PythonDownloadRequest {
188192
self
189193
}
190194

195+
#[must_use]
196+
pub fn with_arch_mode(mut self, arch_mode: ArchMode) -> Self {
197+
self.arch_mode = Some(arch_mode);
198+
self
199+
}
200+
191201
/// Construct a new [`PythonDownloadRequest`] from a [`PythonRequest`] if possible.
192202
///
193203
/// Returns [`None`] if the request kind is not compatible with a download, e.g., it is
@@ -249,6 +259,7 @@ impl PythonDownloadRequest {
249259
Some(Os::from_env()),
250260
Some(Libc::from_env()?),
251261
None,
262+
None,
252263
))
253264
}
254265

@@ -293,6 +304,11 @@ impl PythonDownloadRequest {
293304
if !arch.supports(key.arch) {
294305
return false;
295306
}
307+
if let Some(mode) = self.arch_mode {
308+
if !mode.allows(arch, &key.arch) {
309+
return false;
310+
}
311+
}
296312
}
297313

298314
if let Some(libc) = &self.libc {
@@ -414,6 +430,7 @@ impl From<&ManagedPythonInstallation> for PythonDownloadRequest {
414430
Some(key.os),
415431
Some(key.libc),
416432
Some(key.prerelease.is_some()),
433+
None,
417434
)
418435
}
419436
}
@@ -485,7 +502,15 @@ impl FromStr for PythonDownloadRequest {
485502
_ => return Err(Error::TooManyParts(s.to_string())),
486503
}
487504
}
488-
Ok(Self::new(version, implementation, arch, os, libc, None))
505+
Ok(Self::new(
506+
version,
507+
implementation,
508+
arch,
509+
os,
510+
libc,
511+
None,
512+
None,
513+
))
489514
}
490515
}
491516

crates/uv-python/src/platform.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ pub enum ArchVariant {
3131
V4,
3232
}
3333

34+
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Default)]
35+
pub enum ArchMode {
36+
/// Select the most precise architecture matching the current platform, e.g., x86-64-v4
37+
#[default]
38+
BestNative,
39+
/// Select the most compatible architecture matching the current platform, e.g., x86-64-v1
40+
CompatibleNative,
41+
/// Select an emulated architecture, e.g., x86-64 on aarch64 macOS.
42+
Emulated,
43+
}
44+
3445
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
3546
pub struct Arch {
3647
pub(crate) family: target_lexicon::Architecture,
@@ -95,6 +106,37 @@ impl Os {
95106
}
96107
}
97108

109+
impl ArchMode {
110+
pub fn allows(self, current: &Arch, other: &Arch) -> bool {
111+
match self {
112+
Self::CompatibleNative | Self::BestNative => {
113+
// The architecture is native if the family is equal
114+
if current.family != other.family {
115+
return false;
116+
}
117+
118+
// There is only a compatibility nuance for x86_64 here
119+
if current.family != target_lexicon::Architecture::X86_64 {
120+
return true;
121+
}
122+
123+
if matches!(self, Self::CompatibleNative) {
124+
// Only allow x86_64 without a variant
125+
return other.variant.is_none();
126+
} else if matches!(self, Self::BestNative) {
127+
// Only allow the variant matching the current architecture
128+
return current.variant == other.variant;
129+
}
130+
131+
// We should handle all cases above
132+
unreachable!();
133+
}
134+
// The architecture is emulated if the family differs
135+
Self::Emulated => current.family != other.family,
136+
}
137+
}
138+
}
139+
98140
impl Arch {
99141
pub fn from_env() -> Self {
100142
Self {

crates/uv/src/commands/python/install.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use uv_python::downloads::{self, DownloadResult, ManagedPythonDownload, PythonDo
1717
use uv_python::managed::{
1818
ManagedPythonInstallation, ManagedPythonInstallations, python_executable_dir,
1919
};
20-
use uv_python::platform::{Arch, Libc};
20+
use uv_python::platform::{Arch, ArchMode, Libc};
2121
use uv_python::{
2222
PythonDownloads, PythonInstallationKey, PythonRequest, PythonVersionFile,
2323
VersionFileDiscoveryOptions, VersionFilePreference,
@@ -51,8 +51,7 @@ impl InstallRequest {
5151
"`{}` is not a valid Python download request; see `uv help python` for supported formats and `uv python list --only-downloads` for available versions",
5252
request.to_canonical_string()
5353
)
54-
})?
55-
.fill()?;
54+
})?.with_arch_mode(ArchMode::BestNative).fill()?;
5655

5756
// Find a matching download
5857
let download =

crates/uv/src/commands/python/list.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::collections::BTreeSet;
33
use std::fmt::Write;
44
use uv_cli::PythonListFormat;
55
use uv_pep440::Version;
6+
use uv_python::platform::ArchMode;
67

78
use anyhow::Result;
89
use itertools::Either;
@@ -89,7 +90,11 @@ pub(crate) async fn list(
8990
} else if all_arches {
9091
base_download_request.fill_platform()?.with_any_arch()
9192
} else {
92-
base_download_request.fill_platform()?
93+
base_download_request
94+
.fill_platform()?
95+
// Only show the best, native architecture by default
96+
// TODO(zanieb): We should expose this option to the user
97+
.with_arch_mode(ArchMode::BestNative)
9398
})
9499
} else {
95100
// If fetching is not automatic, then don't show downloads as available by default

0 commit comments

Comments
 (0)