Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions bin/pyenv-virtualenv-init
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,42 @@ fish )
cat <<EOS
function _pyenv_virtualenv_hook --on-event fish_prompt;
set -l ret \$status
if test "\$PWD" = "\$_PYENV_VH_PWD" \\
-a "\$PYENV_VERSION" = "\$_PYENV_VH_VERSION" \\
-a "\$VIRTUAL_ENV" = "\$_PYENV_VH_VENV" \\
-a -f "\$_PYENV_VH_MARKER"
if test -n "\$PYENV_VERSION"
return \$ret
end
set -l d "\$PWD"
set -l stale 0
set -l found 0
while true
if test -f "\$d/.python-version"
set found 1
test "\$d/.python-version" -nt "\$_PYENV_VH_MARKER"; and set stale 1
break
end
test "\$d" -nt "\$_PYENV_VH_MARKER"; and begin; set stale 1; break; end
test "\$d" = "/"; and break
set d (string replace -r '/[^/]*\$' '' -- "\$d")
test -z "\$d"; and set d "/"
end
if test \$stale = 0 -a \$found = 0
test -f "\$PYENV_ROOT/version"; and test "\$PYENV_ROOT/version" -nt "\$_PYENV_VH_MARKER"; and set stale 1
end
test \$stale = 0; and return \$ret
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 -g _PYENV_VH_MARKER "\$PYENV_ROOT/.pyenv-vh-marker-\$fish_pid"
Copy link
Member

@native-api native-api Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are we going to maintain these marker files? We have to delete them at some point, otherwise they'll keep accumulating. I don't have an idea of a reliable way atm, that's why I wrote "somehow".

Is calling stat really that bad? It accepts multiple arguments so a single call would be enough regardless of how many entries we have to check.
Then, it's easy to check for changes by simply comparing outputs:

LOCAL_VERSION_PATHS=<paths to .python-version and dirs if any; can be an array>
SAVED_MTIMES="$(stat -c %Y $LOCAL_VERSION_PATHS)"
<...>
if [[ "$(stat -c %Y $LOCAL_VERSION_PATHS)" != "$SAVED_MTIMES" ]]; then <the cache is stale>; fi

command touch "\$_PYENV_VH_MARKER"
return \$ret
end
EOS
Expand All @@ -130,11 +161,42 @@ esac
if [[ "$shell" != "fish" ]]; then
cat <<EOS
local ret=\$?
if [ "\${PWD}" = "\${_PYENV_VH_PWD-}" ] \\
&& [ "\${PYENV_VERSION-}" = "\${_PYENV_VH_VERSION-}" ] \\
&& [ "\${VIRTUAL_ENV-}" = "\${_PYENV_VH_VENV-}" ] \\
&& [ -f "\${_PYENV_VH_MARKER-}" ]; then
if [ -n "\${PYENV_VERSION-}" ]; then
return \$ret
fi
local _pvh_d="\${PWD}" _pvh_stale=0 _pvh_found=0
while :; do
if [ -f "\${_pvh_d}/.python-version" ]; then
_pvh_found=1
[ "\${_pvh_d}/.python-version" -nt "\${_PYENV_VH_MARKER}" ] && _pvh_stale=1
break
fi
[ "\${_pvh_d}" -nt "\${_PYENV_VH_MARKER}" ] && { _pvh_stale=1; break; }
[ "\${_pvh_d}" = "/" ] && break
_pvh_d="\${_pvh_d%/*}"
[ -z "\${_pvh_d}" ] && _pvh_d="/"
done
if [ "\${_pvh_stale}" = 0 ] && [ "\${_pvh_found}" = 0 ]; then
[ -f "\${PYENV_ROOT}/version" ] \\
&& [ "\${PYENV_ROOT}/version" -nt "\${_PYENV_VH_MARKER}" ] \\
&& _pvh_stale=1
fi
[ "\${_pvh_stale}" = 0 ] && return \$ret
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-}"
_PYENV_VH_MARKER="\${PYENV_ROOT}/.pyenv-vh-marker-\$\$"
: > "\${_PYENV_VH_MARKER}"
return \$ret
};
EOS
Expand Down
93 changes: 93 additions & 0 deletions test/init.bats
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,42 @@ export PATH="${TMP}/pyenv/plugins/pyenv-virtualenv/shims:\${PATH}";
export PYENV_VIRTUALENV_INIT=1;
_pyenv_virtualenv_hook() {
local ret=\$?
if [ "\${PWD}" = "\${_PYENV_VH_PWD-}" ] \\
&& [ "\${PYENV_VERSION-}" = "\${_PYENV_VH_VERSION-}" ] \\
&& [ "\${VIRTUAL_ENV-}" = "\${_PYENV_VH_VENV-}" ] \\
&& [ -f "\${_PYENV_VH_MARKER-}" ]; then
if [ -n "\${PYENV_VERSION-}" ]; then
return \$ret
fi
local _pvh_d="\${PWD}" _pvh_stale=0 _pvh_found=0
while :; do
if [ -f "\${_pvh_d}/.python-version" ]; then
_pvh_found=1
[ "\${_pvh_d}/.python-version" -nt "\${_PYENV_VH_MARKER}" ] && _pvh_stale=1
break
fi
[ "\${_pvh_d}" -nt "\${_PYENV_VH_MARKER}" ] && { _pvh_stale=1; break; }
[ "\${_pvh_d}" = "/" ] && break
_pvh_d="\${_pvh_d%/*}"
[ -z "\${_pvh_d}" ] && _pvh_d="/"
done
if [ "\${_pvh_stale}" = 0 ] && [ "\${_pvh_found}" = 0 ]; then
[ -f "\${PYENV_ROOT}/version" ] \\
&& [ "\${PYENV_ROOT}/version" -nt "\${_PYENV_VH_MARKER}" ] \\
&& _pvh_stale=1
fi
[ "\${_pvh_stale}" = 0 ] && return \$ret
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-}"
_PYENV_VH_MARKER="\${PYENV_ROOT}/.pyenv-vh-marker-\$\$"
: > "\${_PYENV_VH_MARKER}"
return \$ret
};
if ! [[ "\${PROMPT_COMMAND-}" =~ _pyenv_virtualenv_hook ]]; then
Expand All @@ -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 "\$PWD" = "\$_PYENV_VH_PWD" \\
-a "\$PYENV_VERSION" = "\$_PYENV_VH_VERSION" \\
-a "\$VIRTUAL_ENV" = "\$_PYENV_VH_VENV" \\
-a -f "\$_PYENV_VH_MARKER"
if test -n "\$PYENV_VERSION"
return \$ret
end
set -l d "\$PWD"
set -l stale 0
set -l found 0
while true
if test -f "\$d/.python-version"
set found 1
test "\$d/.python-version" -nt "\$_PYENV_VH_MARKER"; and set stale 1
break
end
test "\$d" -nt "\$_PYENV_VH_MARKER"; and begin; set stale 1; break; end
test "\$d" = "/"; and break
set d (string replace -r '/[^/]*\$' '' -- "\$d")
test -z "\$d"; and set d "/"
end
if test \$stale = 0 -a \$found = 0
test -f "\$PYENV_ROOT/version"; and test "\$PYENV_ROOT/version" -nt "\$_PYENV_VH_MARKER"; and set stale 1
end
test \$stale = 0; and return \$ret
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 -g _PYENV_VH_MARKER "\$PYENV_ROOT/.pyenv-vh-marker-\$fish_pid"
command touch "\$_PYENV_VH_MARKER"
return \$ret
end
EOS
Expand All @@ -97,11 +159,42 @@ export PATH="${TMP}/pyenv/plugins/pyenv-virtualenv/shims:\${PATH}";
export PYENV_VIRTUALENV_INIT=1;
_pyenv_virtualenv_hook() {
local ret=\$?
if [ "\${PWD}" = "\${_PYENV_VH_PWD-}" ] \\
&& [ "\${PYENV_VERSION-}" = "\${_PYENV_VH_VERSION-}" ] \\
&& [ "\${VIRTUAL_ENV-}" = "\${_PYENV_VH_VENV-}" ] \\
&& [ -f "\${_PYENV_VH_MARKER-}" ]; then
if [ -n "\${PYENV_VERSION-}" ]; then
return \$ret
fi
local _pvh_d="\${PWD}" _pvh_stale=0 _pvh_found=0
while :; do
if [ -f "\${_pvh_d}/.python-version" ]; then
_pvh_found=1
[ "\${_pvh_d}/.python-version" -nt "\${_PYENV_VH_MARKER}" ] && _pvh_stale=1
break
fi
[ "\${_pvh_d}" -nt "\${_PYENV_VH_MARKER}" ] && { _pvh_stale=1; break; }
[ "\${_pvh_d}" = "/" ] && break
_pvh_d="\${_pvh_d%/*}"
[ -z "\${_pvh_d}" ] && _pvh_d="/"
done
if [ "\${_pvh_stale}" = 0 ] && [ "\${_pvh_found}" = 0 ]; then
[ -f "\${PYENV_ROOT}/version" ] \\
&& [ "\${PYENV_ROOT}/version" -nt "\${_PYENV_VH_MARKER}" ] \\
&& _pvh_stale=1
fi
[ "\${_pvh_stale}" = 0 ] && return \$ret
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-}"
_PYENV_VH_MARKER="\${PYENV_ROOT}/.pyenv-vh-marker-\$\$"
: > "\${_PYENV_VH_MARKER}"
return \$ret
};
typeset -g -a precmd_functions
Expand Down
Loading