Compare commits

...

6 Commits

Author SHA1 Message Date
dependabot[bot]
bdf8d254fe
Bump actions/setup-python from 6 to 7 in the github-actions group (#3505)
Bumps the github-actions group with 1 update: [actions/setup-python](https://github.com/actions/setup-python).


Updates `actions/setup-python` from 6 to 7
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-01 16:38:32 +03:00
Michael Šimáček
f5915fc866
Add graalpy3.12-25.2.4 (#3502) 2026-07-29 05:53:27 +03:00
Michael Newman
84e0f3a887
Fix typos 2026-07-29 05:49:46 +03:00
Ayush
7126870320
pyenv-binary: add the package subcommand; add pyenv install --list --bare (#3498)
Co-authored-by: native-api <vano@mail.mipt.ru>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-07-28 14:31:20 +03:00
Michael Newman
26dd5d9dda
Fix typo 2026-07-27 21:57:58 +03:00
Michael Newman
bac39a0ef8
Fix typo 2026-07-27 08:30:33 +03:00
13 changed files with 399 additions and 18 deletions

View File

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-slim
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
- uses: actions/setup-python@v7
with:
python-version: 3
cache: 'pip'

View File

@ -387,7 +387,7 @@
* Add CPython 3.13.0a4 by @saaketp in https://github.com/pyenv/pyenv/pull/2903
* Handle the case where `pyenv-commands --sh` returns nothing by @aphedges in https://github.com/pyenv/pyenv/pull/2908
* Document default build configuration customizations by @native-api in https://github.com/pyenv/pyenv/pull/2911
* Use Homebrew in Linux if Pyenv is installled with Homebrew by @native-api in https://github.com/pyenv/pyenv/pull/2906
* Use Homebrew in Linux if Pyenv is installed with Homebrew by @native-api in https://github.com/pyenv/pyenv/pull/2906
* Add miniforge and mambaforge 22.11.1-3, 22.11.1-4, 23.1.0-0 to 23.11.0-0 by @aphedges in https://github.com/pyenv/pyenv/pull/2909
* Add miniconda3-24.1.2 by @binbjz in https://github.com/pyenv/pyenv/pull/2915
* Minor grammar fix in libffi backport patch in 2.5.x by @cuinix in https://github.com/pyenv/pyenv/pull/2922
@ -1523,7 +1523,7 @@
* pyenv: Prefer gawk over awk if both are available.
* python-build: Add new PyPy release; pypy-2.3, pypy-2.3-src (#162)
* python-build: Add new Anaconda release; anaconda-1.9.2 (#155)
* python-build: Add new Miniconda releases; miniconda-3.3.0, minoconda-3.4.2, miniconda3-3.3.0, miniconda3-3.4.2
* python-build: Add new Miniconda releases; miniconda-3.3.0, miniconda-3.4.2, miniconda3-3.3.0, miniconda3-3.4.2
* python-build: Add new Stackless releases; stackless-2.7.3, stackless-2.7.4, stackless-2.7.5, stackless-2.7.6, stackless-3.2.5, stackless-3.3.5 (#164)
* python-build: Add IronPython versions (setuptools and pip will work); ironpython-2.7.4, ironpython-dev
* python-build: Add new Jython beta release; jython-2.7-beta2

View File

@ -672,7 +672,7 @@ to `PATH` in the `<command>`'s environment, the same as what e.g. RVM does.
### Running nested shells from Python-based programs
In addition to altering `PATH`, `pyenv exec` sets `PYENV_VERSION` in the
executed program's environment to ensure that it won't spontaneouly switch to
executed program's environment to ensure that it won't spontaneously switch to
using a different Python version.
Some Python-based programs (e.g. Jupyter) can spawn nested shell sessions.

View File

@ -16,11 +16,29 @@ and dependency metadata exist to catch that.
## Commands
### `pyenv binary save <version> [<output-dir>]`
### `pyenv binary package <version>:<entry> --archive-base-url <url>`
Installs `<version>` from source under the separate name `<entry>`, packages
that install with `save`, then emits a python-build definition for it with
`generate-installer`. The archive, metadata and definition land in the current
directory, named after the entry; host the archive under `<url>` and drop the
definition into python-build's definition directory. Keeping the entry name
distinct from the version lets the binary sit alongside a normal source
install of the same version.
```sh
pyenv binary package 3.12.7:3.12.7-debian-12 \
--archive-base-url https://example.com/binaries
# writes 3.12.7-debian-12.tar.gz, its .meta file and
# a `3.12.7-debian-12' definition
```
### `pyenv binary save <version> [<output-dir>] [--name <name>]`
Packs an installed version into `<version>-<platform>.tar.gz` (relative paths)
and writes `<version>-<platform>.meta` describing the build platform (OS, arch,
distro and libc version) and the system libraries the build links against.
distro and libc version) and the system libraries the build links against. Use
`--name` to set a different base name for both files.
```sh
pyenv binary save 3.12.7 ./dist

View File

@ -0,0 +1,90 @@
#!/usr/bin/env bash
#
# Summary: Create an installable binary package from a Python version
#
# Usage: pyenv binary package <version>:<entry> --archive-base-url <url>
#
# Installs <version> from source under the separate name <entry>, saves it as
# a binary package, then emits a python-build definition for that package.
# The archive, metadata and definition are written to the current directory
# as <entry>.tar.gz, <entry>.meta and <entry>.
#
# <version> A version `pyenv install' knows how to build.
# <entry> The name to build under and to install the binary
# as, e.g. `3.13.14-debian-12'. Keeping it distinct
# from <version> lets the binary sit alongside a
# normal source install of the same version.
# --archive-base-url <url>
# Where the archive will be hosted; the definition
# downloads it from <url>/<entry>.tar.gz.
#
set -e
[ -n "$PYENV_DEBUG" ] && set -x
# Provide pyenv completions
if [ "$1" = "--complete" ]; then
echo --archive-base-url
exec pyenv-install --list --bare
fi
spec=""
archive_base_url=""
while [ $# -gt 0 ]; do
case "$1" in
--archive-base-url )
[ $# -ge 2 ] || { echo "pyenv-binary: --archive-base-url needs a value" >&2; exit 1; }
archive_base_url="$2"; shift 2 ;;
-* )
echo "pyenv-binary: unknown option \`$1'" >&2; exit 1 ;;
* )
[ -z "$spec" ] || { echo "pyenv-binary: unexpected argument \`$1'" >&2; exit 1; }
spec="$1"; shift ;;
esac
done
if [ -z "$spec" ] || [ -z "$archive_base_url" ]; then
pyenv-help --usage binary-package >&2
exit 1
fi
case "$spec" in
*?:?* ) ;;
* )
echo "pyenv-binary: expected <version>:<entry>, e.g. \`3.13.14:3.13.14-debian-12'" >&2
exit 1
;;
esac
entry="${spec##*:}"
case "$entry" in
# `pyenv install' reads a trailing `:latest' as part of the version rather than
# as an alias, so nothing would end up installed under that name.
latest )
echo "pyenv-binary: \`latest' cannot be used as an entry name" >&2
exit 1
;;
# The entry becomes a directory name under versions/. `pyenv install' does not
# validate the alias, and `save' only checks once the build is done, so refuse
# a name that could point elsewhere before compiling anything.
*/* | .. | . )
echo "pyenv-binary: invalid entry name \`${entry}'" >&2
exit 1
;;
esac
os="$(uname -s)"
# `generate-installer' refuses a macOS archive, so the last step here cannot
# succeed on one. Give up before compiling rather than after it.
if [ "$os" = "Darwin" ]; then
echo "pyenv-binary: macOS archives are not supported yet" >&2
exit 1
fi
# `pyenv install' puts a `<version>:<alias>' build under versions/<alias>.
pyenv-install "$spec"
pyenv-binary-save "$entry" "$PWD" --name "$entry"
pyenv-binary-generate-installer "${entry}.meta" \
--archive-url "${archive_base_url%/}/${entry}.tar.gz" -o "$entry"

View File

@ -2,7 +2,7 @@
#
# Summary: Save an installed Python version as a relocatable archive
#
# Usage: pyenv binary save <version> [<output-dir>]
# Usage: pyenv binary save <version> [<output-dir>] [--name <name>]
#
# Packs an installed version into a relocatable .tar.gz (relative paths) and
# writes a metadata file listing the build platform and the system libraries
@ -10,20 +10,46 @@
#
# <version> An installed version, as listed by `pyenv versions --bare'.
# <output-dir> Where to write the archive and metadata (default: `.').
# --name <name> Use <name> as the archive and metadata base name instead of
# <version>-<platform>.
#
set -e
[ -n "$PYENV_DEBUG" ] && set -x
# Provide pyenv completions
if [ "$1" = "--complete" ]; then
echo --name
exec pyenv-versions --bare
fi
version="$1"
output_dir="${2:-$PWD}"
version=""
output_dir=""
package_name=""
while [ $# -gt 0 ]; do
case "$1" in
--name )
[ $# -ge 2 ] && [ -n "$2" ] || { echo "pyenv-binary: --name needs a value" >&2; exit 1; }
package_name="$2"; shift 2 ;;
-* )
echo "pyenv-binary: unknown option \`$1'" >&2; exit 1 ;;
* )
if [ -z "$version" ]; then
version="$1"
elif [ -z "$output_dir" ]; then
output_dir="$1"
else
echo "pyenv-binary: unexpected argument \`$1'" >&2
exit 1
fi
shift ;;
esac
done
output_dir="${output_dir:-$PWD}"
if [ -z "$version" ]; then
echo "Usage: pyenv binary save <version> [<output-dir>]" >&2
echo "Usage: pyenv binary save <version> [<output-dir>] [--name <name>]" >&2
exit 1
fi
@ -36,6 +62,13 @@ case "$version" in
;;
esac
case "$package_name" in
*/* | .. | . | *[[:cntrl:]]* )
echo "pyenv-binary: invalid package name \`${package_name}'" >&2
exit 1
;;
esac
prefix="${PYENV_ROOT}/versions/${version}"
if [ ! -d "${prefix}/bin" ]; then
echo "pyenv-binary: version \`${version}' is not installed" >&2
@ -85,8 +118,9 @@ system_deps() {
}
mkdir -p "$output_dir"
archive="${version}-${platform}.tar.gz"
metadata="${version}-${platform}.meta"
package_name="${package_name:-${version}-${platform}}"
archive="${package_name}.tar.gz"
metadata="${package_name}.meta"
tar -C "$(dirname "$prefix")" -czf "${output_dir}/${archive}" "$(basename "$prefix")"

View File

@ -0,0 +1,92 @@
#!/usr/bin/env bats
load test_helper
# Make the build deterministic: `pyenv-install' just creates the prefix, and
# the platform tools report a fixed Linux target so the real `save' and
# `generate-installer' behave the same on any test host.
stub_build_environment() {
create_stub pyenv-install 'mkdir -p "${PYENV_ROOT}/versions/${1##*:}/bin"'
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub getconf 'echo "glibc 2.17"'
}
@test "completion lists the option and definitions provided by another plugin" {
mkdir -p "${PYENV_ROOT}/plugins/example/share/python-build"
touch "${PYENV_ROOT}/plugins/example/share/python-build/3.12.7-example"
PATH="${BATS_TEST_DIRNAME}/../../python-build/bin:${PATH}"
run pyenv-binary-package --complete
assert_success
assert_line "--archive-base-url"
assert_line "3.12.7-example"
refute_line "Available versions:"
}
@test "fails with no arguments" {
create_stub pyenv-help "echo usage"
run pyenv-binary-package
assert_failure "usage"
}
@test "fails without an archive base url" {
create_stub pyenv-help "echo usage"
run pyenv-binary-package 3.12.7:3.12.7-test
assert_failure "usage"
}
@test "fails when --archive-base-url has no value" {
run pyenv-binary-package 3.12.7:3.12.7-test --archive-base-url
assert_failure "pyenv-binary: --archive-base-url needs a value"
}
@test "rejects a second positional argument" {
run pyenv-binary-package 3.12.7:3.12.7-test extra --archive-base-url http://x/b
assert_failure "pyenv-binary: unexpected argument \`extra'"
}
@test "rejects a bare version without an entry name" {
run pyenv-binary-package 3.12.7 --archive-base-url http://x/b
assert_failure "pyenv-binary: expected <version>:<entry>, e.g. \`3.13.14:3.13.14-debian-12'"
}
@test "rejects an entry name containing a slash" {
run pyenv-binary-package "3.12.7:foo/bar" --archive-base-url http://x/b
assert_failure "pyenv-binary: invalid entry name \`foo/bar'"
}
@test "rejects \`latest' as an entry name" {
run pyenv-binary-package 3.12:latest --archive-base-url http://x/b
assert_failure "pyenv-binary: \`latest' cannot be used as an entry name"
}
@test "refuses to package on macOS before compiling anything" {
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
run pyenv-binary-package 3.12.7:3.12.7-test --archive-base-url http://x/b
assert_failure "pyenv-binary: macOS archives are not supported yet"
}
@test "writes the archive, metadata and definition under the entry name" {
stub_build_environment
cd "${BATS_TEST_TMPDIR}"
run pyenv-binary-package 3.12.7:3.12.7-test \
--archive-base-url http://example.com/binaries
assert_success
assert [ -d "${PYENV_ROOT}/versions/3.12.7-test" ]
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-test.tar.gz" ]
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-test.meta" ]
run grep '^ARCHIVE_URL=' "${BATS_TEST_TMPDIR}/3.12.7-test"
assert_success "ARCHIVE_URL=http://example.com/binaries/3.12.7-test.tar.gz"
}
@test "strips a trailing slash from the archive base url" {
stub_build_environment
cd "${BATS_TEST_TMPDIR}"
run pyenv-binary-package 3.12.7:3.12.7-test \
--archive-base-url http://example.com/binaries/
assert_success
run grep '^ARCHIVE_URL=' "${BATS_TEST_TMPDIR}/3.12.7-test"
assert_success "ARCHIVE_URL=http://example.com/binaries/3.12.7-test.tar.gz"
}

View File

@ -12,7 +12,7 @@ platform() {
@test "fails with no version given" {
run pyenv-binary-save
assert_failure "Usage: pyenv binary save <version> [<output-dir>]"
assert_failure "Usage: pyenv binary save <version> [<output-dir>] [--name <name>]"
}
@test "fails for a version that is not installed" {
@ -44,6 +44,29 @@ platform() {
assert_line 0 "3.12.7/"
}
@test "uses an explicit package name" {
create_version "3.12.7"
local out="${BATS_TEST_TMPDIR}/dist"
run pyenv-binary-save "3.12.7" "$out" --name "custom"
assert_success "Saved custom.tar.gz and custom.meta to $out"
assert [ -f "${out}/custom.tar.gz" ]
run grep '^archive=' "${out}/custom.meta"
assert_success "archive=custom.tar.gz"
}
@test "fails when --name has no value" {
run pyenv-binary-save "3.12.7" --name
assert_failure "pyenv-binary: --name needs a value"
}
@test "rejects an invalid package name" {
create_version "3.12.7"
run pyenv-binary-save "3.12.7" --name "foo/bar"
assert_failure "pyenv-binary: invalid package name \`foo/bar'"
}
@test "records the platform in the metadata" {
create_version "3.12.7"
local out="${BATS_TEST_TMPDIR}/dist"

View File

@ -4,7 +4,7 @@
#
# Usage: pyenv install [-f] [-kvp] <version>[:<alias>]...
# pyenv install [-f] [-kvp] <definition-file>[:<alias>]
# pyenv install -l|--list
# pyenv install -l|--list [--bare]
# pyenv install --version
#
# -l/--list List all available versions
@ -45,6 +45,7 @@ shopt -u nullglob
# Provide pyenv completions
if [ "$1" = "--complete" ]; then
echo --bare
echo --list
echo --force
echo --skip-existing
@ -79,6 +80,8 @@ unset KEEP
unset VERBOSE
unset HAS_PATCH
unset DEBUG
unset BARE
unset LIST
[ -n "$PYENV_DEBUG" ] && VERBOSE="-v"
@ -88,10 +91,11 @@ for option in "${OPTIONS[@]}"; do
"h" | "help" )
usage 0
;;
"bare" )
BARE=1
;;
"l" | "list" )
echo "Available versions:"
definitions | indent
exit
LIST=1
;;
"f" | "force" )
FORCE=true
@ -120,6 +124,16 @@ for option in "${OPTIONS[@]}"; do
esac
done
if [[ -n $LIST ]]; then
if [[ -n $BARE ]]; then
definitions
else
echo "Available versions:"
definitions | indent
fi
exit
fi
unset VERSION_NAME
# The first argument contains the definition to install. If the

View File

@ -0,0 +1,49 @@
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
VERSION='25.2.4'
colorize 1 "GraalPy 23.1 and later installed by python-build use the faster Oracle GraalVM distribution" && echo
colorize 1 "Oracle GraalVM uses the GFTC license, which is free for development and production use, see https://medium.com/graalvm/161527df3d76" && echo
colorize 1 "The GraalVM Community Edition variant of GraalPy is also available, under the name graalpy3.12-community-${VERSION}" && echo
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
case "$graalpy_arch" in
"linux-amd64" )
checksum="b3d0766ae6d55daa15f0db1f6c884383d7eec506b186d0ba2f702523a78ff28d"
;;
"linux-aarch64" )
checksum="e57472272b1b659ae6ac972117723b0515a49cc9577688ed5563919793170d67"
;;
"macos-aarch64" )
checksum="79f0e1fefb42bb484122ae9e50cb21ce14541d18e6bd66c498e44b459ce652df"
;;
* )
{ echo
colorize 1 "ERROR"
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
echo
} >&2
exit 1
;;
esac
install_package "graalpy3.12-${VERSION}" "https://github.com/oracle/graalpython/releases/download/graal-${VERSION}/graalpy3.12-${VERSION}-${graalpy_arch}.tar.gz#${checksum}" "copy" ensurepip

View File

@ -0,0 +1,44 @@
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
VERSION='25.2.4'
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
case "$graalpy_arch" in
"linux-amd64" )
checksum="98af303a6b714bde39077354733254f78023ddef06d61e14cb66a3a01f10ae82"
;;
"linux-aarch64" )
checksum="dda5e97c8b38158184dec189addaece57a37207a0dad38be162621d605f964f5"
;;
"macos-aarch64" )
checksum="76a182713660ce69f640b9666c2861ec8a5839637a972eaa80c6b5cf38f1357b"
;;
* )
{ echo
colorize 1 "ERROR"
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
echo
} >&2
exit 1
;;
esac
install_package "graalpy3.12-community-${VERSION}" "https://github.com/oracle/graalpython/releases/download/graal-${VERSION}/graalpy3.12-community-${VERSION}-${graalpy_arch}.tar.gz#${checksum}" "copy" ensurepip

View File

@ -160,6 +160,22 @@ OUT
unstub python-build
}
@test "list available versions with --bare" {
stub_python_build_lib
stub_python_build "--definitions : echo 2.6.9 2.7.9-rc1 2.7.9-rc2 3.4.2 | tr ' ' $'\\n'"
run pyenv-install --list --bare
assert_success
assert_output <<OUT
2.6.9
2.7.9-rc1
2.7.9-rc2
3.4.2
OUT
unstub python-build
}
@test "upgrade instructions given for a nonexistent version" {
stub brew false
stub_python_build_lib
@ -265,6 +281,7 @@ OUT
run pyenv-install --complete
assert_success
assert_output <<OUT
--bare
--list
--force
--skip-existing

View File

@ -35,7 +35,7 @@ setup() {
unset xdg_var
# Workaround for Powershell. When tests are run from a terminal,
# and running a script fron a here-document,
# and running a script from a here-document,
# Powershell 7.5.4 erroneously prints ANSI escape sequences
# even if its output is redirected, breaking the comparison logic
export NO_COLOR=1