├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── etc └── pyenv.d │ └── exec │ └── pip.bash ├── install.sh ├── libexec ├── easy_install └── pip └── test ├── easy_install.bats ├── installer.bats ├── libexec └── pyenv-exec ├── pip.bats ├── stubs └── stub ├── test_helper.bash └── tmp └── .gitignore /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | install: git clone https://github.com/sstephenson/bats.git 2 | script: bats/bin/bats --tap test 3 | language: c 4 | notifications: 5 | email: 6 | on_success: never 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Yamashita, Yuu 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Since Pyenv v20141211, this plugin's functionality is present in the core Pyenv so this plugin is no longer needed.** 2 | 3 | # pyenv-pip-rehash 4 | 5 | ## Installation 6 | 7 | ### Installing as a pyenv plugin 8 | 9 | Make sure you have pyenv 0.2.0 or later, then run: 10 | 11 | git clone https://github.com/yyuu/pyenv-pip-rehash.git $(pyenv root)/plugins/pyenv-pip-rehash 12 | 13 | 14 | ### Installing with Homebrew (for OS X users) 15 | 16 | Mac OS X users can install pyenv-pip-rehash with the 17 | [Homebrew](http://brew.sh) package manager. 18 | 19 | *This is the recommended method of installation if you installed pyenv 20 | with Homebrew.* 21 | 22 | ``` 23 | $ brew install homebrew/boneyard/pyenv-pip-rehash 24 | ``` 25 | 26 | Or, if you would like to install the latest development release: 27 | 28 | ``` 29 | $ brew install --HEAD homebrew/boneyard/pyenv-pip-rehash 30 | ``` 31 | 32 | ## Usage 33 | 34 | 1. `pip install` a pip that provides executables. 35 | 2. Marvel at how you no longer need to type `pyenv rehash`. 36 | 37 | ## How It Works 38 | 39 | pyenv-pip-rehash hooks every invokation of `pip` commands via `pyenv`. 40 | If the first argument for `pip` is `install` or `uninstall`, it invokes `pyenv rehash` automatically. 41 | 42 | ## History 43 | 44 | **0.0.4** (Mar 30, 2014) 45 | 46 | * Fix infinite loop issue with `system` version (yyuu/pyenv#146) 47 | 48 | **0.0.3** (Jan 22, 2014) 49 | 50 | * Rewrite with using `exec` hook of `pyenv` 51 | * Support `easy_install` in addition to `pip` 52 | * Add tests 53 | 54 | **0.0.2** (Jun 14, 2013) 55 | 56 | * Surely remove `${PIP_SHIM_PATH}` on exit 57 | 58 | **0.0.1** (May 13, 2013) 59 | 60 | * Initial public release. 61 | 62 | ## License 63 | 64 | (The MIT License) 65 | 66 | Copyright (c) 2014 Yamashita, Yuu <> 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining 69 | a copy of this software and associated documentation files (the 70 | "Software"), to deal in the Software without restriction, including 71 | without limitation the rights to use, copy, modify, merge, publish, 72 | distribute, sublicense, and/or sell copies of the Software, and to 73 | permit persons to whom the Software is furnished to do so, subject to 74 | the following conditions: 75 | 76 | The above copyright notice and this permission notice shall be 77 | included in all copies or substantial portions of the Software. 78 | 79 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 80 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 81 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 82 | NONINFRINpipENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 83 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 84 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 85 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 86 | -------------------------------------------------------------------------------- /etc/pyenv.d/exec/pip.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | resolve_link() { 4 | $(type -p greadlink readlink | head -1) "$1" 5 | } 6 | 7 | abs_dirname() { 8 | local cwd="$(pwd)" 9 | local path="$1" 10 | 11 | while [ -n "$path" ]; do 12 | cd "${path%/*}" 13 | local name="${path##*/}" 14 | path="$(resolve_link "$name" || true)" 15 | done 16 | 17 | pwd 18 | cd "$cwd" 19 | } 20 | 21 | PYENV_PIP_REHASH_ROOT="$(abs_dirname "$(abs_dirname "${BASH_SOURCE[0]}")/../../../..")" 22 | 23 | if [ -x "${PYENV_PIP_REHASH_ROOT}/libexec/${PYENV_COMMAND##*/}" ]; then 24 | PYENV_COMMAND_PATH="${PYENV_PIP_REHASH_ROOT}/libexec/${PYENV_COMMAND##*/}" 25 | PYENV_BIN_PATH="${PYENV_PIP_REHASH_ROOT}/libexec" 26 | fi 27 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Usage: PREFIX=/usr/local ./install.sh 3 | # 4 | # Installs pyenv-pip-rehash under $PREFIX. 5 | 6 | set -e 7 | 8 | cd "$(dirname "$0")" 9 | 10 | if [ -z "${PREFIX}" ]; then 11 | PREFIX="/usr/local" 12 | fi 13 | 14 | ETC_PATH="${PREFIX}/etc/pyenv.d" 15 | LIBEXEC_PATH="${PREFIX}/libexec" 16 | 17 | mkdir -p "$ETC_PATH" 18 | mkdir -p "$LIBEXEC_PATH" 19 | 20 | cp -RPp etc/pyenv.d/* "$ETC_PATH" 21 | install -p libexec/* "$LIBEXEC_PATH" 22 | -------------------------------------------------------------------------------- /libexec/easy_install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | [ -n "$PYENV_DEBUG" ] && set -x 5 | 6 | # Remove pyenv-pip-rehash/libexec from PATH to avoid infinite loops in `pyenv-which` (yyuu/pyenv#146) 7 | _PATH=":${PATH}:" 8 | _HERE="$(dirname "${BASH_SOURCE[0]}")" # remove this from PATH 9 | _PATH="${_PATH//:${_HERE}:/:}" 10 | _PATH="${_PATH#:}" 11 | _PATH="${_PATH%:}" 12 | PATH="${_PATH}" 13 | 14 | PYENV_COMMAND_PATH="$(pyenv-which "$(basename "$0")")" 15 | PYENV_BIN_PATH="${PYENV_COMMAND_PATH%/*}" 16 | 17 | export PATH="${PYENV_BIN_PATH}:${PATH}" 18 | 19 | STATUS=0 20 | "$PYENV_COMMAND_PATH" "$@" || STATUS="$?" 21 | 22 | # Run `pyenv-rehash` after a successful installation. 23 | if [ "$STATUS" == "0" ]; then 24 | pyenv-rehash 25 | fi 26 | 27 | exit "$STATUS" 28 | -------------------------------------------------------------------------------- /libexec/pip: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | [ -n "$PYENV_DEBUG" ] && set -x 5 | 6 | # Remove pyenv-pip-rehash/libexec from PATH to avoid infinite loops in `pyenv-which` (yyuu/pyenv#146) 7 | _PATH=":${PATH}:" 8 | _HERE="$(dirname "${BASH_SOURCE[0]}")" # remove this from PATH 9 | _PATH="${_PATH//:${_HERE}:/:}" 10 | _PATH="${_PATH#:}" 11 | _PATH="${_PATH%:}" 12 | PATH="${_PATH}" 13 | 14 | PYENV_COMMAND_PATH="$(pyenv-which "$(basename "$0")")" 15 | PYENV_BIN_PATH="${PYENV_COMMAND_PATH%/*}" 16 | 17 | export PATH="${PYENV_BIN_PATH}:${PATH}" 18 | 19 | STATUS=0 20 | "$PYENV_COMMAND_PATH" "$@" || STATUS="$?" 21 | 22 | # Run `pyenv-rehash` after a successful installation. 23 | if [ "$STATUS" == "0" ]; then 24 | case "$1" in 25 | "install" | "uninstall" ) pyenv-rehash;; 26 | esac 27 | fi 28 | 29 | exit "$STATUS" 30 | -------------------------------------------------------------------------------- /test/easy_install.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | setup() { 6 | export PYENV_PIP_REHASH_ROOT="$(abs_dirname "${BATS_TEST_DIRNAME}/../..")" 7 | stub pyenv-which "easy_install : echo \"${TMP}/bin/easy_install\"" \ 8 | "easy_install : echo \"${TMP}/bin/easy_install\"" 9 | } 10 | 11 | resolve_link() { 12 | $(type -p greadlink readlink | head -1) "$1" 13 | } 14 | 15 | abs_dirname() { 16 | local cwd="$(pwd)" 17 | local path="$1" 18 | 19 | while [ -n "$path" ]; do 20 | cd "${path%/*}" 21 | local name="${path##*/}" 22 | path="$(resolve_link "$name" || true)" 23 | done 24 | 25 | pwd 26 | cd "$cwd" 27 | } 28 | 29 | @test "should invoke rehash after successful easy_install" { 30 | stub pyenv-rehash "echo rehashed" 31 | stub easy_install "echo \"easy_install \$@\"" 32 | 33 | run pyenv-exec easy_install tornado 34 | 35 | unstub pyenv-rehash 36 | unstub easy_install 37 | 38 | assert_success 39 | assert_output <&${!_STUB_DEBUG} 18 | fi 19 | 20 | [ -e "${!_STUB_PLAN}" ] || exit 1 21 | [ -n "${!_STUB_RUN}" ] || eval "${_STUB_RUN}"="${TMPDIR}/${program}-stub-run" 22 | 23 | 24 | # Initialize or load the stub run information. 25 | eval "${_STUB_INDEX}"=1 26 | eval "${_STUB_RESULT}"=0 27 | [ ! -e "${!_STUB_RUN}" ] || source "${!_STUB_RUN}" 28 | 29 | 30 | # Loop over each line in the plan. 31 | index=0 32 | while IFS= read -r line; do 33 | index=$(($index + 1)) 34 | 35 | if [ -z "${!_STUB_END}" ] && [ $index -eq "${!_STUB_INDEX}" ]; then 36 | # We found the plan line we're interested in. 37 | # Start off by assuming success. 38 | result=0 39 | 40 | # Split the line into an array of arguments to 41 | # match and a command to run to produce output. 42 | command=" $line" 43 | if [ "$command" != "${command/ : }" ]; then 44 | patterns="${command%% : *}" 45 | command="${command#* : }" 46 | fi 47 | 48 | # Naively split patterns by whitespace for now. 49 | # In the future, use a sed script to split while 50 | # respecting quoting. 51 | set -f 52 | patterns=($patterns) 53 | set +f 54 | arguments=("$@") 55 | 56 | # Match the expected argument patterns to actual 57 | # arguments. 58 | for (( i=0; i<${#patterns[@]}; i++ )); do 59 | pattern="${patterns[$i]}" 60 | argument="${arguments[$i]}" 61 | 62 | case "$argument" in 63 | $pattern ) ;; 64 | * ) result=1 ;; 65 | esac 66 | done 67 | 68 | # If the arguments matched, evaluate the command 69 | # in a subshell. Otherwise, log the failure. 70 | if [ $result -eq 0 ] ; then 71 | set +e 72 | ( eval "$command" ) 73 | status="$?" 74 | set -e 75 | else 76 | eval "${_STUB_RESULT}"=1 77 | fi 78 | fi 79 | done < "${!_STUB_PLAN}" 80 | 81 | 82 | if [ -n "${!_STUB_END}" ]; then 83 | # Clean up the run file. 84 | rm -f "${!_STUB_RUN}" 85 | 86 | # If the number of lines in the plan is larger than 87 | # the requested index, we failed. 88 | if [ $index -ge "${!_STUB_INDEX}" ]; then 89 | eval "${_STUB_RESULT}"=1 90 | fi 91 | 92 | # Return the result. 93 | exit "${!_STUB_RESULT}" 94 | 95 | else 96 | # If the requested index is larger than the number 97 | # of lines in the plan file, we failed. 98 | if [ "${!_STUB_INDEX}" -gt $index ]; then 99 | eval "${_STUB_RESULT}"=1 100 | fi 101 | 102 | # Write out the run information. 103 | { echo "${_STUB_INDEX}=$((${!_STUB_INDEX} + 1))" 104 | echo "${_STUB_RESULT}=${!_STUB_RESULT}" 105 | } > "${!_STUB_RUN}" 106 | 107 | exit "$status" 108 | 109 | fi 110 | -------------------------------------------------------------------------------- /test/test_helper.bash: -------------------------------------------------------------------------------- 1 | export TMP="$BATS_TEST_DIRNAME/tmp" 2 | 3 | PATH=/usr/bin:/usr/sbin:/bin/:/sbin 4 | PATH="$BATS_TEST_DIRNAME/../bin:$PATH" 5 | PATH="$BATS_TEST_DIRNAME/libexec:$PATH" 6 | PATH="$TMP/bin:$PATH" 7 | export PATH 8 | 9 | teardown() { 10 | rm -fr "$TMP"/* 11 | } 12 | 13 | stub() { 14 | local program="$1" 15 | local prefix="$(echo "$program" | tr a-z- A-Z_)" 16 | shift 17 | 18 | export "${prefix}_STUB_PLAN"="${TMP}/${program}-stub-plan" 19 | export "${prefix}_STUB_RUN"="${TMP}/${program}-stub-run" 20 | export "${prefix}_STUB_END"= 21 | 22 | mkdir -p "${TMP}/bin" 23 | ln -sf "${BATS_TEST_DIRNAME}/stubs/stub" "${TMP}/bin/${program}" 24 | 25 | touch "${TMP}/${program}-stub-plan" 26 | for arg in "$@"; do printf "%s\n" "$arg" >> "${TMP}/${program}-stub-plan"; done 27 | } 28 | 29 | unstub() { 30 | local program="$1" 31 | local prefix="$(echo "$program" | tr a-z- A-Z_)" 32 | local path="${TMP}/bin/${program}" 33 | 34 | export "${prefix}_STUB_END"=1 35 | 36 | local STATUS=0 37 | "$path" || STATUS="$?" 38 | 39 | rm -f "$path" 40 | rm -f "${TMP}/${program}-stub-plan" "${TMP}/${program}-stub-run" 41 | return "$STATUS" 42 | } 43 | 44 | assert() { 45 | if ! "$@"; then 46 | flunk "failed: $@" 47 | fi 48 | } 49 | 50 | flunk() { 51 | { if [ "$#" -eq 0 ]; then cat - 52 | else echo "$@" 53 | fi 54 | } | sed "s:${TMP}:\${TMP}:g" >&2 55 | return 1 56 | } 57 | 58 | assert_success() { 59 | if [ "$status" -ne 0 ]; then 60 | { echo "command failed with exit status $status" 61 | echo "output: $output" 62 | } | flunk 63 | elif [ "$#" -gt 0 ]; then 64 | assert_output "$1" 65 | fi 66 | } 67 | 68 | assert_failure() { 69 | if [ "$status" -eq 0 ]; then 70 | flunk "expected failed exit status" 71 | elif [ "$#" -gt 0 ]; then 72 | assert_output "$1" 73 | fi 74 | } 75 | 76 | assert_equal() { 77 | if [ "$1" != "$2" ]; then 78 | { echo "expected: $1" 79 | echo "actual: $2" 80 | } | flunk 81 | fi 82 | } 83 | 84 | assert_output() { 85 | local expected 86 | if [ $# -eq 0 ]; then expected="$(cat -)" 87 | else expected="$1" 88 | fi 89 | assert_equal "$expected" "$output" 90 | } 91 | 92 | assert_output_contains() { 93 | local expected="$1" 94 | echo "$output" | grep -F "$expected" >/dev/null || { 95 | { echo "expected output to contain $expected" 96 | echo "actual: $output" 97 | } | flunk 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /test/tmp/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | --------------------------------------------------------------------------------