Skip to content

Commit b494ced

Browse files
authored
Add support for Python 3.14 (#1927)
Add support for Python 3.14, and release 3.14.0. The default Python version remains unchanged (at 3.13) for now. Release announcement: https://blog.python.org/2025/10/python-3140-final-is-here.html https://www.python.org/downloads/release/python-3140/ Details on what's new in Python 3.14: https://docs.python.org/3.14/whatsnew/3.14.html The stdlib now has native zstd support, so I've updated the binary testing script to test that (optional) module was compiled. I've also expanded the command name tests to test the version specific binary exists too (eg `python3.14` and not just `python3` / `python`). Binary builds: https://github.com/heroku/heroku-buildpack-python/actions/runs/18317304753 Python 3.14 readiness status of the top 360 packages on PyPI: https://pyreadiness.org/3.14/ Note: We don't yet support the new free-threaded build variants (e.g.`3.14t`) since they will require further changes to version handling etc. Closes #1926. GUS-W-17595543. GUS-W-17595545.
1 parent 227fcbf commit b494ced

9 files changed

Lines changed: 38 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [Unreleased]
44

5+
- Added support for Python 3.14. ([#1927](https://github.com/heroku/heroku-buildpack-python/pull/1927))
56
- Stopped using `--with-system-expat` when compiling new Python versions. ([#1925](https://github.com/heroku/heroku-buildpack-python/pull/1925))
67

78
## [v312] - 2025-10-05

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ The current default Python version is: 3.13
4747

4848
The supported Python versions are:
4949

50+
- Python 3.14
5051
- Python 3.13
5152
- Python 3.12
5253
- Python 3.11

builds/build_python_runtime.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ case "${STACK:?}" in
3232
"3.11"
3333
"3.12"
3434
"3.13"
35+
"3.14"
3536
)
3637
;;
3738
*)
@@ -45,6 +46,10 @@ fi
4546

4647
# Sigstore identities taken from: https://www.python.org/downloads/metadata/sigstore/
4748
case "${PYTHON_MAJOR_VERSION}" in
49+
3.14)
50+
SIGSTORE_IDENTITY='hugo@python.org'
51+
SIGSTORE_ISSUER='https://github.com/login/oauth'
52+
;;
4853
3.12 | 3.13)
4954
SIGSTORE_IDENTITY='thomas@python.org'
5055
SIGSTORE_ISSUER='https://accounts.google.com'

builds/test_python_runtime.sh

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ export LD_LIBRARY_PATH="${INSTALL_DIR}/lib/"
2121

2222
tar --zstd --extract --verbose --file "${ARCHIVE_FILEPATH}" --directory "${INSTALL_DIR}"
2323

24-
# Check Python is able to start and is usable via both the default `python3` command and the
25-
# `python` symlink we create during the build. We use the full filepath rather than adding the
24+
# Check Python is able to start and is usable via both the default `python3` + `python3.XX` commands
25+
# and the `python` symlink we create during the build. We use the full filepath rather than adding the
2626
# directory to PATH to ensure the test doesn't pass because of falling through to system Python.
2727
"${INSTALL_DIR}/bin/python3" --version
28+
major_python_version="$("${INSTALL_DIR}/bin/python3" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")"
29+
"${INSTALL_DIR}/bin/python${major_python_version}" --version
2830
"${INSTALL_DIR}/bin/python" --version
2931

3032
# Check the Python config script still exists/works after the deletion of scripts with broken shebang lines.
@@ -65,6 +67,14 @@ optional_stdlib_modules=(
6567
xml.parsers.expat
6668
zlib
6769
)
70+
71+
# https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-zstandard
72+
if [[ "${major_python_version}" == "3.14" ]]; then
73+
optional_stdlib_modules+=(
74+
compression.zstd
75+
)
76+
fi
77+
6878
if "${INSTALL_DIR}/bin/python3" -c "import $(
6979
IFS=,
7080
echo "${optional_stdlib_modules[*]}"

lib/python_version.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ LATEST_PYTHON_3_10="3.10.18"
99
LATEST_PYTHON_3_11="3.11.13"
1010
LATEST_PYTHON_3_12="3.12.11"
1111
LATEST_PYTHON_3_13="3.13.7"
12+
LATEST_PYTHON_3_14="3.14.0"
1213

1314
OLDEST_SUPPORTED_PYTHON_3_MINOR_VERSION=9
14-
NEWEST_SUPPORTED_PYTHON_3_MINOR_VERSION=13
15+
NEWEST_SUPPORTED_PYTHON_3_MINOR_VERSION=14
1516

1617
DEFAULT_PYTHON_FULL_VERSION="${LATEST_PYTHON_3_13}"
1718
DEFAULT_PYTHON_MAJOR_VERSION="${DEFAULT_PYTHON_FULL_VERSION%.*}"
@@ -420,6 +421,7 @@ function python_version::resolve_python_version() {
420421
3.11) echo "${LATEST_PYTHON_3_11}" ;;
421422
3.12) echo "${LATEST_PYTHON_3_12}" ;;
422423
3.13) echo "${LATEST_PYTHON_3_13}" ;;
424+
3.14) echo "${LATEST_PYTHON_3_14}" ;;
423425
*) utils::abort_internal_error "Unhandled Python major version: ${requested_python_version}" ;;
424426
esac
425427
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Comments are supported
2+
3.14
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# This package has been picked since it has no dependencies and is small/fast to install.
2+
typing-extensions==4.12.2

spec/hatchet/python_version_spec.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
RSpec.shared_examples 'builds with the requested Python version' do |requested_version, resolved_version|
66
it "builds with Python #{requested_version}" do
77
app.deploy do |app|
8-
if requested_version == '3.13'
8+
if ['3.9', '3.10', '3.11', '3.12'].include?(requested_version)
99
expect(clean_output(app.output)).to include(<<~OUTPUT)
1010
remote: -----> Python app detected
1111
remote: -----> Using Python #{requested_version} specified in .python-version
1212
remote: -----> Installing Python #{resolved_version}
13-
remote: -----> Installing pip #{PIP_VERSION}
13+
remote: -----> Installing pip #{PIP_VERSION}, setuptools #{SETUPTOOLS_VERSION} and wheel #{WHEEL_VERSION}
1414
remote: -----> Installing dependencies using 'pip install -r requirements.txt'
1515
remote: Collecting typing-extensions==4.12.2 (from -r requirements.txt (line 2))
1616
OUTPUT
@@ -19,7 +19,7 @@
1919
remote: -----> Python app detected
2020
remote: -----> Using Python #{requested_version} specified in .python-version
2121
remote: -----> Installing Python #{resolved_version}
22-
remote: -----> Installing pip #{PIP_VERSION}, setuptools #{SETUPTOOLS_VERSION} and wheel #{WHEEL_VERSION}
22+
remote: -----> Installing pip #{PIP_VERSION}
2323
remote: -----> Installing dependencies using 'pip install -r requirements.txt'
2424
remote: Collecting typing-extensions==4.12.2 (from -r requirements.txt (line 2))
2525
OUTPUT
@@ -242,7 +242,7 @@
242242
remote: ! Alternatively, request an older Python version by creating
243243
remote: ! a .python-version file in the root directory of your app,
244244
remote: ! that contains a Python version like:
245-
remote: ! 3.13
245+
remote: ! 3.14
246246
remote:
247247
remote: ! Push rejected, failed to compile Python app.
248248
OUTPUT
@@ -310,6 +310,12 @@
310310
it_behaves_like 'builds with the requested Python version', '3.13', LATEST_PYTHON_3_13
311311
end
312312

313+
context 'when .python-version contains Python 3.14' do
314+
let(:app) { Hatchet::Runner.new('spec/fixtures/python_3.14') }
315+
316+
it_behaves_like 'builds with the requested Python version', '3.14', LATEST_PYTHON_3_14
317+
end
318+
313319
context 'when .python-version contains an invalid Python version string' do
314320
let(:app) { Hatchet::Runner.new('spec/fixtures/python_version_file_invalid_version', allow_failure: true) }
315321

@@ -460,7 +466,7 @@
460466
remote: ! https://devcenter.heroku.com/articles/managing-buildpacks#view-your-buildpacks
461467
remote: ! https://devcenter.heroku.com/articles/managing-buildpacks#classic-buildpacks-references
462468
remote: !
463-
remote: ! Otherwise, switch to a supported version (such as Python 3.13)
469+
remote: ! Otherwise, switch to a supported version (such as Python 3.14)
464470
remote: ! by changing the version in your .python-version file.
465471
remote:
466472
remote: ! Push rejected, failed to compile Python app.

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
LATEST_PYTHON_3_11 = '3.11.13'
1616
LATEST_PYTHON_3_12 = '3.12.11'
1717
LATEST_PYTHON_3_13 = '3.13.7'
18+
LATEST_PYTHON_3_14 = '3.14.0'
1819
DEFAULT_PYTHON_FULL_VERSION = LATEST_PYTHON_3_13
1920
DEFAULT_PYTHON_MAJOR_VERSION = DEFAULT_PYTHON_FULL_VERSION.gsub(/\.\d+$/, '')
2021

0 commit comments

Comments
 (0)