@@ -109,6 +109,42 @@ function Resolve-LocalPackagePath {
109109 return (Resolve-Path - LiteralPath $candidate ).Path
110110}
111111
112+ function Get-HostArchitecture {
113+ try {
114+ return [System.Runtime.InteropServices.RuntimeInformation ]::OSArchitecture.ToString()
115+ }
116+ catch {
117+ return $env: PROCESSOR_ARCHITECTURE
118+ }
119+ }
120+
121+ function Test-IsWindowsArm64Host {
122+ $isWindowsHost = $false
123+ try { $isWindowsHost = $IsWindows } catch { }
124+ if (-not $isWindowsHost -and $env: OS -eq " Windows_NT" ) { $isWindowsHost = $true }
125+ if (-not $isWindowsHost ) { return $false }
126+
127+ $arch = Get-HostArchitecture
128+ return $arch -match " ^(Arm64|AArch64)$"
129+ }
130+
131+ function Get-PythonMachine ([string ]$FilePath , [string []]$Arguments ) {
132+ try {
133+ $output = & $FilePath @Arguments - c " import platform; print(platform.machine())" 2>&1
134+ if ($LASTEXITCODE -ne 0 ) { return $null }
135+ return ($output | Out-String ).Trim()
136+ }
137+ catch {
138+ return $null
139+ }
140+ }
141+
142+ function Test-IsUnsupportedWindowsArm64Python ([string ]$FilePath , [string []]$Arguments ) {
143+ if (-not (Test-IsWindowsArm64Host )) { return $false }
144+ $machine = Get-PythonMachine $FilePath $Arguments
145+ return $machine -match " ^(ARM64|AARCH64)$"
146+ }
147+
112148function New-ManagedVenv ($PythonInfo ) {
113149 if ($Force -and (Test-Path - LiteralPath $VenvPath )) {
114150 Invoke-Step " Removing existing managed venv: $VenvPath " {
@@ -126,6 +162,19 @@ function New-ManagedVenv($PythonInfo) {
126162 }
127163
128164 $venvPython = Join-Path $VenvPath " Scripts\python.exe"
165+ if ((Test-Path - LiteralPath $venvPython ) -and (Test-IsUnsupportedWindowsArm64Python $venvPython @ ())) {
166+ Invoke-Step " Removing native ARM64 managed venv: $VenvPath " {
167+ Remove-Item - LiteralPath $VenvPath - Recurse - Force
168+ }
169+ Invoke-Step " Creating managed X-Tester venv: $VenvPath " {
170+ $venvArgs = @ ()
171+ if ($PythonInfo.Arguments ) { $venvArgs += $PythonInfo.Arguments }
172+ $venvArgs += @ (" -m" , " venv" , $VenvPath )
173+ Invoke-External $PythonInfo.FilePath $venvArgs
174+ }
175+ $venvPython = Join-Path $VenvPath " Scripts\python.exe"
176+ }
177+
129178 if (-not (Test-Path - LiteralPath $venvPython )) {
130179 # Microsoft Store / UWP Python redirects writes under %LOCALAPPDATA% into
131180 # %LOCALAPPDATA%\Packages\PythonSoftwareFoundation.Python.*\LocalCache\Local\<rest>.
@@ -164,7 +213,7 @@ function Install-XTesterIntoVenv([string]$VenvPython) {
164213 Invoke-WithCleanPythonPath {
165214 Invoke-Step " Upgrading pip and installing private-feed auth helpers" {
166215 Invoke-External $VenvPython @ (" -m" , " pip" , " install" , " -U" , " pip" )
167- Invoke-External $VenvPython @ (" -m" , " pip" , " install" , " -U" , " keyring" , " artifacts-keyring" )
216+ Invoke-External $VenvPython @ (" -m" , " pip" , " install" , " -U" , " --prefer-binary " , " keyring" , " artifacts-keyring" )
168217 }
169218
170219 if ($Source -eq " local" ) {
@@ -184,7 +233,7 @@ function Install-XTesterIntoVenv([string]$VenvPython) {
184233
185234 Invoke-Step " Installing $installLabel " {
186235 Invoke-External $VenvPython @ (
187- " -m" , " pip" , " install" , " -U" ,
236+ " -m" , " pip" , " install" , " -U" , " --prefer-binary " ,
188237 " --index-url" , $FeedUrl ,
189238 " --extra-index-url" , $ExtraIndexUrl ,
190239 $packageSpec
@@ -334,6 +383,10 @@ function Test-PythonExecutable([string]$FilePath, [string[]]$Arguments) {
334383 $minor = [int ]$Matches [2 ]
335384 if ($major -lt 3 -or ($major -eq 3 -and $minor -lt 11 )) { return $null }
336385
386+ if (Test-IsUnsupportedWindowsArm64Python $FilePath $Arguments ) {
387+ return $null
388+ }
389+
337390 # Reject Microsoft Store Python distributions. They virtualize writes under
338391 # %LOCALAPPDATA% into %LOCALAPPDATA%\Packages\PythonSoftwareFoundation.Python.*\LocalCache\Local\,
339392 # which breaks `python -m venv` against our managed venv path.
@@ -373,7 +426,7 @@ function Find-PythonCandidate {
373426
374427 $py = Get-Command " py" - ErrorAction SilentlyContinue
375428 if ($py ) {
376- foreach ($flag in @ (" -3.13 " , " -3.12 " , " -3.11 " , " -3" )) {
429+ foreach ($flag in @ (" -3.12 " , " -3.11 " , " -3.13 " , " -3" )) {
377430 $candidates += , @ ($py.Source , @ ($flag ))
378431 }
379432 }
@@ -409,9 +462,15 @@ function Install-PythonViaWinget {
409462 # --disable-interactivity suppresses winget's animated progress bar so the
410463 # console is not flooded with redraw lines (which can also render as
411464 # mojibake on legacy code pages).
412- & $winget.Source install - e -- id Python.Python.3.12 -- scope user `
413- -- accept- source- agreements -- accept- package- agreements -- silent `
414- -- disable-interactivity 2>&1 | ForEach-Object {
465+ $wingetArgs = @ (" install" , " -e" , " --id" , " Python.Python.3.12" , " --scope" , " user" )
466+ if (Test-IsWindowsArm64Host ) {
467+ $wingetArgs += @ (" --architecture" , " x64" )
468+ }
469+ $wingetArgs += @ (
470+ " --accept-source-agreements" , " --accept-package-agreements" , " --silent" ,
471+ " --disable-interactivity"
472+ )
473+ & $winget.Source @wingetArgs 2>&1 | ForEach-Object {
415474 $line = [string ]$_
416475 # Drop empty/whitespace lines and progress redraw lines (block glyphs,
417476 # carriage-return progress percentages, and MB/KB transfer counters).
@@ -447,16 +506,25 @@ function Resolve-Python {
447506 }
448507
449508 Fail " No working Python 3.11+ interpreter was found."
450- Info " Tried: python, python3, py -3.13 , py -3.12 , py -3.11 , py -3 plus %LOCALAPPDATA%\Programs\Python\Python3*."
509+ Info " Tried: python, python3, py -3.12 , py -3.11 , py -3.13 , py -3 plus %LOCALAPPDATA%\Programs\Python\Python3*."
451510 Info " The Microsoft Store stub at WindowsApps\python.exe is intentionally ignored."
511+ if (Test-IsWindowsArm64Host ) {
512+ Info " Native ARM64 Python is intentionally ignored because required auth-helper wheels can fall back to cryptography/OpenSSL source builds."
513+ Info " Use x64 Python 3.12 on Windows ARM64 for this installer."
514+ }
452515 Info " Install Python manually with one of:"
453- Info " winget install -e --id Python.Python.3.12"
516+ if (Test-IsWindowsArm64Host ) {
517+ Info " winget install -e --id Python.Python.3.12 --architecture x64"
518+ } else {
519+ Info " winget install -e --id Python.Python.3.12"
520+ }
454521 Info " choco install python --version=3.12"
455522 Info " https://www.python.org/downloads/"
456523 Info " Then open a new terminal and re-run this installer."
457- exit 1
524+ throw " No working Python 3.11+ interpreter was found. "
458525}
459526
527+ # Entry point
460528Write-Host " "
461529Write-Host " __ __ _____ _ " - ForegroundColor Magenta
462530Write-Host " \ \/ / |_ _|__ ___| |_ ___ _ __ " - ForegroundColor Magenta
0 commit comments