Last active
March 19, 2024 04:28
-
-
Save blakeNaccarato/e9120dcb965c693941d01b2f5a5f4fd7 to your computer and use it in GitHub Desktop.
Cross-platform PowerShell script (tested on Windows, Ubuntu, MacOS) to sync a Python environment with `uv`, example for `requirements.txt`, `pyproject.toml` is similar.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
uv==0.1.22 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<#.SYNOPSIS | |
Sync Python dependencies.#> | |
Param( | |
# Python version. | |
[string]$Version = '3.11' | |
) | |
$REQUIREMENTS = 'requirements.txt' | |
function Sync-Py { | |
<#.SYNOPSIS | |
Sync Python dependencies.#> | |
'SYNCING' | Write-PyProgress | |
$py = Get-Py $Version | |
'INSTALLING UV' | Write-PyProgress | |
$uv = Get-Content $REQUIREMENTS | Select-String -Pattern '^uv' | |
Invoke-Expression "$py -m pip install $uv | |
'SYNCING DEPENDENCIES' | Write-PyProgress | |
Invoke-Expression "$py -m uv pip sync $REQUIREMENTS" | |
'SYNCED' | Write-PyProgress -Done | |
} | |
function Get-Py { | |
<#.SYNOPSIS | |
Get Python interpreter, global in CI, or activated virtual environment locally.#> | |
Param([Parameter(ValueFromPipeline)][string]$Version) | |
begin { $venvPath = '.venv' } | |
process { | |
$Version = $Version ? $Version : (Get-PyDevVersion) | |
$GlobalPy = Get-PyGlobal $Version | |
if (!(Test-Path $venvPath)) { | |
"CREATING VIRTUAL ENVIRONMENT: $venvPath" | Write-PyProgress | |
Invoke-Expression "$GlobalPy -m venv $venvPath" | |
} | |
$VenvPy = Start-PyEnv $venvPath | |
$foundVersion = Invoke-Expression "$VenvPy --version" | |
if ($foundVersion | | |
Select-String -Pattern "^Python $([Regex]::Escape($Version))\.\d+$") { | |
"SYNCING VIRTUAL ENVIRONMENT: $(Resolve-Path $VenvPy -Relative)" | | |
Write-PyProgress | |
return $VenvPy | |
} | |
"REMOVING VIRTUAL ENVIRONMENT WITH INCORRECT PYTHON: $Env:VIRTUAL_ENV" | | |
Write-PyProgress -Done | |
Remove-Item -Recurse -Force $Env:VIRTUAL_ENV | |
return Get-Py $Version | |
} | |
} | |
function Get-PyDevVersion { | |
<#.SYNOPSIS | |
Get the expected version of Python for development, from '.copier-answers.yml'.#> | |
$ver_pattern = '^python_version:\s?["'']([^"'']+)["'']$' | |
$re = Get-Content '.copier-answers.yml' | Select-String -Pattern $ver_pattern | |
return $re.Matches.Groups[1].value | |
} | |
function Get-PyGlobal { | |
<#.SYNOPSIS | |
Get global Python interpreter.#> | |
Param([Parameter(Mandatory, ValueFromPipeline)][string]$Version) | |
process { | |
if ((Test-Command 'py') -and | |
(py '--list' | Select-String -Pattern "^\s?-V:$([Regex]::Escape($Version))")) { | |
return "py -$Version" | |
} | |
elseif (Test-Command "python$Version") { return "python$Version" } | |
elseif (Test-Command 'python') { return 'python' } | |
throw "Expected Python $Version, which does not appear to be installed. Ensure it is installed (e.g. from https://www.python.org/downloads/) and run this script again." | |
} | |
} | |
function Start-PyEnv { | |
<#.SYNOPSIS | |
Activate and get the Python interpreter for the virtual environment.#> | |
Param([Parameter(Mandatory, ValueFromPipeline)][string]$venvPath) | |
process { | |
if ($IsWindows) { $bin = 'Scripts'; $py = 'python.exe' } | |
else { $bin = 'bin'; $py = 'python' } | |
Invoke-Expression "$venvPath/$bin/Activate.ps1" | |
return "$Env:VIRTUAL_ENV/$bin/$py" | |
} | |
} | |
function Write-PyProgress { | |
<#.SYNOPSIS | |
Write progress and completion messages.#> | |
Param([Parameter(Mandatory, ValueFromPipeline)][string]$Message, | |
[switch]$Done) | |
begin { $Color = $Done ? 'Green' : 'Yellow' } | |
process { | |
if (!$Done) { Write-Host } | |
Write-Host "$Message$($Done ? '' : '...')" -ForegroundColor $Color | |
} | |
} | |
function Test-Command { | |
<#.SYNOPSIS | |
Like `Get-Command` but errors are ignored.#> | |
return Get-Command @args -ErrorAction 'Ignore' | |
} | |
Sync-Py |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment