mirror of
https://github.com/pyenv/pyenv-virtualenv.git
synced 2026-09-10 07:16:18 -04:00
Cache hook state to skip redundant pyenv sh-activate calls
Mtime-based caching: stat the directory tree once per prompt (~2ms) and skip full activation when nothing has changed. - Compare PYENV_VERSION, VIRTUAL_ENV, PWD; shortcut when PYENV_VERSION matches - Walk PWD to / for .python-version (stop at first; include broken symlinks) - Path list built once on miss, stored as array (spaces in paths) - Global version file skipped when local version active - version-name hooks checked at init; if present, caching disabled - Covers bash, zsh, and fish
This commit is contained in:
parent
38f3333f3a
commit
8546b11429
|
|
@ -9,6 +9,23 @@
|
|||
set -e
|
||||
[ -n "$PYENV_DEBUG" ] && set -x
|
||||
|
||||
# Detect stat format for mtime: GNU uses -c %Y, BSD uses -f %m
|
||||
# -L follows symlinks: a symlinked .python-version reflects target changes
|
||||
if stat -L -c %Y / >/dev/null 2>&1; then
|
||||
_stat_fmt="-L -c %Y"
|
||||
else
|
||||
_stat_fmt="-L -f %m"
|
||||
fi
|
||||
|
||||
# Check for version-name hooks at init time. Hooks can alter version
|
||||
# resolution in ways the mtime cache cannot track. If present, the hook
|
||||
# falls back to upstream behavior (no caching). Restart shell after
|
||||
# installing or removing pyenv plugins.
|
||||
_has_version_hooks=""
|
||||
if [ -n "$(pyenv hooks version-name 2>/dev/null)" ]; then
|
||||
_has_version_hooks=1
|
||||
fi
|
||||
|
||||
resolve_link() {
|
||||
$(type -p greadlink readlink | head -1) "$1"
|
||||
}
|
||||
|
|
@ -103,7 +120,8 @@ esac
|
|||
|
||||
case "$shell" in
|
||||
fish )
|
||||
cat <<EOS
|
||||
if [ -n "$_has_version_hooks" ]; then
|
||||
cat <<EOS
|
||||
function _pyenv_virtualenv_hook --on-event fish_prompt;
|
||||
set -l ret \$status
|
||||
if [ -n "\$VIRTUAL_ENV" ]
|
||||
|
|
@ -114,6 +132,50 @@ function _pyenv_virtualenv_hook --on-event fish_prompt;
|
|||
return \$ret
|
||||
end
|
||||
EOS
|
||||
else
|
||||
cat <<EOS
|
||||
function _pyenv_virtualenv_hook --on-event fish_prompt;
|
||||
set -l ret \$status
|
||||
if test "\$PYENV_VERSION" = "\$_PYENV_VH_VERSION" \\
|
||||
-a "\$VIRTUAL_ENV" = "\$_PYENV_VH_VENV"
|
||||
if test -n "\$PYENV_VERSION"
|
||||
return \$ret
|
||||
end
|
||||
if test "\$PWD" = "\$_PYENV_VH_PWD" \\
|
||||
-a "(stat ${_stat_fmt} \$_PYENV_VH_PATHS 2>/dev/null)" = "\$_PYENV_VH_MTIMES"
|
||||
return \$ret
|
||||
end
|
||||
end
|
||||
if [ -n "\$VIRTUAL_ENV" ]
|
||||
pyenv activate --quiet; or pyenv deactivate --quiet; or true
|
||||
else
|
||||
pyenv activate --quiet; or true
|
||||
end
|
||||
set -g _PYENV_VH_PWD "\$PWD"
|
||||
set -g _PYENV_VH_VERSION "\$PYENV_VERSION"
|
||||
set -g _PYENV_VH_VENV "\$VIRTUAL_ENV"
|
||||
set -l d "\$PWD"
|
||||
set -l _pvh_found_local 0
|
||||
set -g _PYENV_VH_PATHS
|
||||
while true
|
||||
if test -e "\$d/.python-version"; or test -L "\$d/.python-version"
|
||||
set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$d/.python-version"
|
||||
set _pvh_found_local 1
|
||||
break
|
||||
end
|
||||
set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$d"
|
||||
test "\$d" = "/"; and break
|
||||
set d (string replace -r '/[^/]*\$' '' -- "\$d")
|
||||
test -z "\$d"; and set d "/"
|
||||
end
|
||||
if test "\$_pvh_found_local" = "0"
|
||||
set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$PYENV_ROOT/version"
|
||||
end
|
||||
set -g _PYENV_VH_MTIMES (stat ${_stat_fmt} \$_PYENV_VH_PATHS 2>/dev/null)
|
||||
return \$ret
|
||||
end
|
||||
EOS
|
||||
fi
|
||||
;;
|
||||
ksh )
|
||||
cat <<EOS
|
||||
|
|
@ -128,7 +190,8 @@ EOS
|
|||
esac
|
||||
|
||||
if [[ "$shell" != "fish" ]]; then
|
||||
cat <<EOS
|
||||
if [ -n "$_has_version_hooks" ]; then
|
||||
cat <<EOS
|
||||
local ret=\$?
|
||||
if [ -n "\${VIRTUAL_ENV-}" ]; then
|
||||
eval "\$(pyenv sh-activate --quiet || pyenv sh-deactivate --quiet || true)" || true
|
||||
|
|
@ -138,6 +201,49 @@ if [[ "$shell" != "fish" ]]; then
|
|||
return \$ret
|
||||
};
|
||||
EOS
|
||||
else
|
||||
cat <<EOS
|
||||
local ret=\$?
|
||||
# Cache: env vars checked once, path list and stat rebuilt on miss only
|
||||
if [ "\${PYENV_VERSION-}" = "\${_PYENV_VH_VERSION-}" ] \\
|
||||
&& [ "\${VIRTUAL_ENV-}" = "\${_PYENV_VH_VENV-}" ]; then
|
||||
if [ -n "\${PYENV_VERSION-}" ]; then
|
||||
return \$ret
|
||||
fi
|
||||
if [ "\${PWD}" = "\${_PYENV_VH_PWD-}" ] \\
|
||||
&& [ "\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)" = "\${_PYENV_VH_MTIMES-}" ]; then
|
||||
return \$ret
|
||||
fi
|
||||
fi
|
||||
if [ -n "\${VIRTUAL_ENV-}" ]; then
|
||||
eval "\$(pyenv sh-activate --quiet || pyenv sh-deactivate --quiet || true)" || true
|
||||
else
|
||||
eval "\$(pyenv sh-activate --quiet || true)" || true
|
||||
fi
|
||||
_PYENV_VH_PWD="\${PWD}"
|
||||
_PYENV_VH_VERSION="\${PYENV_VERSION-}"
|
||||
_PYENV_VH_VENV="\${VIRTUAL_ENV-}"
|
||||
local _pvh_d="\${PWD}" _pvh_found_local=0
|
||||
_PYENV_VH_PATHS=()
|
||||
while :; do
|
||||
if [ -e "\${_pvh_d}/.python-version" ] || [ -L "\${_pvh_d}/.python-version" ]; then
|
||||
_PYENV_VH_PATHS+=("\${_pvh_d}/.python-version")
|
||||
_pvh_found_local=1
|
||||
break
|
||||
fi
|
||||
_PYENV_VH_PATHS+=("\${_pvh_d}")
|
||||
[ "\${_pvh_d}" = "/" ] && break
|
||||
_pvh_d="\${_pvh_d%/*}"
|
||||
[ -z "\${_pvh_d}" ] && _pvh_d="/"
|
||||
done
|
||||
if [ "\${_pvh_found_local}" = "0" ]; then
|
||||
_PYENV_VH_PATHS+=("\${PYENV_ROOT}/version")
|
||||
fi
|
||||
_PYENV_VH_MTIMES="\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)"
|
||||
return \$ret
|
||||
};
|
||||
EOS
|
||||
fi
|
||||
|
||||
case "$shell" in
|
||||
bash )
|
||||
|
|
|
|||
|
|
@ -54,11 +54,42 @@ export PATH="${TMP}/pyenv/plugins/pyenv-virtualenv/shims:\${PATH}";
|
|||
export PYENV_VIRTUALENV_INIT=1;
|
||||
_pyenv_virtualenv_hook() {
|
||||
local ret=\$?
|
||||
# Cache: env vars checked once, path list and stat rebuilt on miss only
|
||||
if [ "\${PYENV_VERSION-}" = "\${_PYENV_VH_VERSION-}" ] \\
|
||||
&& [ "\${VIRTUAL_ENV-}" = "\${_PYENV_VH_VENV-}" ]; then
|
||||
if [ -n "\${PYENV_VERSION-}" ]; then
|
||||
return \$ret
|
||||
fi
|
||||
if [ "\${PWD}" = "\${_PYENV_VH_PWD-}" ] \\
|
||||
&& [ "\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)" = "\${_PYENV_VH_MTIMES-}" ]; then
|
||||
return \$ret
|
||||
fi
|
||||
fi
|
||||
if [ -n "\${VIRTUAL_ENV-}" ]; then
|
||||
eval "\$(pyenv sh-activate --quiet || pyenv sh-deactivate --quiet || true)" || true
|
||||
else
|
||||
eval "\$(pyenv sh-activate --quiet || true)" || true
|
||||
fi
|
||||
_PYENV_VH_PWD="\${PWD}"
|
||||
_PYENV_VH_VERSION="\${PYENV_VERSION-}"
|
||||
_PYENV_VH_VENV="\${VIRTUAL_ENV-}"
|
||||
local _pvh_d="\${PWD}" _pvh_found_local=0
|
||||
_PYENV_VH_PATHS=()
|
||||
while :; do
|
||||
if [ -e "\${_pvh_d}/.python-version" ] || [ -L "\${_pvh_d}/.python-version" ]; then
|
||||
_PYENV_VH_PATHS+=("\${_pvh_d}/.python-version")
|
||||
_pvh_found_local=1
|
||||
break
|
||||
fi
|
||||
_PYENV_VH_PATHS+=("\${_pvh_d}")
|
||||
[ "\${_pvh_d}" = "/" ] && break
|
||||
_pvh_d="\${_pvh_d%/*}"
|
||||
[ -z "\${_pvh_d}" ] && _pvh_d="/"
|
||||
done
|
||||
if [ "\${_pvh_found_local}" = "0" ]; then
|
||||
_PYENV_VH_PATHS+=("\${PYENV_ROOT}/version")
|
||||
fi
|
||||
_PYENV_VH_MTIMES="\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)"
|
||||
return \$ret
|
||||
};
|
||||
if ! [[ "\${PROMPT_COMMAND-}" =~ _pyenv_virtualenv_hook ]]; then
|
||||
|
|
@ -78,11 +109,42 @@ set -gx PATH '${TMP}/pyenv/plugins/pyenv-virtualenv/shims' \$PATH;
|
|||
set -gx PYENV_VIRTUALENV_INIT 1;
|
||||
function _pyenv_virtualenv_hook --on-event fish_prompt;
|
||||
set -l ret \$status
|
||||
if test "\$PYENV_VERSION" = "\$_PYENV_VH_VERSION" \\
|
||||
-a "\$VIRTUAL_ENV" = "\$_PYENV_VH_VENV"
|
||||
if test -n "\$PYENV_VERSION"
|
||||
return \$ret
|
||||
end
|
||||
if test "\$PWD" = "\$_PYENV_VH_PWD" \\
|
||||
-a "(stat ${_stat_fmt} \$_PYENV_VH_PATHS 2>/dev/null)" = "\$_PYENV_VH_MTIMES"
|
||||
return \$ret
|
||||
end
|
||||
end
|
||||
if [ -n "\$VIRTUAL_ENV" ]
|
||||
pyenv activate --quiet; or pyenv deactivate --quiet; or true
|
||||
else
|
||||
pyenv activate --quiet; or true
|
||||
end
|
||||
set -g _PYENV_VH_PWD "\$PWD"
|
||||
set -g _PYENV_VH_VERSION "\$PYENV_VERSION"
|
||||
set -g _PYENV_VH_VENV "\$VIRTUAL_ENV"
|
||||
set -l d "\$PWD"
|
||||
set -l _pvh_found_local 0
|
||||
set -g _PYENV_VH_PATHS
|
||||
while true
|
||||
if test -e "\$d/.python-version"; or test -L "\$d/.python-version"
|
||||
set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$d/.python-version"
|
||||
set _pvh_found_local 1
|
||||
break
|
||||
end
|
||||
set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$d"
|
||||
test "\$d" = "/"; and break
|
||||
set d (string replace -r '/[^/]*\$' '' -- "\$d")
|
||||
test -z "\$d"; and set d "/"
|
||||
end
|
||||
if test "\$_pvh_found_local" = "0"
|
||||
set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$PYENV_ROOT/version"
|
||||
end
|
||||
set -g _PYENV_VH_MTIMES (stat ${_stat_fmt} \$_PYENV_VH_PATHS 2>/dev/null)
|
||||
return \$ret
|
||||
end
|
||||
EOS
|
||||
|
|
@ -97,11 +159,42 @@ export PATH="${TMP}/pyenv/plugins/pyenv-virtualenv/shims:\${PATH}";
|
|||
export PYENV_VIRTUALENV_INIT=1;
|
||||
_pyenv_virtualenv_hook() {
|
||||
local ret=\$?
|
||||
# Cache: env vars checked once, path list and stat rebuilt on miss only
|
||||
if [ "\${PYENV_VERSION-}" = "\${_PYENV_VH_VERSION-}" ] \\
|
||||
&& [ "\${VIRTUAL_ENV-}" = "\${_PYENV_VH_VENV-}" ]; then
|
||||
if [ -n "\${PYENV_VERSION-}" ]; then
|
||||
return \$ret
|
||||
fi
|
||||
if [ "\${PWD}" = "\${_PYENV_VH_PWD-}" ] \\
|
||||
&& [ "\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)" = "\${_PYENV_VH_MTIMES-}" ]; then
|
||||
return \$ret
|
||||
fi
|
||||
fi
|
||||
if [ -n "\${VIRTUAL_ENV-}" ]; then
|
||||
eval "\$(pyenv sh-activate --quiet || pyenv sh-deactivate --quiet || true)" || true
|
||||
else
|
||||
eval "\$(pyenv sh-activate --quiet || true)" || true
|
||||
fi
|
||||
_PYENV_VH_PWD="\${PWD}"
|
||||
_PYENV_VH_VERSION="\${PYENV_VERSION-}"
|
||||
_PYENV_VH_VENV="\${VIRTUAL_ENV-}"
|
||||
local _pvh_d="\${PWD}" _pvh_found_local=0
|
||||
_PYENV_VH_PATHS=()
|
||||
while :; do
|
||||
if [ -e "\${_pvh_d}/.python-version" ] || [ -L "\${_pvh_d}/.python-version" ]; then
|
||||
_PYENV_VH_PATHS+=("\${_pvh_d}/.python-version")
|
||||
_pvh_found_local=1
|
||||
break
|
||||
fi
|
||||
_PYENV_VH_PATHS+=("\${_pvh_d}")
|
||||
[ "\${_pvh_d}" = "/" ] && break
|
||||
_pvh_d="\${_pvh_d%/*}"
|
||||
[ -z "\${_pvh_d}" ] && _pvh_d="/"
|
||||
done
|
||||
if [ "\${_pvh_found_local}" = "0" ]; then
|
||||
_PYENV_VH_PATHS+=("\${PYENV_ROOT}/version")
|
||||
fi
|
||||
_PYENV_VH_MTIMES="\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)"
|
||||
return \$ret
|
||||
};
|
||||
typeset -g -a precmd_functions
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
export TMP="$BATS_TEST_DIRNAME/tmp"
|
||||
|
||||
# Detect stat format for mtime: GNU uses -c %Y, BSD uses -f %m
|
||||
# Must match the detection in bin/pyenv-virtualenv-init
|
||||
if stat -L -c %Y / >/dev/null 2>&1; then
|
||||
_stat_fmt="-L -c %Y"
|
||||
else
|
||||
_stat_fmt="-L -f %m"
|
||||
fi
|
||||
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
||||
|
||||
PATH=/usr/bin:/usr/sbin:/bin:/sbin
|
||||
|
|
|
|||
Loading…
Reference in a new issue