Functions/GenXdev.AI/EnsurePip.ps1

<##############################################################################
Part of PowerShell module : GenXdev.AI
Original cmdlet filename : EnsurePip.ps1
Original author : René Vaessen / GenXdev
Version : 1.300.2025
################################################################################
Copyright (c) René Vaessen / GenXdev
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
    http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
################################################################################>

###############################################################################
<#
.SYNOPSIS
Ensures pip is installed and functional for the specified Python installation.
 
.DESCRIPTION
Verifies that pip is available and functional for the given Python executable.
If pip is not available, attempts to install it using ensurepip. Validates
pip functionality by checking version and basic operations. Returns $true if
pip is ready; throws Write-Error on failure.
 
.PARAMETER PythonPath
The path to the Python executable. If not specified, uses the Python found
in PATH.
 
.PARAMETER Timeout
Timeout in seconds for pip installation and verification.
 
.PARAMETER Force
Forces reinstallation/upgrade of pip.
 
.EXAMPLE
EnsurePip
 
Ensures pip is available for the default Python installation.
 
.EXAMPLE
EnsurePip -PythonPath "C:\Python39\python.exe" -Force
 
Forces pip installation for a specific Python executable.
 
.EXAMPLE
$pythonPath = EnsurePython
EnsurePip -PythonPath $pythonPath
 
Ensures pip for a specific Python installation returned by EnsurePython.
#>

function EnsurePip {

    [CmdletBinding()]

    param(
        #######################################################################
        [Parameter(
            Mandatory = $false,
            Position = 0,
            HelpMessage = "Path to Python executable"
        )]
        [string] $PythonPath,
        #######################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Timeout in seconds for pip installation"
        )]
        [ValidateRange(30, 1800)]
        [int] $Timeout = 300,
        #######################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Forces reinstallation/upgrade of pip"
        )]
        [switch] $Force
        #######################################################################
    )

    begin {
        # initialize variables
        $pipAvailable = $false
        $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

        # show initial progress
        Microsoft.PowerShell.Utility\Write-Progress `
            -Activity "pip Installation" `
            -Status "Checking pip availability..."
    }

    process {
        # determine python path
        if (-not $PythonPath) {
            $pythonCmd = Microsoft.PowerShell.Core\Get-Command python -ErrorAction SilentlyContinue
            if (-not $pythonCmd) {
                Microsoft.PowerShell.Utility\Write-Error `
                    "Python not found in PATH. Please specify -PythonPath or ensure Python is installed."
                return
            }
            $PythonPath = $pythonCmd.Source
        }

        # verify python executable exists
        if (-not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $PythonPath)) {
            Microsoft.PowerShell.Utility\Write-Error `
                "Python executable not found at: ${PythonPath}"
            return
        }

        Microsoft.PowerShell.Utility\Write-Verbose `
            "Using Python at: ${PythonPath}"

        # check if pip is already available (unless Force is specified)
        if (-not $Force) {
            Microsoft.PowerShell.Utility\Write-Progress `
                -Activity "pip Installation" `
                -Status "Checking existing pip installation..." `
                -PercentComplete 25

            # check pip via command line
            $pipversion = & $PythonPath -m pip --version 2>$null

            if ($pipversion) {
                Microsoft.PowerShell.Utility\Write-Verbose `
                    "pip already available: ${pipversion}"
                return $true
            }

            # fallback: check pip via Python import
            $pipcheck = & $PythonPath -c "import pip; print('pip', pip.__version__)" 2>$null

            if ($pipcheck) {
                Microsoft.PowerShell.Utility\Write-Verbose `
                    "pip detected via Python import: ${pipcheck}"
                return $true
            }
        }

        # install/upgrade pip using ensurepip
        Microsoft.PowerShell.Utility\Write-Progress `
            -Activity "pip Installation" `
            -Status "Installing pip via ensurepip..." `
            -PercentComplete 50

        Microsoft.PowerShell.Utility\Write-Verbose `
            "Installing pip via ensurepip"

        try {
            $ensurepipArgs = @("-m", "ensurepip", "--default-pip")
            if ($Force) {
                $ensurepipArgs += "--upgrade"
            }

            $ensurepipOutput = & $PythonPath $ensurepipArgs 2>&1
            Microsoft.PowerShell.Utility\Write-Verbose "ensurepip output: $ensurepipOutput"

        } catch {
            Microsoft.PowerShell.Utility\Write-Verbose "ensurepip failed: $($_.Exception.Message)"
        }

        # verify pip installation
        Microsoft.PowerShell.Utility\Write-Progress `
            -Activity "pip Installation" `
            -Status "Verifying pip installation..." `
            -PercentComplete 75

        # try pip command line again
        $pipversion = & $PythonPath -m pip --version 2>$null

        if ($pipversion) {
            Microsoft.PowerShell.Utility\Write-Verbose `
                "pip successfully installed: ${pipversion}"
            $pipAvailable = $true
        } else {
            # try alternative pip check
            $pipcheck = & $PythonPath -c "import pip; print('pip', pip.__version__)" 2>$null

            if ($pipcheck) {
                Microsoft.PowerShell.Utility\Write-Verbose `
                    "pip available via Python import: ${pipcheck}"
                $pipAvailable = $true
            } else {
                Microsoft.PowerShell.Utility\Write-Error `
                    "Failed to install or verify pip. Try manually installing pip or reinstalling Python with pip support."
                return
            }
        }

        # upgrade pip if Force is specified and pip is available
        if ($Force -and $pipAvailable) {
            Microsoft.PowerShell.Utility\Write-Progress `
                -Activity "pip Installation" `
                -Status "Upgrading pip..." `
                -PercentComplete 90

            try {
                $upgradeOutput = & $PythonPath -m pip install --upgrade pip 2>&1
                Microsoft.PowerShell.Utility\Write-Verbose "pip upgrade output: $upgradeOutput"
            } catch {
                Microsoft.PowerShell.Utility\Write-Verbose "pip upgrade failed: $($_.Exception.Message)"
            }
        }

        # check timeout
        if ($stopwatch.Elapsed.TotalSeconds -gt $Timeout) {
            Microsoft.PowerShell.Utility\Write-Error `
                "pip installation timed out after ${Timeout} seconds."
            return
        }

        # final verification
        $finalPipVersion = & $PythonPath -m pip --version 2>$null
        if ($finalPipVersion) {
            Microsoft.PowerShell.Utility\Write-Verbose `
                "Final pip verification: ${finalPipVersion}"
        }

        # complete progress
        Microsoft.PowerShell.Utility\Write-Progress `
            -Activity "pip Installation" `
            -Status "pip ready" `
            -PercentComplete 100

        return $pipAvailable
    }

    end {
        Microsoft.PowerShell.Utility\Write-Progress `
            -Activity "pip Installation" `
            -Completed
    }
}
###############################################################################