File: pyenv-rehash

package info (click to toggle)
pyenv 2.6.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,496 kB
  • sloc: sh: 4,914; python: 410; makefile: 161; ansic: 60
file content (190 lines) | stat: -rwxr-xr-x 4,775 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env bash
# Summary: Rehash pyenv shims (run this after installing executables)

set -e
[ -n "$PYENV_DEBUG" ] && set -x

SHIM_PATH="${PYENV_ROOT}/shims"
PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.pyenv-shim"

# Create the shims directory if it doesn't already exist.
mkdir -p "$SHIM_PATH"

acquire_lock() {
  # Ensure only one instance of pyenv-rehash is running at a time by
  # setting the shell's `noclobber` option and attempting to write to
  # the prototype shim file. If the file already exists, print a warning
  # to stderr and exit with a non-zero status.
  local ret
  set -o noclobber
  echo > "$PROTOTYPE_SHIM_PATH" 2>| /dev/null || ret=1
  set +o noclobber
  [ -z "${ret}" ]
}

# If we were able to obtain a lock, register a trap to clean up the
# prototype shim when the process exits.
trap release_lock EXIT

remove_prototype_shim() {
  rm -f "$PROTOTYPE_SHIM_PATH"
}

release_lock() {
  remove_prototype_shim
}

if [ ! -w "$SHIM_PATH" ]; then
  echo "pyenv: cannot rehash: $SHIM_PATH isn't writable"
  exit 1
fi

unset acquired
start=$SECONDS
while (( SECONDS <= start + ${PYENV_REHASH_TIMEOUT:-60} )); do
  if acquire_lock 2>/dev/null; then
    acquired=1
    break
  else
    # POSIX sleep(1) doesn't provide subsecond precision, but many others do
    sleep 0.1 2>/dev/null || sleep 1
  fi
done

if [ -z "${acquired}" ]; then
  echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists"
  exit 1
fi

# The prototype shim file is a script that re-execs itself, passing
# its filename and any arguments to `pyenv exec`. This file is
# hard-linked for every executable and then removed. The linking
# technique is fast, uses less disk space than unique files, and also
# serves as a locking mechanism.
create_prototype_shim() {
  cat > "$PROTOTYPE_SHIM_PATH" <<SH
#!/usr/bin/env bash
set -e
[ -n "\$PYENV_DEBUG" ] && set -x

program="\${0##*/}"

export PYENV_ROOT="$PYENV_ROOT"
exec "$(command -v pyenv)" exec "\$program" "\$@"
SH
  chmod +x "$PROTOTYPE_SHIM_PATH"
}

# If the contents of the prototype shim file differ from the contents
# of the first shim in the shims directory, assume pyenv has been
# upgraded and the existing shims need to be removed.
remove_outdated_shims() {
  local shim
  for shim in "$SHIM_PATH"/*; do
    if ! diff "$PROTOTYPE_SHIM_PATH" "$shim" >/dev/null 2>&1; then
      rm -f "$SHIM_PATH"/*
    fi
    break
  done
}

# List basenames of executables for every Python version
list_executable_names() {
  local version file
  pyenv-versions --bare --skip-aliases | \
  while read -r version; do
    for file in "${PYENV_ROOT}/versions/${version}/bin/"*; do
      echo "${file##*/}"
    done
  done
}

# The basename of each argument passed to `make_shims` will be
# registered for installation as a shim. In this way, plugins may call
# `make_shims` with a glob to register many shims at once.
make_shims() {
  local file shim
  for file; do
    shim="${file##*/}"
    register_shim "$shim"
  done
}

if ((${BASH_VERSINFO[0]} > 3)); then

  declare -A registered_shims

  # Registers the name of a shim to be generated.
  register_shim() {
    registered_shims["$1"]=1
  }

  # Install all shims registered via `make_shims` or `register_shim` directly.
  install_registered_shims() {
    local shim file
    for shim in "${!registered_shims[@]}"; do
      file="${SHIM_PATH}/${shim}"
      [ -e "$file" ] || cp "$PROTOTYPE_SHIM_PATH" "$file"
    done
  }

  # Once the registered shims have been installed, we make a second pass
  # over the contents of the shims directory. Any file that is present
  # in the directory but has not been registered as a shim should be
  # removed.
  remove_stale_shims() {
    local shim
    for shim in "$SHIM_PATH"/*; do
      if [[ ! ${registered_shims["${shim##*/}"]} ]]; then
        rm -f "$shim"
      fi
    done
  }

else # Same for bash < 4.

    registered_shims=" "

    register_shim() {
      registered_shims="${registered_shims}${1} "
    }

    install_registered_shims() {
      local shim file
      for shim in $registered_shims; do
        file="${SHIM_PATH}/${shim}"
        [ -e "$file" ] || cp "$PROTOTYPE_SHIM_PATH" "$file"
      done
    }

    remove_stale_shims() {
      local shim
      for shim in "$SHIM_PATH"/*; do
        if [[ "$registered_shims" != *" ${shim##*/} "* ]]; then
          rm -f "$shim"
        fi
      done
    }
fi

shopt -s nullglob

# Create the prototype shim, then register shims for all known
# executables.
create_prototype_shim
remove_outdated_shims
# shellcheck disable=SC2046
make_shims $(list_executable_names | sort -u)


# Allow plugins to register shims.
OLDIFS="$IFS"
IFS=$'\n' scripts=(`pyenv-hooks rehash`)
IFS="$OLDIFS"

for script in "${scripts[@]}"; do
  source "$script"
done

install_registered_shims
remove_stale_shims