2424
2525$ErrorActionPreference = " Stop"
2626
27- $Script :InstallerVersion = " 1.4 .0"
27+ $Script :InstallerVersion = " 1.5 .0"
2828
2929# Force UTF-8 console output so winget's progress glyphs and other tool output
3030# are not rendered as mojibake in legacy code-page consoles.
@@ -434,20 +434,49 @@ function Find-PythonCandidate {
434434 }
435435 }
436436
437- # Common per-user install locations winget uses for Python.Python.3.x.
438- $localPython = Join-Path $env: LOCALAPPDATA " Programs\Python"
439- if (Test-Path - LiteralPath $localPython ) {
440- $found = Get-ChildItem - LiteralPath $localPython - Directory - ErrorAction SilentlyContinue |
441- Where-Object { $_.Name -match " ^Python3(1[1-9]|[2-9][0-9])" } |
437+ # Common install locations for python.org / winget Python.Python.3.x packages.
438+ $installRoots = @ (
439+ (Join-Path $env: LOCALAPPDATA " Programs\Python" ),
440+ (Join-Path $env: ProgramFiles " Python" ),
441+ $env: ProgramFiles ,
442+ ${env: ProgramFiles(x86)}
443+ )
444+ foreach ($root in $installRoots ) {
445+ if (-not $root ) { continue }
446+ if (-not (Test-Path - LiteralPath $root )) { continue }
447+ $found = Get-ChildItem - LiteralPath $root - Directory - ErrorAction SilentlyContinue |
448+ Where-Object { $_.Name -match " ^Python3(1[1-9]|[2-9][0-9])$" } |
442449 Sort-Object Name - Descending
443450 foreach ($dir in $found ) {
444451 $exe = Join-Path $dir.FullName " python.exe"
445452 if (Test-Path - LiteralPath $exe ) { $candidates += , @ ($exe , @ ()) }
446453 }
447454 }
448455
456+ # winget user-scope installs often land under %LOCALAPPDATA%\Microsoft\WinGet\Packages\Python.Python.3.*.
457+ $wingetPackages = Join-Path $env: LOCALAPPDATA " Microsoft\WinGet\Packages"
458+ if (Test-Path - LiteralPath $wingetPackages ) {
459+ $pythonPkgs = Get-ChildItem - LiteralPath $wingetPackages - Directory - ErrorAction SilentlyContinue |
460+ Where-Object { $_.Name -like " Python.Python.3.*" } |
461+ Sort-Object Name - Descending
462+ foreach ($pkg in $pythonPkgs ) {
463+ $exes = Get-ChildItem - LiteralPath $pkg.FullName - Recurse - Filter python.exe - ErrorAction SilentlyContinue - Depth 4
464+ foreach ($exe in $exes ) { $candidates += , @ ($exe.FullName , @ ()) }
465+ }
466+ }
467+
468+ # winget Links shim directory exposes python.exe / python3.exe shortcuts on PATH on newer winget builds.
469+ $wingetLinks = Join-Path $env: LOCALAPPDATA " Microsoft\WinGet\Links"
470+ if (Test-Path - LiteralPath $wingetLinks ) {
471+ foreach ($name in @ (" python.exe" , " python3.exe" , " python3.12.exe" , " python3.11.exe" )) {
472+ $exe = Join-Path $wingetLinks $name
473+ if (Test-Path - LiteralPath $exe ) { $candidates += , @ ($exe , @ ()) }
474+ }
475+ }
476+
449477 if ($candidates.Count -eq 0 ) {
450- Warn " No Python candidates were discovered (no python/python3 on PATH, no py.exe launcher, no %LOCALAPPDATA%\Programs\Python\Python3* install)."
478+ Warn " No Python candidates were discovered."
479+ Info " Scanned: PATH (python, python3), py.exe launcher, %LOCALAPPDATA%\Programs\Python, %ProgramFiles%\Python, %ProgramFiles%, %ProgramFiles(x86)%, %LOCALAPPDATA%\Microsoft\WinGet\Packages\Python.Python.3.*, %LOCALAPPDATA%\Microsoft\WinGet\Links."
451480 }
452481 foreach ($pair in $candidates ) {
453482 $result = Test-PythonExecutable $pair [0 ] $pair [1 ]
@@ -493,7 +522,19 @@ function Install-PythonViaWinget {
493522 # winget does not refresh the current session's PATH; pull the new locations in manually.
494523 $machinePath = [Environment ]::GetEnvironmentVariable(" Path" , " Machine" )
495524 $userPath = [Environment ]::GetEnvironmentVariable(" Path" , " User" )
496- $env: Path = (@ ($machinePath , $userPath ) | Where-Object { $_ }) -join " ;"
525+ $extra = @ ()
526+ $wingetLinks = Join-Path $env: LOCALAPPDATA " Microsoft\WinGet\Links"
527+ if (Test-Path - LiteralPath $wingetLinks ) { $extra += $wingetLinks }
528+ $localProgramsPython = Join-Path $env: LOCALAPPDATA " Programs\Python"
529+ if (Test-Path - LiteralPath $localProgramsPython ) {
530+ Get-ChildItem - LiteralPath $localProgramsPython - Directory - ErrorAction SilentlyContinue |
531+ Where-Object { $_.Name -match " ^Python3(1[1-9]|[2-9][0-9])$" } |
532+ ForEach-Object {
533+ $extra += $_.FullName
534+ $extra += (Join-Path $_.FullName " Scripts" )
535+ }
536+ }
537+ $env: Path = (@ ($machinePath , $userPath ) + $extra | Where-Object { $_ }) -join " ;"
497538 return $true
498539}
499540
@@ -512,7 +553,7 @@ function Resolve-Python {
512553 }
513554
514555 Fail " No working Python 3.11+ interpreter was found."
515- Info " Tried: python, python3, py -3.12, py -3.11, py -3.13, py -3 plus %LOCALAPPDATA%\Programs\Python\Python3* ."
556+ Info " Tried: python, python3, py -3.12, py -3.11, py -3.13, py -3.14, py -3 plus %LOCALAPPDATA%\Programs\Python, %ProgramFiles%\Python, %ProgramFiles%, %ProgramFiles(x86)%, %LOCALAPPDATA%\Microsoft\WinGet\Packages\Python.Python.3.*, %LOCALAPPDATA%\Microsoft\WinGet\Links ."
516557 Info " The Microsoft Store stub at WindowsApps\python.exe is intentionally ignored."
517558 if (Test-IsWindowsArm64Host ) {
518559 Info " Native ARM64 Python is intentionally ignored because required auth-helper wheels can fall back to cryptography/OpenSSL source builds."
0 commit comments