From 44c20af80ec1d4d83f86c03d72949416ac858173 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 13 Mar 2014 14:59:33 +0100 Subject: [PATCH 1/4] Optimize pyenv-which: implement remove_from_path in Bash This greatly improves the performance of `pyenv virtualenvwrapper_lazy`, which happens to call pyenv-which a lot. For profiling I've initially used: % zmodload zsh/zprof % eval "$(pyenv init -)" % pyenv virtualenvwrapper_lazy Before: % zprof|grep -E '(pyenv|virtualenv)' 1) 1 754,07 754,07 58,95% 751,50 751,50 58,75% pyenv 21) 1 2,57 2,57 0,20% 2,57 2,57 0,20% virtualenvwrapper_setup_lazy_loader 1) 1 754,07 754,07 58,95% 751,50 751,50 58,75% pyenv 1/1 2,57 2,57 0,20% 2,57 2,57 virtualenvwrapper_setup_lazy_loader [21] 1/1 2,57 2,57 0,20% 2,57 2,57 pyenv [1] 21) 1 2,57 2,57 0,20% 2,57 2,57 0,20% virtualenvwrapper_setup_lazy_loader After: % zprof|grep -E '(pyenv|virtualenv)' 1) 1 383,30 383,30 27,97% 380,88 380,88 27,79% pyenv 31) 1 2,42 2,42 0,18% 2,42 2,42 0,18% virtualenvwrapper_setup_lazy_loader 1) 1 383,30 383,30 27,97% 380,88 380,88 27,79% pyenv 1/1 2,42 2,42 0,18% 2,42 2,42 virtualenvwrapper_setup_lazy_loader [31] 1/1 2,42 2,42 0,18% 2,42 2,42 pyenv [1] 31) 1 2,42 2,42 0,18% 2,42 2,42 0,18% virtualenvwrapper_setup_lazy_loader Fixes https://github.com/yyuu/pyenv-virtualenvwrapper/issues/13 --- libexec/pyenv-which | 40 +++++----------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/libexec/pyenv-which b/libexec/pyenv-which index 8e2a6f4e..c5e06978 100755 --- a/libexec/pyenv-which +++ b/libexec/pyenv-which @@ -15,39 +15,6 @@ if [ "$1" = "--complete" ]; then exec pyenv shims --short fi -expand_path() { - if [ ! -d "$1" ]; then - return 1 - fi - - local cwd="$(pwd)" - cd "$1" - pwd - cd "$cwd" -} - -remove_from_path() { - local path_to_remove="$(expand_path "$1")" - local result="" - - if [ -z "$path_to_remove" ]; then - echo "${PATH}" - return - fi - - local paths - IFS=: paths=($PATH) - - for path in "${paths[@]}"; do - path="$(expand_path "$path" || true)" - if [ -n "$path" ] && [ "$path" != "$path_to_remove" ]; then - result="${result}${path}:" - fi - done - - echo "${result%:}" -} - OLDIFS="$IFS" IFS=: versions=($(pyenv-version-name)) IFS=: PYENV_VERSION="${versions[*]}" @@ -61,8 +28,11 @@ fi for version in "${versions[@]}"; do if [ "$version" = "system" ]; then - PATH="$(remove_from_path "${PYENV_ROOT}/shims")" - PYENV_COMMAND_PATH="$(command -v "$PYENV_COMMAND" || true)" + # Remove shims from PATH: + _path=":$PATH:" + _remove="${PYENV_ROOT}/shims" + _path="${_path//:$_remove:/:}" + PYENV_COMMAND_PATH="$(PATH=$_path command -v "$PYENV_COMMAND" || true)" else PYENV_COMMAND_PATH="${PYENV_ROOT}/versions/${version}/bin/${PYENV_COMMAND}" fi From a1df551bcf5165d375fcbbbc26b583ee53cfb611 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 13 Oct 2014 20:31:32 +0200 Subject: [PATCH 2/4] Merge tests and remove_from_path from rbenv This adds the missing parts from the upstream rbenv merge: https://github.com/sstephenson/rbenv/commit/f4652fbbf0bbc3d07a36e8781b2be54ec1f75518 - https://github.com/sstephenson/rbenv/commit/e4cbf04592cfcd9d244fb83284e14524c8ba0377 - https://github.com/sstephenson/rbenv/commit/3ee395f9b576e4111650cef67328f4ffe3687b5a --- libexec/pyenv-which | 30 +++++++++++++++++++++--------- test/which.bats | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/libexec/pyenv-which b/libexec/pyenv-which index c5e06978..ccef0083 100755 --- a/libexec/pyenv-which +++ b/libexec/pyenv-which @@ -15,10 +15,17 @@ if [ "$1" = "--complete" ]; then exec pyenv shims --short fi -OLDIFS="$IFS" -IFS=: versions=($(pyenv-version-name)) -IFS=: PYENV_VERSION="${versions[*]}" -IFS="$OLDIFS" +remove_from_path() { + local path_to_remove="$1" + local path_before + local result=":$PATH:" + while [ "$path_before" != "$result" ]; do + path_before="$result" + result="${result//:$path_to_remove:/:}" + done + echo "${result%:}" +} + PYENV_COMMAND="$1" if [ -z "$PYENV_COMMAND" ]; then @@ -26,17 +33,19 @@ if [ -z "$PYENV_COMMAND" ]; then exit 1 fi +OLDIFS="$IFS" +IFS=: versions=(${PYENV_VERSION:-$(pyenv-version-name)}) +IFS="$OLDIFS" + for version in "${versions[@]}"; do if [ "$version" = "system" ]; then - # Remove shims from PATH: - _path=":$PATH:" - _remove="${PYENV_ROOT}/shims" - _path="${_path//:$_remove:/:}" - PYENV_COMMAND_PATH="$(PATH=$_path command -v "$PYENV_COMMAND" || true)" + PATH="$(remove_from_path "${PYENV_ROOT}/shims")" + PYENV_COMMAND_PATH="$(command -v "$PYENV_COMMAND" || true)" else PYENV_COMMAND_PATH="${PYENV_ROOT}/versions/${version}/bin/${PYENV_COMMAND}" fi if [ -x "$PYENV_COMMAND_PATH" ]; then + PYENV_VERSION="$version" break fi done @@ -50,6 +59,9 @@ done if [ -x "$PYENV_COMMAND_PATH" ]; then echo "$PYENV_COMMAND_PATH" +elif ! [ -d "${PYENV_ROOT}/versions/${PYENV_VERSION}" ]; then + echo "pyenv: version \`$PYENV_VERSION' is not installed" >&2 + exit 1 else echo "pyenv: $PYENV_COMMAND: command not found" >&2 diff --git a/test/which.bats b/test/which.bats index dcce6665..6d80eea8 100644 --- a/test/which.bats +++ b/test/which.bats @@ -31,6 +31,31 @@ create_executable() { assert_success "${PYENV_TEST_DIR}/bin/kill-all-humans" } +@test "searches PATH for system version (shims prepended)" { + create_executable "${PYENV_TEST_DIR}/bin" "kill-all-humans" + create_executable "${PYENV_ROOT}/shims" "kill-all-humans" + + PATH="${PYENV_ROOT}/shims:$PATH" PYENV_VERSION=system run pyenv-which kill-all-humans + assert_success "${PYENV_TEST_DIR}/bin/kill-all-humans" +} + +@test "searches PATH for system version (shims appended)" { + create_executable "${PYENV_TEST_DIR}/bin" "kill-all-humans" + create_executable "${PYENV_ROOT}/shims" "kill-all-humans" + + PATH="$PATH:${PYENV_ROOT}/shims" PYENV_VERSION=system run pyenv-which kill-all-humans + assert_success "${PYENV_TEST_DIR}/bin/kill-all-humans" +} + +@test "searches PATH for system version (shims spread)" { + create_executable "${PYENV_TEST_DIR}/bin" "kill-all-humans" + create_executable "${PYENV_ROOT}/shims" "kill-all-humans" + + PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/shims:/tmp/non-existent:$PATH:${PYENV_ROOT}/shims" \ + PYENV_VERSION=system run pyenv-which kill-all-humans + assert_success "${PYENV_TEST_DIR}/bin/kill-all-humans" +} + @test "version not installed" { create_executable "3.4" "py.test" PYENV_VERSION=3.3 run pyenv-which py.test @@ -72,3 +97,15 @@ SH assert_success assert_output "HELLO=:hello:ugly:world:again" } + +@test "discovers version from pyenv-version-name" { + mkdir -p "$PYENV_ROOT" + cat > "${PYENV_ROOT}/version" <<<"3.4" + create_executable "3.4" "python" + + mkdir -p "$PYENV_TEST_DIR" + cd "$PYENV_TEST_DIR" + + PYENV_VERSION= run pyenv-which python + assert_success "${PYENV_ROOT}/versions/3.4/bin/python" +} From bbf8cb4a954abfa0900307d32bdeeef5801159c0 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 13 Oct 2014 21:15:22 +0200 Subject: [PATCH 3/4] Add test for PYENV_VERSION=3.4:2.7 --- test/which.bats | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/which.bats b/test/which.bats index 6d80eea8..8816cb10 100644 --- a/test/which.bats +++ b/test/which.bats @@ -21,6 +21,9 @@ create_executable() { PYENV_VERSION=3.4 run pyenv-which py.test assert_success "${PYENV_ROOT}/versions/3.4/bin/py.test" + + PYENV_VERSION=3.4:2.7 run pyenv-which py.test + assert_success "${PYENV_ROOT}/versions/3.4/bin/py.test" } @test "searches PATH for system version" { From e82f9431744f160e66cfddd751eeb31cf7c8163e Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 13 Oct 2014 21:20:57 +0200 Subject: [PATCH 4/4] =?UTF-8?q?Merge=20rbenv's=20"Isolate=20rbenv-which=20?= =?UTF-8?q?tests=20=E2=80=A6"=20(c69d9a11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the remaining part of https://github.com/sstephenson/rbenv/commit/c69d9a1128283f20ad883178e3649d7ed92be663. commit c69d9a1128283f20ad883178e3649d7ed92be663 Author: Mislav Marohnić Date: Mon Oct 13 12:39:47 2014 +0200 Isolate rbenv-which tests from any `.ruby-version` file on the system Having a `.ruby-version` file in any of the parent directories of the local clone of rbenv could cause the test suite to fail because it wasn't expecting a local version to be set. --- test/which.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/which.bats b/test/which.bats index 8816cb10..d3b69f2b 100644 --- a/test/which.bats +++ b/test/which.bats @@ -96,7 +96,7 @@ echo HELLO="\$(printf ":%s" "\${hellos[@]}")" exit SH - PYENV_HOOK_PATH="$hook_path" IFS=$' \t\n' run pyenv-which anything + PYENV_HOOK_PATH="$hook_path" IFS=$' \t\n' PYENV_VERSION=system run pyenv-which anything assert_success assert_output "HELLO=:hello:ugly:world:again" }