Compare commits

...

7 Commits

Author SHA1 Message Date
Ivan Pozdeev
67cfc116b9 -redundant check
invalid-format checksums will be rejected during comparison anyway
2026-10-03 17:37:36 +03:00
Ivan Pozdeev
f217e6c6cf Fix "broken pipe" in pyenv-binary-install 2026-10-03 17:15:05 +03:00
Ivan Pozdeev
07171d013c 2.8.8 2026-10-03 11:30:37 +03:00
pyenv-bot[bot]
7d8d87afc0
Add CPython 3.15.0rc3 (#3578)
Co-authored-by: native-api <2670332+native-api@users.noreply.github.com>
2026-10-03 09:47:28 +03:00
native-api
411a5188e5
README: grammar fixes [no ci] 2026-10-03 07:09:29 +03:00
Michał
f8b1d7ae09
pyenv-exec: add the --environment option for programs that embed libpython without a path (#3537)
---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: native-api <vano@mail.mipt.ru>
2026-10-02 11:17:30 +03:00
Ayush
6ff25c51d3
pyenv-binary: allow local definitions to override published packages (#3577) 2026-10-02 05:19:21 +03:00
13 changed files with 186 additions and 28 deletions

View File

@ -1,5 +1,9 @@
# Version History
## Release v2.8.8
* pyenv-binary: allow local definitions to override published packages by @macayu17 in https://github.com/pyenv/pyenv/pull/3577
* exec: add the `--environment` option for programs that embed `libpython` without a path by @Romwierz in https://github.com/pyenv/pyenv/pull/3537
* Add CPython 3.15.0rc3 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3578
## Release v2.8.7
* Fix rehash repair of individually corrupted shims by @aprylewu in https://github.com/pyenv/pyenv/pull/3540

View File

@ -315,7 +315,7 @@ Lists all Python versions with the given command installed.
## `pyenv exec`
Usage: pyenv exec <command> [arg1 arg2...]
Usage: pyenv exec [-N|--environment] <command> [arg1 arg2...]
Runs an executable by first preparing PATH so that the selected Python
version's `bin` directory is at the front.
@ -326,7 +326,18 @@ For example, if the currently selected Python version is 3.9.7:
is equivalent to:
PATH="$PYENV_ROOT/versions/3.9.7/bin:$PATH" pip install -r requirements.txt
PYENV_VERSION="3.9.7" 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`

View File

@ -12,7 +12,7 @@
set -e
[ -n "$PYENV_DEBUG" ] && set -x
version="2.8.7"
version="2.8.8"
git_revision=""
if cd "${BASH_SOURCE%/*}" 2>/dev/null && git remote -v 2>/dev/null | grep -q pyenv; then

View File

@ -2,7 +2,17 @@
#
# Summary: Run an executable with the selected Python version
#
# Usage: pyenv exec <command> [arg1 arg2...]
# Usage: pyenv exec [-N|--environment] <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
# version's `bin' directory is at the front.
@ -18,9 +28,16 @@ set -e
# Provide pyenv completions
if [ "$1" = "--complete" ]; then
echo --environment
exec pyenv-shims --short
fi
unset MODIFY_ENV
if [ "$1" = "-N" ] || [ "$1" = "--environment" ]; then
MODIFY_ENV=true
shift
fi
PYENV_VERSION="$(pyenv-version-name -f)"
PYENV_COMMAND="$1"
@ -54,4 +71,27 @@ if [ "${PYENV_BIN_PATH#${PYENV_ROOT}}" != "${PYENV_BIN_PATH}" ]; then
# Only add to $PATH for non-system version.
export PATH="${PYENV_BIN_PATH}:${PATH}"
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" "$@"

View File

@ -33,6 +33,11 @@ proceeds.
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>`
Installs `<version>` from source under a separate name, packages that install

View File

@ -100,10 +100,12 @@ if [ -z "$entry" ]; then
echo "pyenv-binary: no binary available for $requested on $os/$arch" >&2
exit 1
fi
if ! [[ "$expected_sha" =~ ^[0-9a-fA-F]{64}$ ]]; then
echo "pyenv-binary: invalid checksum for $entry" >&2
exit 1
definitions="$(pyenv-install --list --bare)"
if grep -qxF -- "$entry" <<<"$definitions"; then
pyenv-install "$entry:$version"
exit
fi
expected_sha="$(printf '%s' "$expected_sha" | tr '[:upper:]' '[:lower:]')"
curl -fsSL -o "$tmpdir/$entry" "$base_url/$entry"
@ -117,7 +119,7 @@ else
echo "pyenv-binary: need sha256sum, shasum or openssl to verify $entry" >&2
exit 1
fi
if [ "$actual_sha" != "$expected_sha" ]; then
if [[ $expected_sha != "$actual_sha" ]]; then
echo "pyenv-binary: checksum mismatch for $entry" >&2
exit 1
fi

View File

@ -44,7 +44,7 @@ STUB
"$(host_distro)" 0 >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
stub_downloads
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub pyenv-install 'echo installed'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo installed'
run pyenv-binary-install 3.14.7
assert_failure
@ -58,6 +58,50 @@ STUB
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 "installs a matching published binary with an uppercase checksum" {
create_stub lsb_release 'case "$1" in -si) echo Ubuntu;; -sr) echo 24.04;; esac'
use_manifest
@ -70,6 +114,7 @@ STUB
stub_downloads
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub pyenv-install <<'STUB'
[ "$1" = --list ] && exit
definition="${1%:*}"
printf '%s:%s\n' "${definition##*/}" "${1##*:}"
cat "$definition"
@ -89,7 +134,7 @@ definition"
"$(host_distro)" "$definition_sha" >> "$BATS_TEST_TMPDIR/plugin/share/pyenv-binary/versions/3.14"
stub_downloads
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub pyenv-install 'echo "${1##*:}"'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo "${1##*:}"'
run pyenv-binary-install 3.14
assert_success "3.14.10"
@ -125,7 +170,7 @@ definition"
stub_downloads
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
create_stub sw_vers 'echo 26.6.2'
create_stub pyenv-install 'echo "${1##*:}"'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo "${1##*:}"'
run pyenv-binary-install 3.14.7
assert_success "3.14.7"
@ -137,7 +182,7 @@ definition"
"$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 sw_vers 'echo 15.7.8'
create_stub pyenv-install 'echo installed'
create_stub pyenv-install '[ "$1" = --list ] && exit; echo installed'
run pyenv-binary-install 3.14.7
assert_failure "pyenv-binary: no binary available for 3.14.7 on Darwin/arm64"

View File

@ -182,13 +182,13 @@ You can set certain environment variables to control the build process.
their original source URLs instead of using a mirror.
* `PYTHON_BUILD_HTTP_CLIENT`, explicitly specify the HTTP client type to use. `aria2`, `curl` and `wget` are the supported values and by default, are searched in that order.
* `PYTHON_BUILD_CURL_OPTS`, `PYTHON_BUILD_WGET_OPTS`, `PYTHON_BUILD_ARIA2_OPTS` pass additional parameters to the corresponding HTTP client.
* `PYTHON_BUILD_SKIP_HOMEBREW`, if set, will not search for libraries installed by Homebrew when it would normally will.
* `PYTHON_BUILD_USE_HOMEBREW`, if set, will search for libraries installed by Homebrew when it would normally not.
* `PYTHON_BUILD_SKIP_HOMEBREW`, if set, will not search for libraries installed by Homebrew when it normally would.
* `PYTHON_BUILD_USE_HOMEBREW`, if set, will search for libraries installed by Homebrew when it normally wouldn't.
* `PYTHON_BUILD_HOMEBREW_OPENSSL_FORMULA`, override the Homebrew OpenSSL formula to use.
* `PYTHON_BUILD_MACPORTS_OPENSSL_FORMULA`, override the MacPorts OpenSSL formula to use. The default is `openssl3 openssl`.
* `PYTHON_BUILD_TCLTK_FORMULA`, override the Homebrew Tcl/Tk formula to use.
* `PYTHON_BUILD_SKIP_MACPORTS`, if set, will not search for libraries installed by MacPorts when it would normally will.
* `PYTHON_BUILD_USE_MACPORTS`, if set, will search for libraries installed by MacPorts when it would normally not.
* `PYTHON_BUILD_SKIP_MACPORTS`, if set, will not search for libraries installed by MacPorts when it normally would.
* `PYTHON_BUILD_USE_MACPORTS`, if set, will search for libraries installed by MacPorts when it normally wouldn't.
* `PYTHON_BUILD_ROOT` overrides the default location from where build definitions
in `share/python-build/` are looked up.
* `PYTHON_BUILD_DEFINITIONS` can be a list of colon-separated paths that get

View File

@ -14,7 +14,7 @@
# -g/--debug Build a debug version
#
PYTHON_BUILD_VERSION="2.8.7"
PYTHON_BUILD_VERSION="2.8.8"
OLDIFS="$IFS"

View File

@ -1,11 +0,0 @@
prefer_openssl3
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
install_package "openssl-4.0.2" "https://github.com/openssl/openssl/releases/download/openssl-4.0.2/openssl-4.0.2.tar.gz#736b467530f916737b7031310ccb21d8218c6229e61e8e160cd1d3458cd543a8" mac_openssl --if has_broken_mac_openssl
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
if has_tar_xz_support; then
install_package "Python-3.15.0rc2" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0rc2.tar.xz#8d93af5eaaaea5adfd41bd786a7ba3f03f2ad1ab57c6a65e0b963deab91d5ad7" standard verify_py315 copy_python_gdb ensurepip
else
install_package "Python-3.15.0rc2" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0rc2.tgz#164dbb80f5e1fdd8768387724bfa014da1000401b2015d872a2396c8b858b829" standard verify_py315 copy_python_gdb ensurepip
fi

View File

@ -0,0 +1,11 @@
prefer_openssl3
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
install_package "openssl-4.0.3" "https://github.com/openssl/openssl/releases/download/openssl-4.0.3/openssl-4.0.3.tar.gz#325b5c806167c13b40b1ffeadfe0248197c00eccc4cf123ec1e28d2d2fd216d9" mac_openssl --if has_broken_mac_openssl
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
if has_tar_xz_support; then
install_package "Python-3.15.0rc3" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0rc3.tar.xz#506630aa66d5441188f34df22c0760bfe87fc92212630493ebc9ff3b3a7a1d49" standard verify_py315 copy_python_gdb ensurepip
else
install_package "Python-3.15.0rc3" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0rc3.tgz#98a6264c7a48e4ce0f9a9ea4bdd211ea9fcff32b55c79c5efc1ae6d91b032210" standard verify_py315 copy_python_gdb ensurepip
fi

View File

@ -34,6 +34,7 @@ EOF
assert_success
assert_output <<OUT
--help
--environment
fab
python
OUT
@ -139,3 +140,53 @@ $envvarname=/unusual/shim/location:/another/shim/location
_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
!
}