Merge 0541b2ce0872fb5e54779508dee6a1c49218bc18 into b9ede4ae0e66b29f07401de2db097984ca84034a

This commit is contained in:
Florian Blanchet 2025-06-05 10:35:14 -04:00 committed by GitHub
commit 54f34bde42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 219 additions and 6 deletions

View File

@ -269,6 +269,19 @@ See [Advanced configuration](#advanced-configuration) for details and more confi
</details> </details>
#### Microsoft PowerShell
<details>
Add the commands to `$profile.CurrentUserAllHosts` by running the following in your terminal:
~~~ pwsh
echo '$Env:PYENV_ROOT="$HOME/.pyenv"' >> $profile.CurrentUserAllHosts
echo '$Env:PATH="$Env:PYENV_ROOT/bin:$Env:PATH"' >> $profile.CurrentUserAllHosts
echo 'iex ((pyenv init -) -join "`n")' >> $profile.CurrentUserAllHosts
~~~
</details>
### C. Restart your shell ### C. Restart your shell
---- ----
@ -686,7 +699,7 @@ opposed to this idea. Here's what `eval "$(pyenv init -)"` actually does:
3. **Installs autocompletion.** This is entirely optional but pretty 3. **Installs autocompletion.** This is entirely optional but pretty
useful. Sourcing `<pyenv installation prefix>/completions/pyenv.bash` will set that useful. Sourcing `<pyenv installation prefix>/completions/pyenv.bash` will set that
up. There are also completions for Zsh and Fish. up. There are also completions for Zsh, Fish and PowerShell.
4. **Rehashes shims.** From time to time you'll need to rebuild your 4. **Rehashes shims.** From time to time you'll need to rebuild your
shim files. Doing this on init makes sure everything is up to shim files. Doing this on init makes sure everything is up to

17
completions/pyenv.pwsh Normal file
View File

@ -0,0 +1,17 @@
$scriptblock = {
param($wordToComplete, $commandAst, $cursorPosition)
$words = $commandAst.ToString()
if ( $wordToComplete ) {
$matches = (($words[0..$cursorPosition] -join '') | Select-String -Pattern "\s+" -AllMatches).Matches
if ( $matches ) {
$cursorPosition = $matches[-1].Index - 1
}
}
$words = $words[0..$cursorPosition] -join '' -split "\s+"
if ( $words.Count -ge 2 ) {
pyenv completions $words[1] | where { $_ -match $wordToComplete }
} else {
pyenv commands | where { $_ -match $wordToComplete }
}
}
Register-ArgumentCompleter -Native -CommandName pyenv -ScriptBlock $scriptblock

View File

@ -15,6 +15,7 @@ if [ "$1" = "--complete" ]; then
echo bash echo bash
echo fish echo fish
echo ksh echo ksh
echo pwsh
echo zsh echo zsh
exit exit
fi fi
@ -98,6 +99,10 @@ function detect_profile() {
profile_explain="~/.bash_profile if it exists, otherwise ~/.profile" profile_explain="~/.bash_profile if it exists, otherwise ~/.profile"
rc='~/.bashrc' rc='~/.bashrc'
;; ;;
pwsh )
profile='~/.config/powershell/profile.ps1'
rc='~/.config/powershell/profile.ps1'
;;
zsh ) zsh )
profile='~/.zprofile' profile='~/.zprofile'
rc='~/.zshrc' rc='~/.zshrc'
@ -146,6 +151,21 @@ function help_() {
echo 'pyenv init - fish | source' echo 'pyenv init - fish | source'
echo echo
;; ;;
pwsh )
echo '# Load pyenv automatically by appending'
echo -n "# the following to "
if [ "$profile" == "$rc" ]; then
echo "$profile :"
else
echo
echo "${profile_explain:-$profile} (for login shells)"
echo "and $rc (for interactive shells) :"
fi
echo
echo '$Env:PYENV_ROOT="$HOME/.pyenv"'
echo '$Env:PATH="$Env:PYENV_ROOT/bin:$Env:PATH"'
echo 'iex ((pyenv init -) -join "`n")'
;;
* ) * )
echo '# Load pyenv automatically by appending' echo '# Load pyenv automatically by appending'
echo -n "# the following to " echo -n "# the following to "
@ -182,6 +202,11 @@ function print_path() {
print_path_prepend_shims print_path_prepend_shims
echo 'end' echo 'end'
;; ;;
pwsh )
echo 'if ( $Env:PATH -match "'"${PYENV_ROOT}/shims"'" ) {'
print_path_prepend_shims
echo '}'
;;
* ) * )
echo 'if [[ ":$PATH:" != *'\':"${PYENV_ROOT}"/shims:\''* ]]; then' echo 'if [[ ":$PATH:" != *'\':"${PYENV_ROOT}"/shims:\''* ]]; then'
print_path_prepend_shims print_path_prepend_shims
@ -195,6 +220,10 @@ function print_path() {
echo 'set -eg PATH[$pyenv_index]; end; set -e pyenv_index' echo 'set -eg PATH[$pyenv_index]; end; set -e pyenv_index'
print_path_prepend_shims print_path_prepend_shims
;; ;;
pwsh )
echo '$Env:PATH="$(($Env:PATH -split '"':'"' | where { -not ($_ -match '"'${PYENV_ROOT}/shims'"') }) -join '"':'"')"'
print_path_prepend_shims
;;
* ) * )
# Some distros (notably Debian-based) set Bash's SSH_SOURCE_BASHRC compilation option # Some distros (notably Debian-based) set Bash's SSH_SOURCE_BASHRC compilation option
# that makes it source `bashrc` under SSH even when not interactive. # that makes it source `bashrc` under SSH even when not interactive.
@ -219,6 +248,9 @@ function print_path_prepend_shims() {
fish ) fish )
echo 'set -gx PATH '\'"${PYENV_ROOT}/shims"\'' $PATH' echo 'set -gx PATH '\'"${PYENV_ROOT}/shims"\'' $PATH'
;; ;;
pwsh )
echo '$Env:PATH="'"${PYENV_ROOT}"'/shims:$Env:PATH"'
;;
* ) * )
echo 'export PATH="'"${PYENV_ROOT}"'/shims:${PATH}"' echo 'export PATH="'"${PYENV_ROOT}"'/shims:${PATH}"'
;; ;;
@ -230,6 +262,9 @@ function print_env() {
fish ) fish )
echo "set -gx PYENV_SHELL $shell" echo "set -gx PYENV_SHELL $shell"
;; ;;
pwsh )
echo '$Env:PYENV_SHELL="'"$shell"'"'
;;
* ) * )
echo "export PYENV_SHELL=$shell" echo "export PYENV_SHELL=$shell"
;; ;;
@ -239,13 +274,27 @@ function print_env() {
function print_completion() { function print_completion() {
completion="${0%/*/*}/completions/pyenv.${shell}" completion="${0%/*/*}/completions/pyenv.${shell}"
if [ -r "$completion" ]; then if [ -r "$completion" ]; then
case "$shell" in
pwsh )
echo "iex (gc $completion -Raw)"
;;
* )
echo "source '$completion'" echo "source '$completion'"
;;
esac
fi fi
} }
function print_rehash() { function print_rehash() {
if [ -z "$no_rehash" ]; then if [ -z "$no_rehash" ]; then
case "$shell" in
pwsh )
echo '& pyenv rehash 2>/dev/null'
;;
* )
echo 'command pyenv rehash 2>/dev/null' echo 'command pyenv rehash 2>/dev/null'
;;
esac
fi fi
} }
@ -266,6 +315,25 @@ function print_shell_function() {
end end
end' end'
;; ;;
pwsh )
cat <<EOS
function pyenv {
\$command=""
if ( \$args.Count -gt 0 ) {
\$command, \$args = \$args
}
if ( ("${commands[*]}" -split ' ') -contains \$command ) {
\$shell_cmds = & \$Env:PYENV_ROOT/bin/pyenv sh-\$command \$args
if ( \$shell_cmds.Count -gt 0 ) {
iex (\$shell_cmds -join "\`n")
}
} else {
& \$Env:PYENV_ROOT/bin/pyenv \$command \$args
}
}
EOS
;;
ksh | ksh93 | mksh ) ksh | ksh93 | mksh )
echo \ echo \
'function pyenv { 'function pyenv {
@ -278,7 +346,7 @@ end'
;; ;;
esac esac
if [ "$shell" != "fish" ]; then if [ "$shell" != "fish" ] && [ "$shell" != "pwsh" ]; then
IFS="|" IFS="|"
echo \ echo \
' [ "$#" -gt 0 ] && shift ' [ "$#" -gt 0 ] && shift

View File

@ -14,7 +14,7 @@ shell="$(basename "${PYENV_SHELL:-$SHELL}")"
pyenv-rehash pyenv-rehash
case "$shell" in case "$shell" in
fish ) fish | pwsh )
# no rehash support # no rehash support
;; ;;
* ) * )

View File

@ -48,6 +48,9 @@ if [ "$versions" = "--unset" ]; then
echo 'set -gu PYENV_VERSION_OLD "$PYENV_VERSION"' echo 'set -gu PYENV_VERSION_OLD "$PYENV_VERSION"'
echo "set -e PYENV_VERSION" echo "set -e PYENV_VERSION"
;; ;;
pwsh )
echo '$Env:PYENV_VERSION, $Env:PYENV_VERSION_OLD = $null, $Env:PYENV_VERSION'
;;
* ) * )
echo 'PYENV_VERSION_OLD="${PYENV_VERSION-}"' echo 'PYENV_VERSION_OLD="${PYENV_VERSION-}"'
echo "unset PYENV_VERSION" echo "unset PYENV_VERSION"
@ -74,6 +77,16 @@ else
echo "pyenv: PYENV_VERSION_OLD is not set" >&2 echo "pyenv: PYENV_VERSION_OLD is not set" >&2
false false
end end
EOS
;;
pwsh )
cat <<EOS
if ( Get-Item -Path Env:\PYENV_VERSION* ) {
\$Env:PYENV_VERSION, \$Env:PYENV_VERSION_OLD = \$Env:PYENV_VERSION_OLD, \$Env:PYENV_VERSION
} else {
Write-Error "pyenv: Env:PYENV_VERSION_OLD is not set"
return \$false
}
EOS EOS
;; ;;
* ) * )
@ -109,6 +122,9 @@ if pyenv-prefix "${versions[@]}" >/dev/null; then
echo 'set -gu PYENV_VERSION_OLD "$PYENV_VERSION"' echo 'set -gu PYENV_VERSION_OLD "$PYENV_VERSION"'
echo "set -gx PYENV_VERSION \"$version\"" echo "set -gx PYENV_VERSION \"$version\""
;; ;;
pwsh )
echo '$Env:PYENV_VERSION, $Env:PYENV_VERSION_OLD = "'"${version}"'", $Env:PYENV_VERSION'
;;
* ) * )
echo 'PYENV_VERSION_OLD="${PYENV_VERSION-}"' echo 'PYENV_VERSION_OLD="${PYENV_VERSION-}"'
echo "export PYENV_VERSION=\"${version}\"" echo "export PYENV_VERSION=\"${version}\""

View File

@ -74,6 +74,19 @@ OUT
assert_line 'pyenv init - fish | source' assert_line 'pyenv init - fish | source'
} }
@test "setup shell completions (pwsh)" {
root="$(cd $BATS_TEST_DIRNAME/.. && pwd)"
run pyenv-init - pwsh
assert_success
assert_line "iex (gc ${root}/test/../libexec/../completions/pyenv.pwsh -Raw)"
}
@test "pwsh instructions" {
run pyenv-init pwsh
assert [ "$status" -eq 1 ]
assert_line 'iex ((pyenv init -) -join "`n")'
}
@test "shell detection for installer" { @test "shell detection for installer" {
run pyenv-init --detect-shell run pyenv-init --detect-shell
assert_success assert_success
@ -100,6 +113,13 @@ OUT
assert_line "set -gx PATH '${PYENV_ROOT}/shims' \$PATH" assert_line "set -gx PATH '${PYENV_ROOT}/shims' \$PATH"
} }
@test "adds shims to PATH (pwsh)" {
export PATH="${BATS_TEST_DIRNAME}/../libexec:/usr/bin:/bin:/usr/local/bin"
run pyenv-init - pwsh
assert_success
assert_line '$Env:PATH="'${PYENV_ROOT}'/shims:$Env:PATH"'
}
@test "removes existing shims from PATH" { @test "removes existing shims from PATH" {
OLDPATH="$PATH" OLDPATH="$PATH"
export PATH="${BATS_TEST_DIRNAME}/nonexistent:${PYENV_ROOT}/shims:$PATH" export PATH="${BATS_TEST_DIRNAME}/nonexistent:${PYENV_ROOT}/shims:$PATH"
@ -124,6 +144,19 @@ echo "\$PATH"
assert_output "${PYENV_ROOT}/shims:${BATS_TEST_DIRNAME}/nonexistent:${OLDPATH//${PYENV_ROOT}\/shims:/}" assert_output "${PYENV_ROOT}/shims:${BATS_TEST_DIRNAME}/nonexistent:${OLDPATH//${PYENV_ROOT}\/shims:/}"
} }
@test "removes existing shims from PATH (pwsh)" {
command -v pwsh >/dev/null || skip "-- pwsh not installed"
OLDPATH="$PATH"
export PATH="${BATS_TEST_DIRNAME}/nonexistent:${PYENV_ROOT}/shims:$PATH"
run pwsh -noni -c - <<!
\$Env:PATH="$PATH"
iex ((pyenv init -) -join "\`n")
echo "\$Env:PATH"
!
assert_success
assert_output "${PYENV_ROOT}/shims:${BATS_TEST_DIRNAME}/nonexistent:${OLDPATH//${PYENV_ROOT}\/shims:/}"
}
@test "adds shims to PATH with --no-push-path if they're not on PATH" { @test "adds shims to PATH with --no-push-path if they're not on PATH" {
export PATH="${BATS_TEST_DIRNAME}/../libexec:/usr/bin:/bin:/usr/local/bin" export PATH="${BATS_TEST_DIRNAME}/../libexec:/usr/bin:/bin:/usr/local/bin"
run bash -e <<! run bash -e <<!
@ -146,6 +179,18 @@ echo "\$PATH"
assert_output "${PYENV_ROOT}/shims:${PATH}" assert_output "${PYENV_ROOT}/shims:${PATH}"
} }
@test "adds shims to PATH with --no-push-path if they're not on PATH (pwsh)" {
command -v pwsh >/dev/null || skip "-- pwsh not installed"
export PATH="${BATS_TEST_DIRNAME}/../libexec:/usr/bin:/bin:/usr/local/bin"
run pwsh -noni -c - <<!
\$Env:PATH="$PATH"
iex ((pyenv init - --no-push-path) -join "\`n")
echo "\$Env:PATH"
!
assert_success
assert_output "${PYENV_ROOT}/shims:${PATH}"
}
@test "doesn't change PATH with --no-push-path if shims are already on PATH" { @test "doesn't change PATH with --no-push-path if shims are already on PATH" {
export PATH="${BATS_TEST_DIRNAME}/../libexec:${PYENV_ROOT}/shims:/usr/bin:/bin:/usr/local/bin" export PATH="${BATS_TEST_DIRNAME}/../libexec:${PYENV_ROOT}/shims:/usr/bin:/bin:/usr/local/bin"
run bash -e <<! run bash -e <<!
@ -168,6 +213,18 @@ echo "\$PATH"
assert_output "${PATH}" assert_output "${PATH}"
} }
@test "doesn't change PATH with --no-push-path if shims are already on PATH (pwsh)" {
command -v pwsh >/dev/null || skip "-- pwsh not installed"
export PATH="${BATS_TEST_DIRNAME}/../libexec:/usr/bin:${PYENV_ROOT}/shims:/bin:/usr/local/bin"
run pwsh -noni -c - <<!
\$Env:PATH="$PATH"
iex ((pyenv init - --no-push-path) -join "\`n")
echo "\$Env:PATH"
!
assert_success
assert_output "${PATH}"
}
@test "outputs sh-compatible syntax" { @test "outputs sh-compatible syntax" {
run pyenv-init - bash run pyenv-init - bash
assert_success assert_success
@ -202,3 +259,10 @@ echo
assert_line ' switch "$command"' assert_line ' switch "$command"'
refute_line ' case "$command" in' refute_line ' case "$command" in'
} }
@test "outputs pwsh-specific syntax (pwsh)" {
run pyenv-init - pwsh
assert_success
refute_line ' switch "$command"'
refute_line ' case "$command" in'
}

View File

@ -120,3 +120,10 @@ SH
assert_success "" assert_success ""
assert [ -x "${PYENV_ROOT}/shims/python" ] assert [ -x "${PYENV_ROOT}/shims/python" ]
} }
@test "sh-rehash in pwsh" {
create_executable "3.4" "python"
PYENV_SHELL=pwsh run pyenv-sh-rehash
assert_success ""
assert [ -x "${PYENV_ROOT}/shims/python" ]
}

View File

@ -31,6 +31,11 @@ load test_helper
assert_success 'echo "$PYENV_VERSION"' assert_success 'echo "$PYENV_VERSION"'
} }
@test "shell version (pwsh)" {
PYENV_SHELL=pwsh PYENV_VERSION="1.2.3" run pyenv-sh-shell
assert_success 'echo "$PYENV_VERSION"'
}
@test "shell revert" { @test "shell revert" {
PYENV_SHELL=bash run pyenv-sh-shell - PYENV_SHELL=bash run pyenv-sh-shell -
assert_success assert_success
@ -43,6 +48,12 @@ load test_helper
assert_line 0 'if set -q PYENV_VERSION_OLD' assert_line 0 'if set -q PYENV_VERSION_OLD'
} }
@test "shell revert (pwsh)" {
PYENV_SHELL=pwsh run pyenv-sh-shell -
assert_success
assert_line 0 'if ( Get-Item -Path Env:\PYENV_VERSION* ) {'
}
@test "shell unset" { @test "shell unset" {
PYENV_SHELL=bash run pyenv-sh-shell --unset PYENV_SHELL=bash run pyenv-sh-shell --unset
assert_success assert_success
@ -61,6 +72,14 @@ set -e PYENV_VERSION
OUT OUT
} }
@test "shell unset (pwsh)" {
PYENV_SHELL=pwsh run pyenv-sh-shell --unset
assert_success
assert_output <<OUT
\$Env:PYENV_VERSION, \$Env:PYENV_VERSION_OLD = \$null, \$Env:PYENV_VERSION
OUT
}
@test "shell change invalid version" { @test "shell change invalid version" {
run pyenv-sh-shell 1.2.3 run pyenv-sh-shell 1.2.3
assert_failure assert_failure
@ -89,3 +108,12 @@ set -gu PYENV_VERSION_OLD "\$PYENV_VERSION"
set -gx PYENV_VERSION "1.2.3" set -gx PYENV_VERSION "1.2.3"
OUT OUT
} }
@test "shell change version (pwsh)" {
mkdir -p "${PYENV_ROOT}/versions/1.2.3"
PYENV_SHELL=pwsh run pyenv-sh-shell 1.2.3
assert_success
assert_output <<OUT
\$Env:PYENV_VERSION, \$Env:PYENV_VERSION_OLD = "1.2.3", \$Env:PYENV_VERSION
OUT
}