Compare commits

..

No commits in common. "master" and "v2.8.7" have entirely different histories.

6 changed files with 7 additions and 179 deletions

View File

@ -315,7 +315,7 @@ Lists all Python versions with the given command installed.
## `pyenv exec` ## `pyenv exec`
Usage: pyenv exec [-N|--environment] <command> [arg1 arg2...] Usage: pyenv exec <command> [arg1 arg2...]
Runs an executable by first preparing PATH so that the selected Python Runs an executable by first preparing PATH so that the selected Python
version's `bin` directory is at the front. version's `bin` directory is at the front.
@ -326,18 +326,7 @@ For example, if the currently selected Python version is 3.9.7:
is equivalent to: is equivalent to:
PYENV_VERSION="3.9.7" PATH="$PYENV_ROOT/versions/3.9.7/bin:$PATH" pip install -r requirements.txt PATH="$PYENV_ROOT/versions/3.9.7/bin:$PATH" pip install -r requirements.txt
The `--environment` option sets `PYTHONHOME` and `LD_LIBRARY_PATH` (`DYLD_LIBRARY_PATH` in macOS) in the program's environment.
This is needed to run programs that embed Python by loading `libpython` without specifying rpath or an explicit path.
This breaks linking for programs that expect to link to a different `libpython` with the same name as one
in selected Python -- thus it's not done by default.
In macOS, System Integrity Protection (SIP) removes `DYLD_LIBRARY_PATH` from a process' environment
upon `exec` (not `fork`) if the executable being run is protected. The protection covers preinstalled system software
(including preinstalled shells) and does not cover user-installed apps and 3rd-party software. See
[About System Integrity Protection on your Mac - Apple Support](https://support.apple.com/en-us/102149)
for details.
## `pyenv root` ## `pyenv root`

View File

@ -2,17 +2,7 @@
# #
# Summary: Run an executable with the selected Python version # Summary: Run an executable with the selected Python version
# #
# Usage: pyenv exec [-N|--environment] <command> [arg1 arg2...] # Usage: pyenv exec <command> [arg1 arg2...]
#
# -N/--environment Set PYTHONHOME and LD_LIBRARY_PATH (DYLD_LIBRARY_PATH in macOS)
# envvars for the running command.
# This allows to run programs that embed Python without using rpath or
# exact library path to libpython.
# WARNING: For the running command and its child processes, this will
# break linkage for programs that expect to be linked to a different
# libpython instance with the same name!
# WARNING: In macOS, DYLD_LIBRARY_PATH will be unset by the system for
# processes covered by System Integrity Protection.
# #
# Runs an executable by first preparing PATH so that the selected Python # Runs an executable by first preparing PATH so that the selected Python
# version's `bin' directory is at the front. # version's `bin' directory is at the front.
@ -28,16 +18,9 @@ set -e
# Provide pyenv completions # Provide pyenv completions
if [ "$1" = "--complete" ]; then if [ "$1" = "--complete" ]; then
echo --environment
exec pyenv-shims --short exec pyenv-shims --short
fi fi
unset MODIFY_ENV
if [ "$1" = "-N" ] || [ "$1" = "--environment" ]; then
MODIFY_ENV=true
shift
fi
PYENV_VERSION="$(pyenv-version-name -f)" PYENV_VERSION="$(pyenv-version-name -f)"
PYENV_COMMAND="$1" PYENV_COMMAND="$1"
@ -71,27 +54,4 @@ if [ "${PYENV_BIN_PATH#${PYENV_ROOT}}" != "${PYENV_BIN_PATH}" ]; then
# Only add to $PATH for non-system version. # Only add to $PATH for non-system version.
export PATH="${PYENV_BIN_PATH}:${PATH}" export PATH="${PYENV_BIN_PATH}:${PATH}"
fi fi
if [ -n "$MODIFY_ENV" ]; then
# Check if multiple Python versions are selected
VERSIONS=$(pyenv-prefix)
if [[ "$VERSIONS" == *:* ]]; then
VERSION="${VERSIONS%%:*}"
echo "pyenv: Warning: multiple Python versions are selected." \
"Setting environment variables for the first one, (${VERSION#$PYENV_ROOT})" >&2
else
VERSION="$VERSIONS"
fi
# Assign the correct version of PYTHONHOME
export PYTHONHOME="$VERSION"
case "$(uname -s)" in
Darwin)
export DYLD_LIBRARY_PATH="${PYTHONHOME}/lib${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}}"
;;
*)
export LD_LIBRARY_PATH="${PYTHONHOME}/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
;;
esac
fi
exec "$PYENV_COMMAND_PATH" "$@" exec "$PYENV_COMMAND_PATH" "$@"

View File

@ -33,11 +33,6 @@ proceeds.
pyenv binary install 3.14.7 pyenv binary install 3.14.7
``` ```
A definition with the selected package entry's name in a plugin's
`share/python-build` directory overrides the published definition. The normal
Python-Build search order applies, including `PYTHON_BUILD_DEFINITIONS`.
Local definitions are installed without downloading the stock definition.
### `pyenv binary package [-v|--verbose] <version>[:<entry>] --archive-base-url <url>` ### `pyenv binary package [-v|--verbose] <version>[:<entry>] --archive-base-url <url>`
Installs `<version>` from source under a separate name, packages that install Installs `<version>` from source under a separate name, packages that install

View File

@ -100,12 +100,6 @@ if [ -z "$entry" ]; then
echo "pyenv-binary: no binary available for $requested on $os/$arch" >&2 echo "pyenv-binary: no binary available for $requested on $os/$arch" >&2
exit 1 exit 1
fi fi
definitions="$(pyenv-install --list --bare)" || definitions=""
if printf '%s\n' "$definitions" | grep -qxF -- "$entry"; then
pyenv-install "$entry:$version"
exit
fi
if ! [[ "$expected_sha" =~ ^[0-9a-fA-F]{64}$ ]]; then if ! [[ "$expected_sha" =~ ^[0-9a-fA-F]{64}$ ]]; then
echo "pyenv-binary: invalid checksum for $entry" >&2 echo "pyenv-binary: invalid checksum for $entry" >&2
exit 1 exit 1

View File

@ -44,7 +44,7 @@ STUB
"$(host_distro)" 0 >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14" "$(host_distro)" 0 >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
stub_downloads stub_downloads
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac' create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo installed' create_stub pyenv-install 'echo installed'
run pyenv-binary-install 3.14.7 run pyenv-binary-install 3.14.7
assert_failure assert_failure
@ -58,64 +58,6 @@ STUB
assert_success "" assert_success ""
} }
@test "a plugin definition overrides a published binary without its checksum" {
create_stub lsb_release 'case "$1" in -si) echo Ubuntu;; -sr) echo 24.04;; esac'
use_manifest
export TMPDIR="$BATS_TEST_TMPDIR/temp"
mkdir -p "$TMPDIR"
local entry="3.14.7-ubuntu-24.04-x86_64"
printf '3.14.7\t%s\tLinux\tx86_64\t%s\t%s\n' "$entry" \
"$(host_distro)" "" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub curl 'touch "$BATS_TEST_TMPDIR/downloaded"; exit 1'
PATH="${BATS_TEST_DIRNAME}/../../python-build/bin:$PATH"
mkdir -p "$PYENV_ROOT/plugins/first/share/python-build" "$PYENV_ROOT/plugins/second/share/python-build"
printf '%s\n' 'mkdir -p "$PREFIX_PATH"; echo first > "$PREFIX_PATH/marker"' \
> "$PYENV_ROOT/plugins/first/share/python-build/$entry"
echo 'exit 1' > "$PYENV_ROOT/plugins/second/share/python-build/$entry"
run pyenv-binary-install 3.14
assert_success ""
assert_equal first "$(cat "$PYENV_ROOT/versions/3.14.7/marker")"
[ ! -e "$BATS_TEST_TMPDIR/downloaded" ]
assert_equal "" "$(find "$TMPDIR" -mindepth 1 -maxdepth 1 -type d)"
}
@test "an explicit definition path takes precedence over a plugin definition" {
create_stub lsb_release 'case "$1" in -si) echo Ubuntu;; -sr) echo 24.04;; esac'
use_manifest
local entry="3.14.7-ubuntu-24.04-x86_64"
printf '3.14.7\t%s\tLinux\tx86_64\t%s\t%s\n' "$entry" \
"$(host_distro)" "$definition_sha" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub curl 'touch "$BATS_TEST_TMPDIR/downloaded"; exit 1'
PATH="${BATS_TEST_DIRNAME}/../../python-build/bin:$PATH"
export PYTHON_BUILD_DEFINITIONS="$BATS_TEST_TMPDIR/custom definitions"
mkdir -p "$PYTHON_BUILD_DEFINITIONS" "$PYENV_ROOT/plugins/custom/share/python-build"
printf '%s\n' 'mkdir -p "$PREFIX_PATH"; echo custom > "$PREFIX_PATH/marker"' \
> "$PYTHON_BUILD_DEFINITIONS/$entry"
echo 'exit 1' > "$PYENV_ROOT/plugins/custom/share/python-build/$entry"
run pyenv-binary-install 3.14.7
assert_success ""
assert_equal custom "$(cat "$PYENV_ROOT/versions/3.14.7/marker")"
[ ! -e "$BATS_TEST_TMPDIR/downloaded" ]
}
@test "rejects an invalid published checksum when definition lookup fails" {
create_stub lsb_release 'case "$1" in -si) echo Ubuntu;; -sr) echo 24.04;; esac'
use_manifest
printf '3.14.7\t3.14.7-ubuntu-24.04-x86_64\tLinux\tx86_64\t%s\tinvalid\n' \
"$(host_distro)" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub pyenv-install 'exit 1'
create_stub curl 'touch "$BATS_TEST_TMPDIR/downloaded"; exit 1'
run pyenv-binary-install 3.14.7
assert_failure "pyenv-binary: invalid checksum for 3.14.7-ubuntu-24.04-x86_64"
[ ! -e "$BATS_TEST_TMPDIR/downloaded" ]
}
@test "installs a matching published binary with an uppercase checksum" { @test "installs a matching published binary with an uppercase checksum" {
create_stub lsb_release 'case "$1" in -si) echo Ubuntu;; -sr) echo 24.04;; esac' create_stub lsb_release 'case "$1" in -si) echo Ubuntu;; -sr) echo 24.04;; esac'
use_manifest use_manifest
@ -128,7 +70,6 @@ STUB
stub_downloads stub_downloads
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac' create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub pyenv-install <<'STUB' create_stub pyenv-install <<'STUB'
[ "$1" = --list ] && exit
definition="${1%:*}" definition="${1%:*}"
printf '%s:%s\n' "${definition##*/}" "${1##*:}" printf '%s:%s\n' "${definition##*/}" "${1##*:}"
cat "$definition" cat "$definition"
@ -148,7 +89,7 @@ definition"
"$(host_distro)" "$definition_sha" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14" "$(host_distro)" "$definition_sha" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
stub_downloads stub_downloads
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac' create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo "${1##*:}"' create_stub pyenv-install 'echo "${1##*:}"'
run pyenv-binary-install 3.14 run pyenv-binary-install 3.14
assert_success "3.14.10" assert_success "3.14.10"
@ -184,7 +125,7 @@ definition"
stub_downloads stub_downloads
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac' create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
create_stub sw_vers 'echo 26.6.2' create_stub sw_vers 'echo 26.6.2'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo "${1##*:}"' create_stub pyenv-install 'echo "${1##*:}"'
run pyenv-binary-install 3.14.7 run pyenv-binary-install 3.14.7
assert_success "3.14.7" assert_success "3.14.7"
@ -196,7 +137,7 @@ definition"
"$definition_sha" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14" "$definition_sha" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac' create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
create_stub sw_vers 'echo 15.7.8' create_stub sw_vers 'echo 15.7.8'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo installed' create_stub pyenv-install 'echo installed'
run pyenv-binary-install 3.14.7 run pyenv-binary-install 3.14.7
assert_failure "pyenv-binary: no binary available for 3.14.7 on Darwin/arm64" assert_failure "pyenv-binary: no binary available for 3.14.7 on Darwin/arm64"

View File

@ -34,7 +34,6 @@ EOF
assert_success assert_success
assert_output <<OUT assert_output <<OUT
--help --help
--environment
fab fab
python python
OUT OUT
@ -140,53 +139,3 @@ $envvarname=/unusual/shim/location:/another/shim/location
_PYENV_SHIM_PATH= _PYENV_SHIM_PATH=
! !
} }
@test "--environment sets envvars in Linux" {
export PYENV_VERSION="test"
create_alt_executable print_env <<!
#!$BASH
echo PYTHONHOME="\$PYTHONHOME"
echo LD_LIBRARY_PATH="\$LD_LIBRARY_PATH"
!
create_path_executable uname "echo Linux"
LD_LIBRARY_PATH= run pyenv-exec -N print_env
assert_output_glob <<!
PYTHONHOME=*/test
LD_LIBRARY_PATH=*/test/lib
!
LD_LIBRARY_PATH=/foo/bar run pyenv-exec -N print_env
assert_output_glob <<!
PYTHONHOME=*/test
LD_LIBRARY_PATH=*/test/lib:/foo/bar
!
}
@test "--environment sets envvars in macOS" {
if [[ $(uname -s) == "Darwin" && $BASH == "/bin/bash" \
&& $(LC_ALL=C csrutil status) == *"enabled." ]]; then
skip "macOS with Bash covered by SIP"
fi
export PYENV_VERSION="test"
create_alt_executable print_env <<!
#!$BASH
echo PYTHONHOME="\$PYTHONHOME"
echo DYLD_LIBRARY_PATH="\$DYLD_LIBRARY_PATH"
!
create_path_executable uname "echo Darwin"
DYLD_LIBRARY_PATH= run pyenv-exec -N print_env
assert_output_glob <<!
PYTHONHOME=*/test
DYLD_LIBRARY_PATH=*/test/lib
!
DYLD_LIBRARY_PATH=/foo/bar run pyenv-exec -N print_env
assert_output_glob <<!
PYTHONHOME=*/test
DYLD_LIBRARY_PATH=*/test/lib:/foo/bar
!
}