disfavor aarch64 windows in its own house (#13724)

and prefer emulated x64 windows in its stead.

This is preparatory work for shipping support for uv downloading and
installing aarch64 (arm64) windows Pythons. We've [had builds for this
platform ready for a
while](https://github.com/astral-sh/python-build-standalone/pull/387),
but have held back on shipping them due to a fundamental problem:

**The Python packaging ecosystem does not have strong support for
aarch64 windows**, e.g., not many projects build aarch64 wheels yet. The
net effect of this is that, if we handed you an aarch64 python
interpreter on windows, you would have to build a lot more sdists, and
there's a high chance you will simply fail to build that sdist and be
sad.

Yes unfortunately, in this case a non-native Python interpreter simply
*works better* than the native one... in terms of working at all, today.
Of course, if the native interpreter works for your project, it should
presumably have better performance and platform compatibility.

We do not want to stand in the way of progress, as ideally this
situation is a temporary state of affairs as the ecosystem grows to
support aarch64 windows. To enable progress, on aarch64 Windows builds
of uv:

* We will still use a native python interpreter, e.g., if it's at the
front of your `PATH` or the only installed version.
* If we are choosing between equally good interpreters that differ in
architecture, x64 will be preferred.
* If the aarch64 version is newer, we will prefer the aarch64 one.
* We will emit a diagnostic on installation, and show the python request
to pass to uv to force aarch64 windows to be used.
* Will be shipping [aarch64 Windows Python
downloads](https://github.com/astral-sh/python-build-standalone/pull/387)
* Will probably add some kind of global override setting/env-var to
disable this behaviour.
* Will be shipping this behaviour in
[astral-sh/setup-uv](https://github.com/astral-sh/setup-uv)

We're coordinating with Microsoft, GitHub (for the `setup-python`
action), and the CPython team (for the `python.org` installers), to
ensure we're aligned on this default and the timing of toggling to
prefer native distributions in the future.

See discussion in 

- https://github.com/astral-sh/uv/issues/12906

---

This is an alternative to 

* #13719 

which uses sorting rather than filtering, as discussed in 

* #13721
This commit is contained in:
Aria Desires 2025-06-30 17:42:00 -04:00 committed by GitHub
parent 1c7c174bc8
commit 317ce6e245
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 98 additions and 6 deletions

View file

@ -2090,6 +2090,28 @@ jobs:
- name: "Validate global Python install"
run: py -3.13 ./scripts/check_system_python.py --uv ./uv.exe
system-test-windows-aarch64-aarch64-python-313:
timeout-minutes: 10
needs: build-binary-windows-aarch64
name: "check system | aarch64 python3.13 on windows aarch64"
runs-on: github-windows-11-aarch64-4
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.13"
architecture: "arm64"
allow-prereleases: true
- name: "Download binary"
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: uv-windows-aarch64-${{ github.sha }}
- name: "Validate global Python install"
run: py -3.13 ./scripts/check_system_python.py --uv ./uv.exe
# Test our PEP 514 integration that installs Python into the Windows registry.
system-test-windows-registry:
timeout-minutes: 10

View file

@ -43,15 +43,36 @@ impl Ord for Arch {
return self.variant.cmp(&other.variant);
}
let native = Arch::from_env();
// For the time being, manually make aarch64 windows disfavored
// on its own host platform, because most packages don't have wheels for
// aarch64 windows, making emulation more useful than native execution!
//
// The reason we do this in "sorting" and not "supports" is so that we don't
// *refuse* to use an aarch64 windows pythons if they happen to be installed
// and nothing else is available.
//
// Similarly if someone manually requests an aarch64 windows install, we
// should respect that request (this is the way users should "override"
// this behaviour).
let preferred = if cfg!(all(windows, target_arch = "aarch64")) {
Arch {
family: target_lexicon::Architecture::X86_64,
variant: None,
}
} else {
// Prefer native architectures
Arch::from_env()
};
// Prefer native architectures
match (self.family == native.family, other.family == native.family) {
match (
self.family == preferred.family,
other.family == preferred.family,
) {
(true, true) => unreachable!(),
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
(false, false) => {
// Both non-native, fallback to lexicographic order
// Both non-preferred, fallback to lexicographic order
self.family.to_string().cmp(&other.family.to_string())
}
}

View file

@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::Write;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
@ -15,7 +16,9 @@ use tracing::{debug, trace};
use uv_configuration::PreviewMode;
use uv_fs::Simplified;
use uv_python::downloads::{self, DownloadResult, ManagedPythonDownload, PythonDownloadRequest};
use uv_python::downloads::{
self, ArchRequest, DownloadResult, ManagedPythonDownload, PythonDownloadRequest,
};
use uv_python::managed::{
ManagedPythonInstallation, ManagedPythonInstallations, PythonMinorVersionLink,
create_link_to_executable, python_executable_dir,
@ -401,6 +404,7 @@ pub(crate) async fn install(
let mut errors = vec![];
let mut downloaded = Vec::with_capacity(downloads.len());
let mut requests_by_new_installation = BTreeMap::new();
while let Some((download, result)) = tasks.next().await {
match result {
Ok(download_result) => {
@ -412,10 +416,19 @@ pub(crate) async fn install(
let installation = ManagedPythonInstallation::new(path, download);
changelog.installed.insert(installation.key().clone());
for request in &requests {
// Take note of which installations satisfied which requests
if request.matches_installation(&installation) {
requests_by_new_installation
.entry(installation.key().clone())
.or_insert(Vec::new())
.push(request);
}
}
if changelog.existing.contains(installation.key()) {
changelog.uninstalled.insert(installation.key().clone());
}
downloaded.push(installation);
downloaded.push(installation.clone());
}
Err(err) => {
errors.push((download.key().clone(), anyhow::Error::new(err)));
@ -529,6 +542,42 @@ pub(crate) async fn install(
}
if !changelog.installed.is_empty() {
for install_key in &changelog.installed {
// Make a note if the selected python is non-native for the architecture,
// if none of the matching user requests were explicit
let native_arch = Arch::from_env();
if install_key.arch().family() != native_arch.family() {
let not_explicit =
requests_by_new_installation
.get(install_key)
.and_then(|requests| {
let all_non_explicit = requests.iter().all(|request| {
if let PythonRequest::Key(key) = &request.request {
!matches!(key.arch(), Some(ArchRequest::Explicit(_)))
} else {
true
}
});
if all_non_explicit {
requests.iter().next()
} else {
None
}
});
if let Some(not_explicit) = not_explicit {
let native_request =
not_explicit.download_request.clone().with_arch(native_arch);
writeln!(
printer.stderr(),
"{} uv selected a Python distribution with an emulated architecture ({}) for your platform because support for the native architecture ({}) is not yet mature; to override this behaviour, request the native architecture explicitly with: {}",
"note:".bold(),
install_key.arch(),
native_arch,
native_request
)?;
}
}
}
if changelog.installed.len() == 1 {
let installed = changelog.installed.iter().next().unwrap();
// Ex) "Installed Python 3.9.7 in 1.68s"