init: honor custom PYENV_ROOT in init setup tips (#3569)

This commit is contained in:
Zhaoqi Xu 2026-09-29 07:41:17 +08:00 committed by GitHub
parent d9b158620e
commit 699e27fb27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 102 additions and 13 deletions

View File

@ -189,8 +189,76 @@ function help_() {
} >&2
}
# True when tips/install should keep the portable default root form.
function pyenv_root_is_default() {
[[ ${PYENV_ROOT} == "${HOME}/.pyenv" ]]
}
# Escape a value for inclusion inside double quotes in a POSIX shell script.
function escape_for_double_quoted_posix() {
local s="$1"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
s="${s//\$/\\\$}"
s="${s//\`/\\\`}"
printf '%s' "$s"
}
# Escape a value for inclusion inside double quotes in a fish script.
function escape_for_double_quoted_fish() {
local s="$1"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
s="${s//\$/\\\$}"
printf '%s' "$s"
}
# Escape a value for inclusion inside single quotes in a PowerShell script.
function escape_for_single_quoted_pwsh() {
local s="$1"
# Bash 3.2 (macOS / CI docker): avoid \' in ${//} replacement —
# it becomes a literal backslash. Double single-quotes via a var instead.
local q="'"
s="${s//${q}/${q}${q}}"
printf '%s' "$s"
}
# Emit the PYENV_ROOT assignment line for tips/--install (posix|fish|pwsh).
# Portable default keeps $HOME / $Env:HOME; custom roots are quoted per target shell.
function print_pyenv_root_assignment() {
case "$1" in
posix )
if pyenv_root_is_default; then
echo 'export PYENV_ROOT="$HOME/.pyenv"'
else
printf 'export PYENV_ROOT="%s"\n' "$(escape_for_double_quoted_posix "${PYENV_ROOT}")"
fi
;;
fish )
if pyenv_root_is_default; then
# Keep master's expandable $HOME form (fish expands unquoted $HOME).
echo 'set -Ux PYENV_ROOT $HOME/.pyenv'
else
printf 'set -Ux PYENV_ROOT "%s"\n' "$(escape_for_double_quoted_fish "${PYENV_ROOT}")"
fi
;;
pwsh )
if pyenv_root_is_default; then
echo '$Env:PYENV_ROOT="$Env:HOME/.pyenv"'
else
# Single-quoted PowerShell literal: no $ / backtick expansion.
printf "\$Env:PYENV_ROOT='%s'\n" "$(escape_for_single_quoted_pwsh "${PYENV_ROOT}")"
fi
;;
* )
echo "pyenv: internal error: unknown shell family for PYENV_ROOT assignment: $1" >&2
return 1
;;
esac
}
function print_posix_shell_setup() {
echo 'export PYENV_ROOT="$HOME/.pyenv"'
print_pyenv_root_assignment posix
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"'
echo 'eval "$(pyenv init - '$shell')"'
}
@ -200,7 +268,7 @@ function print_fish_shell_setup() {
}
function print_fish_user_path_setup() {
echo 'set -Ux PYENV_ROOT $HOME/.pyenv'
print_pyenv_root_assignment fish
echo 'if functions -q fish_add_path'
echo ' test -d $PYENV_ROOT/bin; and fish_add_path $PYENV_ROOT/bin'
echo 'else'
@ -209,7 +277,7 @@ function print_fish_user_path_setup() {
}
function print_pwsh_shell_setup() {
echo '$Env:PYENV_ROOT="$Env:HOME/.pyenv"'
print_pyenv_root_assignment pwsh
echo 'if (Test-Path -LP "$Env:PYENV_ROOT/bin" -PathType Container) {'
echo ' $Env:PATH="$Env:PYENV_ROOT/bin:$Env:PATH" }'
echo 'iex ((pyenv init -) -join "`n")'

View File

@ -93,8 +93,9 @@ OUT
assert_line "--install"
}
@test "install setup for detected shell startup files" {
mkdir -p "$HOME"
@test "install setup for detected shell startup files (default PYENV_ROOT)" {
export PYENV_ROOT="$HOME/.pyenv"
mkdir -p "$PYENV_ROOT"
run pyenv-init --install
assert_success
@ -104,8 +105,9 @@ OUT
assert_equal "$expected_setup" "$(cat "$HOME/.profile")"
}
@test "install setup for bash uses existing bash_profile" {
mkdir -p "$HOME"
@test "install setup for bash uses existing bash_profile (default PYENV_ROOT)" {
export PYENV_ROOT="$HOME/.pyenv"
mkdir -p "$PYENV_ROOT"
touch "$HOME/.bash_profile"
run pyenv-init --install bash
@ -117,8 +119,9 @@ OUT
assert [ ! -e "$HOME/.profile" ]
}
@test "install setup for zsh startup files" {
mkdir -p "$HOME"
@test "install setup for zsh startup files (default PYENV_ROOT)" {
export PYENV_ROOT="$HOME/.pyenv"
mkdir -p "$PYENV_ROOT"
run pyenv-init --install zsh
assert_success
@ -128,8 +131,9 @@ OUT
assert_equal "$expected_setup" "$(cat "$HOME/.zprofile")"
}
@test "install setup for fish startup file" {
mkdir -p "$HOME"
@test "install setup for fish startup file (default PYENV_ROOT)" {
export PYENV_ROOT="$HOME/.pyenv"
mkdir -p "$PYENV_ROOT"
create_stub fish <<OUT
printf '%s\n' "\$2" > "$PYENV_TEST_DIR/fish-script"
OUT
@ -143,8 +147,9 @@ OUT
assert_equal "$expected_setup" "$(cat "$HOME/.config/fish/config.fish")"
}
@test "install setup for pwsh startup file" {
mkdir -p "$HOME"
@test "install setup for pwsh startup file (default PYENV_ROOT)" {
export PYENV_ROOT="$HOME/.pyenv"
mkdir -p "$PYENV_ROOT"
run pyenv-init --install pwsh
assert_success
@ -153,6 +158,22 @@ OUT
assert_equal "$expected_setup" "$(cat "$HOME/.config/powershell/profile.ps1")"
}
@test "init honors custom PYENV_ROOT and quotes it according to each shell's rules" {
export PYENV_ROOT='/tmp/pyenv$root/"quote"'
run pyenv-init bash
assert [ "$status" -eq 1 ]
assert_line 'export PYENV_ROOT="/tmp/pyenv\$root/\"quote\""'
run pyenv-init fish
assert [ "$status" -eq 1 ]
assert_line 'set -Ux PYENV_ROOT "/tmp/pyenv\$root/\"quote\""'
export PYENV_ROOT="/tmp/pyenv'root"
run pyenv-init pwsh
assert [ "$status" -eq 1 ]
assert_line "\$Env:PYENV_ROOT='/tmp/pyenv''root'"
}
@test "install refuses to modify files with pyenv-related code" {
mkdir -p "$HOME"
echo 'eval "$(pyenv init -)"' > "$HOME/.bashrc"