Spaces:
Running
Running
param([parameter(Position=0,Mandatory=$false)] [Hashtable] $CondaModuleArgs=@{}) | |
# Defaults from before we had arguments. | |
if (-not $CondaModuleArgs.ContainsKey('ChangePs1')) { | |
$CondaModuleArgs.ChangePs1 = $True | |
} | |
## ENVIRONMENT MANAGEMENT ###################################################### | |
<# | |
.SYNOPSIS | |
Obtains a list of valid conda environments. | |
.EXAMPLE | |
Get-CondaEnvironment | |
.EXAMPLE | |
genv | |
#> | |
function Get-CondaEnvironment { | |
[CmdletBinding()] | |
param(); | |
begin {} | |
process { | |
# NB: the JSON output of conda env list does not include the names | |
# of each env, so we need to parse the fragile output instead. | |
& $Env:CONDA_EXE $Env:_CE_M $Env:_CE_CONDA env list | ` | |
Where-Object { -not $_.StartsWith("#") } | ` | |
Where-Object { -not $_.Trim().Length -eq 0 } | ` | |
ForEach-Object { | |
$envLine = $_ -split "\s+"; | |
$Active = $envLine[1] -eq "*"; | |
[PSCustomObject] @{ | |
Name = $envLine[0]; | |
Active = $Active; | |
Path = if ($Active) {$envLine[2]} else {$envLine[1]}; | |
} | Write-Output; | |
} | |
} | |
end {} | |
} | |
<# | |
.SYNOPSIS | |
Activates a conda environment, placing its commands and packages at | |
the head of $Env:PATH. | |
.EXAMPLE | |
Enter-CondaEnvironment base | |
.EXAMPLE | |
etenv base | |
.NOTES | |
This command does not currently support activating environments stored | |
in a non-standard location. | |
#> | |
function Enter-CondaEnvironment { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory=$false)][switch]$Stack, | |
[Parameter(Position=0)][string]$Name | |
); | |
begin { | |
If ($Stack) { | |
$activateCommand = (& $Env:CONDA_EXE $Env:_CE_M $Env:_CE_CONDA shell.powershell activate --stack $Name | Out-String); | |
} Else { | |
$activateCommand = (& $Env:CONDA_EXE $Env:_CE_M $Env:_CE_CONDA shell.powershell activate $Name | Out-String); | |
} | |
Write-Verbose "[conda shell.powershell activate $Name]`n$activateCommand"; | |
Invoke-Expression -Command $activateCommand; | |
} | |
process {} | |
end {} | |
} | |
<# | |
.SYNOPSIS | |
Deactivates the current conda environment, if any. | |
.EXAMPLE | |
Exit-CondaEnvironment | |
.EXAMPLE | |
exenv | |
#> | |
function Exit-CondaEnvironment { | |
[CmdletBinding()] | |
param(); | |
begin { | |
$deactivateCommand = (& $Env:CONDA_EXE $Env:_CE_M $Env:_CE_CONDA shell.powershell deactivate | Out-String); | |
# If deactivate returns an empty string, we have nothing more to do, | |
# so return early. | |
if ($deactivateCommand.Trim().Length -eq 0) { | |
return; | |
} | |
Write-Verbose "[conda shell.powershell deactivate]`n$deactivateCommand"; | |
Invoke-Expression -Command $deactivateCommand; | |
} | |
process {} | |
end {} | |
} | |
## CONDA WRAPPER ############################################################### | |
<# | |
.SYNOPSIS | |
conda is a tool for managing and deploying applications, environments | |
and packages. | |
.PARAMETER Command | |
Subcommand to invoke. | |
.EXAMPLE | |
conda install toolz | |
#> | |
function Invoke-Conda() { | |
# Don't use any explicit args here, we'll use $args and tab completion | |
# so that we can capture everything, INCLUDING short options (e.g. -n). | |
if ($Args.Count -eq 0) { | |
# No args, just call the underlying conda executable. | |
& $Env:CONDA_EXE $Env:_CE_M $Env:_CE_CONDA; | |
} | |
else { | |
$Command = $Args[0]; | |
if ($Args.Count -ge 2) { | |
$OtherArgs = $Args[1..($Args.Count - 1)]; | |
} else { | |
$OtherArgs = @(); | |
} | |
switch ($Command) { | |
"activate" { | |
Enter-CondaEnvironment @OtherArgs; | |
} | |
"deactivate" { | |
Exit-CondaEnvironment; | |
} | |
default { | |
# There may be a command we don't know want to handle | |
# differently in the shell wrapper, pass it through | |
# verbatim. | |
& $Env:CONDA_EXE $Env:_CE_M $Env:_CE_CONDA $Command @OtherArgs; | |
} | |
} | |
} | |
} | |
## TAB COMPLETION ############################################################## | |
# We borrow the approach used by posh-git, in which we override any existing | |
# functions named TabExpansion, look for commands we can complete on, and then | |
# default to the previously defined TabExpansion function for everything else. | |
if (Test-Path Function:\TabExpansion) { | |
# Since this technique is common, we encounter an infinite loop if it's | |
# used more than once unless we give our backup a unique name. | |
Rename-Item Function:\TabExpansion CondaTabExpansionBackup | |
} | |
function Expand-CondaEnv() { | |
param( | |
[string] | |
$Filter | |
); | |
$ValidEnvs = Get-CondaEnvironment; | |
$ValidEnvs ` | |
| Where-Object { $_.Name -like "$filter*" } ` | |
| ForEach-Object { $_.Name } ` | |
| Write-Output; | |
$ValidEnvs ` | |
| Where-Object { $_.Path -like "$filter*" } ` | |
| ForEach-Object { $_.Path } ` | |
| Write-Output; | |
} | |
function Expand-CondaSubcommands() { | |
param( | |
[string] | |
$Filter | |
); | |
$ValidCommands = Invoke-Conda shell.powershell commands; | |
# Add in the commands defined within this wrapper, filter, sort, and return. | |
$ValidCommands + @('activate', 'deactivate') ` | |
| Where-Object { $_ -like "$Filter*" } ` | |
| Sort-Object ` | |
| Write-Output; | |
} | |
function TabExpansion($line, $lastWord) { | |
$lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart() | |
switch -regex ($lastBlock) { | |
# Pull out conda commands we recognize first before falling through | |
# to the general patterns for conda itself. | |
"^conda activate (.*)" { Expand-CondaEnv $lastWord; break; } | |
"^etenv (.*)" { Expand-CondaEnv $lastWord; break; } | |
# If we got down to here, check arguments to conda itself. | |
"^conda (.*)" { Expand-CondaSubcommands $lastWord; break; } | |
# Finally, fall back on existing tab expansion. | |
default { | |
if (Test-Path Function:\CondaTabExpansionBackup) { | |
CondaTabExpansionBackup $line $lastWord | |
} | |
} | |
} | |
} | |
## PROMPT MANAGEMENT ########################################################### | |
<# | |
.SYNOPSIS | |
Modifies the current prompt to show the currently activated conda | |
environment, if any. | |
.EXAMPLE | |
Add-CondaEnvironmentToPrompt | |
Causes the current session's prompt to display the currently activated | |
conda environment. | |
#> | |
if ($CondaModuleArgs.ChangePs1) { | |
# We use the same procedure to nest prompts as we did for nested tab completion. | |
if (Test-Path Function:\prompt) { | |
Rename-Item Function:\prompt CondaPromptBackup | |
} else { | |
function CondaPromptBackup() { | |
# Restore a basic prompt if the definition is missing. | |
"PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "; | |
} | |
} | |
function global:prompt() { | |
if ($Env:CONDA_PROMPT_MODIFIER) { | |
$Env:CONDA_PROMPT_MODIFIER | Write-Host -NoNewline | |
} | |
CondaPromptBackup; | |
} | |
} | |
## ALIASES ##################################################################### | |
New-Alias conda Invoke-Conda -Force | |
New-Alias genv Get-CondaEnvironment -Force | |
New-Alias etenv Enter-CondaEnvironment -Force | |
New-Alias exenv Exit-CondaEnvironment -Force | |
## EXPORTS ################################################################### | |
Export-ModuleMember ` | |
-Alias * ` | |
-Function ` | |
Invoke-Conda, ` | |
Get-CondaEnvironment, ` | |
Enter-CondaEnvironment, Exit-CondaEnvironment, ` | |
TabExpansion, prompt | |