Skip to content

Improve performance and output of pyenv virtualenvs #502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion bin/pyenv-activate
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ set -e
# Provide pyenv completions
if [ "$1" = "--complete" ]; then
echo --unset
exec pyenv-virtualenvs --bare
exec pyenv-virtualenvs --bare --only-aliases
Copy link
Member

Choose a reason for hiding this comment

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

This is a design change -- so this should be discussed separately. Envs are supposed to be accessible under both full name and alias. I'm not sure why that is; aliases are internally called "compat", indicating that they were added for compatibility with... something.

Copy link
Author

Choose a reason for hiding this comment

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

I'll remove that change for now and we can discuss elsewhere. What's the best place to have a design discussion?

Copy link
Member

Choose a reason for hiding this comment

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

What's the best place to have a design discussion?

Discussions.

fi

{ printf "\x1B[31;1m"
Expand Down
2 changes: 1 addition & 1 deletion bin/pyenv-sh-activate
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ while [ $# -gt 0 ]; do
"--complete" )
# Provide pyenv completions
echo --unset
exec pyenv-virtualenvs --bare
exec pyenv-virtualenvs --bare --only-aliases
;;
"-f" | "--force" )
FORCE=1
Expand Down
2 changes: 1 addition & 1 deletion bin/pyenv-virtualenv
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fi

# Provide pyenv completions
if [ "$1" = "--complete" ]; then
exec pyenv-versions --bare
exec pyenv-versions --bare --skip-envs --skip-aliases
fi

unset PIP_REQUIRE_VENV
Expand Down
141 changes: 106 additions & 35 deletions bin/pyenv-virtualenvs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/usr/bin/env bash
#
# Summary: List all Python virtualenvs found in `$PYENV_ROOT/versions/*'.
# Usage: pyenv virtualenvs [--bare] [--skip-aliases]
# Usage: pyenv virtualenvs [--bare] [--skip-aliases] [--only-aliases]
#
# List all virtualenvs found in `$PYENV_ROOT/versions/*' and its `$PYENV_ROOT/versions/envs/*'.

set -e
[ -n "$PYENV_DEBUG" ] && set -x
if [ -L "${BASH_SOURCE}" ]; then
if [ -L "${BASH_SOURCE[0]}" ]; then
READLINK=$(type -p greadlink readlink | head -1)
if [ -z "$READLINK" ]; then
echo "pyenv: cannot find readlink - are you missing GNU coreutils?" >&2
Expand All @@ -16,12 +16,12 @@ if [ -L "${BASH_SOURCE}" ]; then
resolve_link() {
$READLINK -f "$1"
}
script_path=$(resolve_link ${BASH_SOURCE})
script_path=$(resolve_link "${BASH_SOURCE[0]}")
else
script_path=${BASH_SOURCE}
script_path="${BASH_SOURCE[0]}"
fi

. ${script_path%/*}/../libexec/pyenv-virtualenv-realpath
. "${script_path%/*}"/../libexec/pyenv-virtualenv-realpath

if [ -z "$PYENV_ROOT" ]; then
PYENV_ROOT="${HOME}/.pyenv"
Expand All @@ -34,10 +34,11 @@ for arg; do
case "$arg" in
--complete )
echo --bare
echo --skip-aliases
echo --only-aliases
exit ;;
--bare ) bare=1 ;;
--skip-aliases ) skip_aliases=1 ;;
--only-aliases ) only_aliases=1 ;;
* )
pyenv-help --usage virtualenvs >&2
exit 1
Expand All @@ -47,24 +48,60 @@ done

versions_dir="${PYENV_ROOT}/versions"

if [ -d "$versions_dir" ]; then
versions_dir="$(realpath "$versions_dir")"
if ! enable -f "${BASH_SOURCE%/*}"/pyenv-realpath.dylib realpath 2>/dev/null; then
if [ -n "$PYENV_NATIVE_EXT" ]; then
echo "pyenv: failed to load \`realpath' builtin" >&2
exit 1
fi

READLINK=$(type -P readlink)
if [ -z "$READLINK" ]; then
echo "pyenv: cannot find readlink - are you missing GNU coreutils?" >&2
exit 1
fi

resolve_link() {
$READLINK "$1"
}

realpath() {
local path="$1"
local name

# Use a subshell to avoid changing the current path
(
while [ -n "$path" ]; do
name="${path##*/}"
[ "$name" = "$path" ] || cd "${path%/*}"
path="$(resolve_link "$name" || true)"
done

echo "${PWD}/$name"
)
}
fi

if [ -n "$bare" ]; then
hit_prefix=""
miss_prefix=""
if ((BASH_VERSINFO[0] > 3)); then
declare -A current_versions
else
current_versions=()
unset print_origin
fi
if [ -n "$bare" ]; then
include_system=""
else
hit_prefix="* "
miss_prefix=" "
OLDIFS="$IFS"
IFS=: current_versions=($(pyenv-version-name || true))
IFS=:
if ((BASH_VERSINFO[0] > 3)); then
for i in $(pyenv-version-name || true); do
current_versions["$i"]="1"
done
else
read -r -a current_versions <<< "$(pyenv-version-name || true)"
fi
IFS="$OLDIFS"
print_origin="1"
include_system=""
include_system="1"
fi

num_versions=0
Expand All @@ -82,39 +119,73 @@ exists() {
}

print_version() {
if exists "$1" "${current_versions[@]}"; then
echo "${hit_prefix}${1}${print_origin+$2}"
local version="${1:?}"
if [[ -n $bare ]]; then
echo "$version"
return
fi
local path="${2:?}"
if [[ -L "$path" ]]; then
# Only resolve the link itself for printing, do not resolve further.
# Doing otherwise would misinform the user of what the link contains.
version_repr="$version --> $(readlink "$path")"
else
echo "${miss_prefix}${1}${print_origin+$2}"
version_repr="$version"
fi
if [[ ${BASH_VERSINFO[0]} -ge 4 && ${current_versions["$1"]} ]]; then
echo "${hit_prefix}${version_repr} (set by $(pyenv-version-origin))"
elif (( BASH_VERSINFO[0] <= 3 )) && exists "$1" "${current_versions[@]}"; then
echo "${hit_prefix}${version_repr} (set by $(pyenv-version-origin))"
else
echo "${miss_prefix}${version_repr}"
fi
num_versions=$((num_versions + 1))
}

shopt -s dotglob
shopt -s nullglob
for path in "$versions_dir"/*; do
if [ -d "$path" ]; then
if [ -n "$skip_aliases" ] && [ -L "$path" ]; then
target="$(realpath "$path")"
[ "${target%/*/envs/*}" != "$versions_dir" ] || continue
version_dir_entries=("$versions_dir"/*)
venv_dir_entries=("$versions_dir"/*/envs/*)

if sort --version-sort </dev/null >/dev/null 2>&1; then
# system sort supports version sorting
OLDIFS="$IFS"
IFS='||'

read -r -a version_dir_entries <<< "$(
printf "%s||" "${version_dir_entries[@]}" |
sort --version-sort
)"

read -r -a venv_dir_entries <<< "$(
printf "%s||" "${venv_dir_entries[@]}" |
sort --version-sort
)"

IFS="$OLDIFS"
fi

if [ -z "$only_aliases" ]; then
for env_path in "${venv_dir_entries[@]}"; do
if [ -d "${env_path}" ]; then
print_version "${env_path#"${PYENV_ROOT}"/versions/}" "${env_path}"
fi
virtualenv_prefix="$(pyenv-virtualenv-prefix "${path##*/}" 2>/dev/null || true)"
if [ -d "${virtualenv_prefix}" ]; then
print_version "${path##*/}" " (created from ${virtualenv_prefix})"
done
fi

if [ -z "$skip_aliases" ]; then
for env_path in "${version_dir_entries[@]}"; do
if [ -d "${env_path}" ] && [ -L "${env_path}" ]; then
print_version "${env_path#"${PYENV_ROOT}"/versions/}" "${env_path}"
fi
for venv_path in "${path}/envs/"*; do
venv="${path##*/}/envs/${venv_path##*/}"
virtualenv_prefix="$(pyenv-virtualenv-prefix "${venv}" 2>/dev/null || true)"
if [ -d "${virtualenv_prefix}" ]; then
print_version "${venv}" " (created from ${virtualenv_prefix})"
fi
done
fi
done
done
fi
Copy link
Member

Choose a reason for hiding this comment

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

What happens with conda installations? They are currently listed, now they seem to stop being so.
Not sure why they are listed and if that's intended. Technically, each one contains a "base" environment -- but the same could be said about any other Python installation.

Copy link
Author

Choose a reason for hiding this comment

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

I don't know anything about or use conda, so I omitted those. 😊
I will work on adding that back in as they should be listed in the output.

Copy link
Author

Choose a reason for hiding this comment

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

Reading through the previous code, it seems like the intent was to look for certain indicator files and if they are present, list the directory as a virtual environment. I don't really know if that's a good heuristic or not.

Calling pyenv-virtualenv-prefix was what caused the program to take so long to run. If we think this behavior should remain, I'll have to reimplement this functionality using a more efficient approach.

Copy link
Author

Choose a reason for hiding this comment

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

Hmm, the changes in this PR may actually be doing a better job with this than the current code.

I installed miniconda3 and created a new env.

pyenv install miniconda3-latest
pyenv shell miniconda3-latest
conda create -n my_conda_env

I only see my_conda_env in the second output.

Current output running master
> pyenv virtualenvs
  3.10.2/envs/pyls-3.10.2 (created from /Users/sdoran/.pyenv/versions/3.10.2)
  3.10.3/envs/ansible-2.12-3.10.3 (created from /Users/sdoran/.pyenv/versions/3.10.3)
  3.10.3/envs/boto-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.3)
  3.10.3/envs/lexicon-3.10.3 (created from /Users/sdoran/.pyenv/versions/3.10.3)
  3.10.4/envs/ansible-2.12-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  3.10.4/envs/ansible-dev-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  3.10.4/envs/flake8-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  3.10.4/envs/jello-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  3.10.4/envs/jsbeautifier-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  3.10.4/envs/youtube-dl-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  3.10.6/envs/ipython-3.10.6 (created from /Users/sdoran/.pyenv/versions/3.10.6)
  3.10.6/envs/shiv-3.10.6 (created from /Users/sdoran/.pyenv/versions/3.10.6)
  3.10.6/envs/speedtest-cli-3.10.6 (created from /Users/sdoran/.pyenv/versions/3.10.6)
  3.11.1/envs/ansible-2.14-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  3.11.1/envs/ipython-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  3.11.1/envs/titlecase-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  3.11.1/envs/uptime-kuma-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  3.11.3/envs/ipython-3.11.3 (created from /Users/sdoran/.pyenv/versions/3.11.3)
  3.11.3/envs/tasmota (created from /Users/sdoran/.pyenv/versions/3.11.3)
  3.11.7/envs/tasmota-3.11.7 (created from /Users/sdoran/.pyenv/versions/3.11.7)
  3.12.0/envs/ansible-2.16-3.12.0 (created from /Users/sdoran/.pyenv/versions/3.12.0)
  3.12.0/envs/ipython-3.12.0 (created from /Users/sdoran/.pyenv/versions/3.12.0)
  3.12.2/envs/borg (created from /Users/sdoran/.pyenv/versions/3.12.2)
  3.12.2/envs/yt-dlp (created from /Users/sdoran/.pyenv/versions/3.12.2)
  3.12.4/envs/qrcode (created from /Users/sdoran/.pyenv/versions/3.12.4)
  3.12.8/envs/ansible-lint (created from /Users/sdoran/.pyenv/versions/3.12.8)
  3.12.8/envs/cowsay (created from /Users/sdoran/.pyenv/versions/3.12.8)
  3.12.8/envs/pyright-3.12.8 (created from /Users/sdoran/.pyenv/versions/3.12.8)
  3.12.8/envs/ruff-3.12.8 (created from /Users/sdoran/.pyenv/versions/3.12.8)
  3.13.1/envs/asciinema (created from /Users/sdoran/.pyenv/versions/3.13.1)
  3.13.1/envs/diceware (created from /Users/sdoran/.pyenv/versions/3.13.1)
  3.13.1/envs/ipython-3.13.1 (created from /Users/sdoran/.pyenv/versions/3.13.1)
  3.6.15/envs/ipython-3.6.15 (created from /Users/sdoran/.pyenv/versions/3.6.15)
  3.7.12/envs/appomni-dev-3.7.12 (created from /Users/sdoran/.pyenv/versions/3.7.12)
  3.7.12/envs/ipython-3.7.12 (created from /Users/sdoran/.pyenv/versions/3.7.12)
  3.8.12/envs/ipython-3.8.12 (created from /Users/sdoran/.pyenv/versions/3.8.12)
  3.9.9/envs/ipython-3.9.9 (created from /Users/sdoran/.pyenv/versions/3.9.9)
  ansible-2.12-3.10.3 (created from /Users/sdoran/.pyenv/versions/3.10.3)
  ansible-2.12-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  ansible-2.14-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  ansible-2.16-3.12.0 (created from /Users/sdoran/.pyenv/versions/3.12.0)
  ansible-dev-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  ansible-lint (created from /Users/sdoran/.pyenv/versions/3.12.8)
  appomni-dev-3.7.12 (created from /Users/sdoran/.pyenv/versions/3.7.12)
  asciinema (created from /Users/sdoran/.pyenv/versions/3.13.1)
  borg (created from /Users/sdoran/.pyenv/versions/3.12.2)
  boto-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.3)
  cowsay (created from /Users/sdoran/.pyenv/versions/3.12.8)
  diceware (created from /Users/sdoran/.pyenv/versions/3.13.1)
  flake8-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  ipython-3.10.6 (created from /Users/sdoran/.pyenv/versions/3.10.6)
  ipython-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  ipython-3.11.3 (created from /Users/sdoran/.pyenv/versions/3.11.3)
  ipython-3.12.0 (created from /Users/sdoran/.pyenv/versions/3.12.0)
  ipython-3.13.1 (created from /Users/sdoran/.pyenv/versions/3.13.1)
  ipython-3.6.15 (created from /Users/sdoran/.pyenv/versions/3.6.15)
  ipython-3.7.12 (created from /Users/sdoran/.pyenv/versions/3.7.12)
  ipython-3.8.12 (created from /Users/sdoran/.pyenv/versions/3.8.12)
  ipython-3.9.9 (created from /Users/sdoran/.pyenv/versions/3.9.9)
  jello-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  jsbeautifier-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  lexicon-3.10.3 (created from /Users/sdoran/.pyenv/versions/3.10.3)
  miniconda3-latest (created from /Users/sdoran/.pyenv/versions/miniconda3-latest)
  pyls-3.10.2 (created from /Users/sdoran/.pyenv/versions/3.10.2)
  pyright-3.12.8 (created from /Users/sdoran/.pyenv/versions/3.12.8)
  qrcode (created from /Users/sdoran/.pyenv/versions/3.12.4)
  ruff-3.12.8 (created from /Users/sdoran/.pyenv/versions/3.12.8)
  shiv-3.10.6 (created from /Users/sdoran/.pyenv/versions/3.10.6)
  speedtest-cli-3.10.6 (created from /Users/sdoran/.pyenv/versions/3.10.6)
  tasmota (created from /Users/sdoran/.pyenv/versions/3.11.3)
  tasmota-3.11.7 (created from /Users/sdoran/.pyenv/versions/3.11.7)
  titlecase-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  uptime-kuma-3.11.1 (created from /Users/sdoran/.pyenv/versions/3.11.1)
  youtube-dl-3.10.4 (created from /Users/sdoran/.pyenv/versions/3.10.4)
  yt-dlp (created from /Users/sdoran/.pyenv/versions/3.12.2)
New output from this branch
  3.10.2/envs/pyls-3.10.2
  3.10.3/envs/ansible-2.12-3.10.3
  3.10.3/envs/boto-3.10.4
  3.10.3/envs/lexicon-3.10.3
  3.10.4/envs/ansible-2.12-3.10.4
  3.10.4/envs/ansible-dev-3.10.4
  3.10.4/envs/flake8-3.10.4
  3.10.4/envs/jello-3.10.4
  3.10.4/envs/jsbeautifier-3.10.4
  3.10.4/envs/youtube-dl-3.10.4
  3.10.6/envs/ipython-3.10.6
  3.10.6/envs/shiv-3.10.6
  3.10.6/envs/speedtest-cli-3.10.6
  3.11.1/envs/ansible-2.14-3.11.1
  3.11.1/envs/ipython-3.11.1
  3.11.1/envs/titlecase-3.11.1
  3.11.1/envs/uptime-kuma-3.11.1
  3.11.3/envs/ipython-3.11.3
  3.11.3/envs/tasmota
  3.11.7/envs/tasmota-3.11.7
  3.12.0/envs/ansible-2.16-3.12.0
  3.12.0/envs/ipython-3.12.0
  3.12.2/envs/borg
  3.12.2/envs/yt-dlp
  3.12.4/envs/qrcode
  3.12.8/envs/ansible-lint
  3.12.8/envs/cowsay
  3.12.8/envs/pyright-3.12.8
  3.12.8/envs/ruff-3.12.8
  3.13.1/envs/asciinema
  3.13.1/envs/diceware
  3.13.1/envs/ipython-3.13.1
  3.6.15/envs/ipython-3.6.15
  3.7.12/envs/appomni-dev-3.7.12
  3.7.12/envs/ipython-3.7.12
  3.8.12/envs/ipython-3.8.12
  3.9.9/envs/ipython-3.9.9
  miniconda3-latest/envs/my_conda_env
  ansible-2.12-3.10.3 --> /Users/sdoran/.pyenv/versions/3.10.3/envs/ansible-2.12-3.10.3
  ansible-2.12-3.10.4 --> /Users/sdoran/.pyenv/versions/3.10.4/envs/ansible-2.12-3.10.4
  ansible-2.14-3.11.1 --> /Users/sdoran/.pyenv/versions/3.11.1/envs/ansible-2.14-3.11.1
  ansible-2.16-3.12.0 --> /Users/sdoran/.pyenv/versions/3.12.0/envs/ansible-2.16-3.12.0
  ansible-dev-3.10.4 --> /Users/sdoran/.pyenv/versions/3.10.4/envs/ansible-dev-3.10.4
  ansible-lint --> /Users/sdoran/.pyenv/versions/3.12.8/envs/ansible-lint
  appomni-dev-3.7.12 --> /Users/sdoran/.pyenv/versions/3.7.12/envs/appomni-dev-3.7.12
  asciinema --> /Users/sdoran/.pyenv/versions/3.13.1/envs/asciinema
  borg --> /Users/sdoran/.pyenv/versions/3.12.2/envs/borg
  boto-3.10.4 --> /Users/sdoran/.pyenv/versions/3.10.3/envs/boto-3.10.4
  cowsay --> /Users/sdoran/.pyenv/versions/3.12.8/envs/cowsay
  diceware --> /Users/sdoran/.pyenv/versions/3.13.1/envs/diceware
  flake8-3.10.4 --> /Users/sdoran/.pyenv/versions/3.10.4/envs/flake8-3.10.4
  ipython-3.10.6 --> /Users/sdoran/.pyenv/versions/3.10.6/envs/ipython-3.10.6
  ipython-3.11.1 --> /Users/sdoran/.pyenv/versions/3.11.1/envs/ipython-3.11.1
  ipython-3.11.3 --> /Users/sdoran/.pyenv/versions/3.11.3/envs/ipython-3.11.3
  ipython-3.12.0 --> /Users/sdoran/.pyenv/versions/3.12.0/envs/ipython-3.12.0
  ipython-3.13.1 --> /Users/sdoran/.pyenv/versions/3.13.1/envs/ipython-3.13.1
  ipython-3.6.15 --> /Users/sdoran/.pyenv/versions/3.6.15/envs/ipython-3.6.15
  ipython-3.7.12 --> /Users/sdoran/.pyenv/versions/3.7.12/envs/ipython-3.7.12
  ipython-3.8.12 --> /Users/sdoran/.pyenv/versions/3.8.12/envs/ipython-3.8.12
  ipython-3.9.9 --> /Users/sdoran/.pyenv/versions/3.9.9/envs/ipython-3.9.9
  jello-3.10.4 --> /Users/sdoran/.pyenv/versions/3.10.4/envs/jello-3.10.4
  jsbeautifier-3.10.4 --> /Users/sdoran/.pyenv/versions/3.10.4/envs/jsbeautifier-3.10.4
  lexicon-3.10.3 --> /Users/sdoran/.pyenv/versions/3.10.3/envs/lexicon-3.10.3
  pyls-3.10.2 --> /Users/sdoran/.pyenv/versions/3.10.2/envs/pyls-3.10.2
  pyright-3.12.8 --> /Users/sdoran/.pyenv/versions/3.12.8/envs/pyright-3.12.8
  qrcode --> /Users/sdoran/.pyenv/versions/3.12.4/envs/qrcode
  ruff-3.12.8 --> /Users/sdoran/.pyenv/versions/3.12.8/envs/ruff-3.12.8
  shiv-3.10.6 --> /Users/sdoran/.pyenv/versions/3.10.6/envs/shiv-3.10.6
  speedtest-cli-3.10.6 --> /Users/sdoran/.pyenv/versions/3.10.6/envs/speedtest-cli-3.10.6
  tasmota --> /Users/sdoran/.pyenv/versions/3.11.3/envs/tasmota
  tasmota-3.11.7 --> /Users/sdoran/.pyenv/versions/3.11.7/envs/tasmota-3.11.7
  titlecase-3.11.1 --> /Users/sdoran/.pyenv/versions/3.11.1/envs/titlecase-3.11.1
  uptime-kuma-3.11.1 --> /Users/sdoran/.pyenv/versions/3.11.1/envs/uptime-kuma-3.11.1
  youtube-dl-3.10.4 --> /Users/sdoran/.pyenv/versions/3.10.4/envs/youtube-dl-3.10.4
  yt-dlp --> /Users/sdoran/.pyenv/versions/3.12.2/envs/yt-dlp


shopt -u dotglob
shopt -u nullglob

if [ "$num_versions" -eq 0 ] && [ -n "$include_system" ]; then
echo "Warning: no Python virtualenv detected on the system" >&2
exit 1
fi

39 changes: 22 additions & 17 deletions test/virtualenvs.bats
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ load test_helper

setup() {
export PYENV_ROOT="${TMP}/pyenv"
mkdir -p "${PYENV_ROOT}/versions/2.7.6"
mkdir -p "${PYENV_ROOT}/versions/3.3.3"
mkdir -p "${PYENV_ROOT}/versions/venv27"
mkdir -p "${PYENV_ROOT}/versions/venv33"
mkdir -p "${PYENV_ROOT}/versions/2.7.6/envs/venv27"
mkdir -p "${PYENV_ROOT}/versions/3.3.3/envs/venv33"
ln -s "venv27" "${PYENV_ROOT}/versions/venv27"
ln -s "venv33" "${PYENV_ROOT}/versions/venv33"
Comment on lines +9 to +10
Copy link
Member

Choose a reason for hiding this comment

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

AFAICS the links are created in the current directory. That doesn't seem correct.
There's a dedicated function to create aliases.

}

create_alias() {
mkdir -p "${PYENV_ROOT}/versions"
ln -s "$2" "${PYENV_ROOT}/versions/$1"
}

@test "list virtual environments only" {
Expand All @@ -21,40 +26,40 @@ setup() {

assert_success
assert_output <<OUT
venv27 (created from ${PYENV_ROOT}/versions/2.7.6)
venv33 (created from ${PYENV_ROOT}/versions/3.3.3)
2.7.6/envs/venv27
3.3.3/envs/venv33
OUT

unstub pyenv-version-name
unstub pyenv-virtualenv-prefix
# unstub pyenv-version-name
# unstub pyenv-virtualenv-prefix
}

@test "list virtual environments with hit prefix" {
stub pyenv-version-name ": echo venv33"
stub pyenv-virtualenv-prefix "2.7.6 : false"
stub pyenv-virtualenv-prefix "3.3.3 : false"
stub pyenv-virtualenv-prefix "venv27 : echo \"/usr\""
stub pyenv-virtualenv-prefix "venv33 : echo \"/usr\""
stub pyenv-virtualenv-prefix "venv27 : echo \"${PYENV_ROOT}/versions/2.7.6\""
stub pyenv-virtualenv-prefix "venv33 : echo \"${PYENV_ROOT}/versions/3.3.3\""
Comment on lines +41 to +42
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if this change improves anything. As a rule of thumb, one should not change test cases unless you've changed the behavior in them -- to avoid accidentally losing test cover.


run pyenv-virtualenvs

assert_success
assert_output <<OUT
venv27 (created from /usr)
* venv33 (created from /usr)
2.7.6/envs/venv27
* 3.3.3/envs/venv33
Comment on lines +48 to +49
Copy link
Member

Choose a reason for hiding this comment

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

Diagnosed this failure. The logic does not equate "xxx" with "yyy/envs/xxx". If you set current version to "venv33", the alias will be the highlighted entry, not the full name.

To diagnose stuff in tests, I set PS4 to the long value from the launcher and run the command with PYENV_DEBUG=1. Then make sure there's a command later that prints its output in some form (in this case, assert_output failure handling logic).

OUT

unstub pyenv-version-name
unstub pyenv-virtualenv-prefix
# unstub pyenv-version-name
Copy link
Member

Choose a reason for hiding this comment

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

unstub checcks if the specific stubbed executable was called exactly according to the sequence set up with stub calls. If not, it fails and prints the plan and the fact.
The implementation is in test_helper.bash and the stubs/stub script which the former calls.

# unstub pyenv-virtualenv-prefix
}

@test "list virtual environments with --bare" {
stub pyenv-virtualenv-prefix "2.7.6 : false"
stub pyenv-virtualenv-prefix "3.3.3 : false"
stub pyenv-virtualenv-prefix "venv27 : echo \"/usr\""
stub pyenv-virtualenv-prefix "venv33 : echo \"/usr\""
stub pyenv-virtualenv-prefix "venv27 : echo \"${PYENV_ROOT}/versions/2.7.6\""
stub pyenv-virtualenv-prefix "venv33 : echo \"${PYENV_ROOT}/versions/3.3.3\""

run pyenv-virtualenvs --bare
run pyenv-virtualenvs --bare --only-aliases

assert_success
assert_output <<OUT
Expand Down
Loading