pyenv/libexec/pyenv-rehash
Casey McGinty 83e5459cfb Remove shebang lines from scripts for performance
All scripts in libexec/ (excluding pyenv) are called through pyenv,
therefore the shebang lines are not necessary. On some systems this
provides a measurable increase in performance of the shell prompt.

Related to pyenv/pyenv-virtualenv#259
2019-03-07 22:15:14 +01:00

172 lines
4.2 KiB
Text
Executable file

# Summary: Rehash pyenv shims (run this after installing executables)
set -e
[ -n "$PYENV_DEBUG" ] && set -x
SHIM_PATH="${PYENV_ROOT}/shims"
PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.pyenv-shim"
# Create the shims directory if it doesn't already exist.
mkdir -p "$SHIM_PATH"
acquire_lock() {
# Ensure only one instance of pyenv-rehash is running at a time by
# setting the shell's `noclobber` option and attempting to write to
# the prototype shim file. If the file already exists, print a warning
# to stderr and exit with a non-zero status.
local ret
set -o noclobber
echo > "$PROTOTYPE_SHIM_PATH" 2>| /dev/null || ret=1
set +o noclobber
[ -z "${ret}" ]
}
# If we were able to obtain a lock, register a trap to clean up the
# prototype shim when the process exits.
trap release_lock EXIT
remove_prototype_shim() {
rm -f "$PROTOTYPE_SHIM_PATH"
}
release_lock() {
remove_prototype_shim
}
if [ ! -w "$SHIM_PATH" ]; then
echo "pyenv: cannot rehash: $SHIM_PATH isn't writable"
exit 1
fi
unset acquired
for (( i=1; i<="${PYENV_REHASH_TIMEOUT:-60}"; i++ )); do
if acquire_lock 2>/dev/null; then
acquired=1
break
else
# POSIX sleep(1) doesn't provides time precision of subsecond
sleep 1
fi
done
if [ -z "${acquired}" ]; then
echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists"
exit 1
fi
# The prototype shim file is a script that re-execs itself, passing
# its filename and any arguments to `pyenv exec`. This file is
# hard-linked for every executable and then removed. The linking
# technique is fast, uses less disk space than unique files, and also
# serves as a locking mechanism.
create_prototype_shim() {
cat > "$PROTOTYPE_SHIM_PATH" <<SH
set -e
[ -n "\$PYENV_DEBUG" ] && set -x
program="\${0##*/}"
if [[ "\$program" = "python"* ]]; then
for arg; do
case "\$arg" in
-c* | -- ) break ;;
*/* )
if [ -f "\$arg" ]; then
export PYENV_FILE_ARG="\$arg"
break
fi
;;
esac
done
fi
export PYENV_ROOT="$PYENV_ROOT"
exec "$(command -v pyenv)" exec "\$program" "\$@"
SH
chmod +x "$PROTOTYPE_SHIM_PATH"
}
# If the contents of the prototype shim file differ from the contents
# of the first shim in the shims directory, assume pyenv has been
# upgraded and the existing shims need to be removed.
remove_outdated_shims() {
local shim
for shim in "$SHIM_PATH"/*; do
if ! diff "$PROTOTYPE_SHIM_PATH" "$shim" >/dev/null 2>&1; then
rm -f "$SHIM_PATH"/*
fi
break
done
}
# List basenames of executables for every Python version
list_executable_names() {
local version file
pyenv-versions --bare --skip-aliases | \
while read -r version; do
for file in "${PYENV_ROOT}/versions/${version}/bin/"*; do
echo "${file##*/}"
done
done
}
# The basename of each argument passed to `make_shims` will be
# registered for installation as a shim. In this way, plugins may call
# `make_shims` with a glob to register many shims at once.
make_shims() {
local file shim
for file; do
shim="${file##*/}"
register_shim "$shim"
done
}
registered_shims=" "
# Registers the name of a shim to be generated.
register_shim() {
registered_shims="${registered_shims}${1} "
}
# Install all the shims registered via `make_shims` or `register_shim` directly.
install_registered_shims() {
local shim file
for shim in $registered_shims; do
file="${SHIM_PATH}/${shim}"
[ -e "$file" ] || cp "$PROTOTYPE_SHIM_PATH" "$file"
done
}
# Once the registered shims have been installed, we make a second pass
# over the contents of the shims directory. Any file that is present
# in the directory but has not been registered as a shim should be
# removed.
remove_stale_shims() {
local shim
for shim in "$SHIM_PATH"/*; do
if [[ "$registered_shims" != *" ${shim##*/} "* ]]; then
rm -f "$shim"
fi
done
}
shopt -s nullglob
# Create the prototype shim, then register shims for all known
# executables.
create_prototype_shim
remove_outdated_shims
# shellcheck disable=SC2046
make_shims $(list_executable_names | sort -u)
# Allow plugins to register shims.
OLDIFS="$IFS"
IFS=$'\n' scripts=(`pyenv-hooks rehash`)
IFS="$OLDIFS"
for script in "${scripts[@]}"; do
source "$script"
done
install_registered_shims
remove_stale_shims