#!/usr/bin/env bash # Summary: Configure the shell environment for pyenv # Usage: eval "$(pyenv init [-|--path] [--no-push-path] [--no-rehash] [])" # pyenv init --install [] # pyenv init --detect-shell [] set -e [ -n "$PYENV_DEBUG" ] && set -x # Provide pyenv completions if [ "$1" = "--complete" ]; then echo - echo --path echo --install echo --no-push-path echo --no-rehash echo --detect-shell echo bash echo fish echo ksh echo pwsh echo zsh exit fi mode="help" while [ "$#" -gt 0 ]; do case "$1" in -) mode="print" ;; --path) mode="path" ;; --install) mode="install" ;; --detect-shell) mode="detect-shell" ;; --no-push-path) no_push_path=1 ;; --no-rehash) no_rehash=1 ;; *) shell="$1" ;; esac shift done # If shell is not provided, detect it. if [ -z "$shell" ]; then if shell=$(tr '\0' ' ' 2>/dev/null &2 } function print_posix_shell_setup() { echo 'export PYENV_ROOT="$HOME/.pyenv"' echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' echo 'eval "$(pyenv init - '$shell')"' } function print_fish_shell_setup() { echo 'pyenv init - fish | source' } function print_fish_user_path_setup() { echo 'set -Ux PYENV_ROOT $HOME/.pyenv' echo 'if functions -q fish_add_path' echo ' test -d $PYENV_ROOT/bin; and fish_add_path $PYENV_ROOT/bin' echo 'else' echo ' test -d $PYENV_ROOT/bin; and set -U fish_user_paths $PYENV_ROOT/bin $fish_user_paths' echo 'end' } function print_pwsh_shell_setup() { echo '$Env:PYENV_ROOT="$Env:HOME/.pyenv"' 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")' } function expand_home_path() { local path="$1" printf '%s\n' "${path/#\~/$HOME}" } function install_shell_startup_files() { if [[ -z $HOME ]]; then echo "pyenv: HOME must be set to configure shell startup files" >&2 return 1 fi detect_profile local files=() local lines=() local profile_path rc_path setup case "$shell" in bash | zsh | ksh | ksh93 | mksh ) rc_path="$(expand_home_path "$rc")" profile_path="$(expand_home_path "$profile")" setup="$(print_posix_shell_setup)" files=("$rc_path") lines=("$setup") if [[ $profile_path != "$rc_path" ]]; then files+=("$profile_path") lines+=("$setup") fi ;; fish ) rc_path="$(expand_home_path "$rc")" files=("$rc_path") lines=("$(print_fish_shell_setup)") ;; pwsh ) rc_path="$(expand_home_path "$rc")" files=("$rc_path") lines=("$(print_pwsh_shell_setup)") ;; * ) echo "pyenv: cannot automatically configure startup files for $shell" >&2 return 1 ;; esac local index for ((index = 0; index < ${#files[@]}; index++)); do check_startup_file "${files[$index]}" || return 1 done if [[ $shell == fish ]]; then install_fish_user_paths || return 1 fi for ((index = 0; index < ${#files[@]}; index++)); do append_lines "${files[$index]}" "${lines[$index]}" done } function check_startup_file() { local file="$1" local grep_status if [[ ! -e $file ]]; then return 0 fi if [[ ! -f $file || ! -r $file ]]; then echo "pyenv: failed to inspect $file" >&2 return 1 fi if grep -Fi pyenv "$file" >/dev/null; then echo "pyenv: cannot automatically apply changes to $file: it appears to already contain Pyenv-related code." >&2 echo "pyenv: review the file's contents and apply changes manually if necessary." >&2 echo "pyenv: run \`pyenv init $shell\` to see the suggested setup." >&2 return 1 else grep_status=$? if [[ $grep_status == 1 ]]; then return 0 fi echo "pyenv: failed to inspect $file" >&2 return "$grep_status" fi } function install_fish_user_paths() { local fish_setup if ! command -v fish >/dev/null; then echo "pyenv: fish is not available to configure fish universal variables" >&2 return 1 fi fish_setup="$(print_fish_user_path_setup)" if ! fish -c "$fish_setup"; then echo "pyenv: failed to configure fish universal variables" >&2 return 1 fi } function append_lines() { local file="$1" local lines="$2" local dir last_char dir="${file%/*}" if [[ $dir != "$file" ]]; then mkdir -p "$dir" fi if [[ -s $file ]]; then last_char="$(tail -c 1 "$file")" || return 1 if [[ -n $last_char ]]; then echo >> "$file" fi fi printf '%s\n' "$lines" >> "$file" } function init_dirs() { mkdir -p "${PYENV_ROOT}/"{shims,versions} } function print_path() { # if no_push_path is set, guard the PATH manipulation with a check on whether # the shim is already in the PATH. if [ -n "$no_push_path" ]; then case "$shell" in fish ) echo 'if not contains -- "'"${PYENV_ROOT}/shims"'" $PATH' print_path_prepend_shims echo 'end' ;; pwsh ) echo 'if ( $Env:PATH -notmatch "'"${PYENV_ROOT}/shims"'" ) {' print_path_prepend_shims echo '}' ;; * ) echo 'if [[ ":$PATH:" != *'\':"${PYENV_ROOT}"/shims:\''* ]]; then' print_path_prepend_shims echo 'fi' ;; esac else case "$shell" in fish ) echo 'while set pyenv_index (contains -i -- "'"${PYENV_ROOT}/shims"'" $PATH)' echo 'set -eg PATH[$pyenv_index]; end; set -e pyenv_index' 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 # that makes it source `bashrc` under SSH even when not interactive. # This is inhibited by a guard in Debian's stock `bashrc` but some people remove it # in order to get proper environment for noninteractive remote commands # (SSH provides /etc/ssh/sshrc and ~/.ssh/rc for that but no-one seems to use them for some reason). # This has caused an infinite `bashrc` execution loop for those people in the below nested Bash invocation (#2367). # --norc negates this behavior of such a customized Bash. echo 'PATH="$(bash --norc -ec '\''IFS=:; paths=($PATH); ' echo 'for i in ${!paths[@]}; do ' echo 'if [[ ${paths[i]} == "'\'\'"${PYENV_ROOT}/shims"\'\''" ]]; then unset '\'\\\'\''paths[i]'\'\\\'\''; ' echo 'fi; done; ' echo 'echo "${paths[*]}"'\'')"' print_path_prepend_shims ;; esac fi } function print_path_prepend_shims() { case "$shell" in fish ) echo 'set -gx PATH '\'"${PYENV_ROOT}/shims"\'' $PATH' ;; pwsh ) echo '$Env:PATH="'"${PYENV_ROOT}"'/shims:$Env:PATH"' ;; * ) echo 'export PATH="'"${PYENV_ROOT}"'/shims:${PATH}"' ;; esac } function print_env() { case "$shell" in fish ) echo "set -gx PYENV_SHELL $shell" ;; pwsh ) echo '$Env:PYENV_SHELL="'"$shell"'"' ;; * ) echo "export PYENV_SHELL=$shell" ;; esac } function print_completion() { completion="${_PYENV_INSTALL_PREFIX}/completions/pyenv.${shell}" if [ -r "$completion" ]; then case "$shell" in pwsh ) echo "iex (gc $completion -Raw)" ;; * ) echo "source '$completion'" ;; esac fi } function print_rehash() { if [ -z "$no_rehash" ]; then case "$shell" in pwsh ) echo '& pyenv rehash' ;; * ) echo 'command pyenv rehash' ;; esac fi } function print_shell_function() { commands=(`pyenv-commands --sh`) case "$shell" in fish ) echo \ 'function pyenv set command $argv[1] set -e argv[1] switch "$command" case '"${commands[*]}"' source (pyenv "sh-$command" $argv|psub) case "*" command pyenv "$command" $argv end end' ;; pwsh ) cat <