Skip to content

Commit

Permalink
Add support for the .python-version file
Browse files Browse the repository at this point in the history
Following on from the recent Python version refactoring, this now adds
support for configuring the app's Python version using a
`.python-version` file. This file is used by several tools in the Python
ecosystem (such as pyenv, `actions/setup-python`, uv), whereas the
existing `runtime.txt` file is proprietary to Heroku.

If both a `runtime.txt` file and a `.python-version` file are present,
then the `runtime.txt` file will take precedence. However, use of the
`.python-version` file is now recommended, since `runtime.txt` will
be deprecated in the future. Both the `runtime.txt` file and
`.python-version` file take precedence over any Python version specified
in a `Pipfile.lock` for Pipenv users.

We support the following `.python-version` syntax:
- Major Python version (e.g. `3.13`, which will then be resolved to the
  latest Python 3.13). (This form is recommended, since it allows for
  Python security updates to be pulled in without having to manually
  bump the version.)
- Exact Python version (e.g. `3.13.0`)
- Comments (lines starting with `#`)
- Blank lines

We don't support the following `.python-version` features:
- Specifying multiple Python versions
- Prefixing versions with `python-` (since this form is undocumented
  and will likely be deprecated by pyenv in the future)

This change is the classic Python buildpack equivalent of the Python
CNB change here:
heroku/buildpacks-python#272

In addition, the existing `runtime.txt` support has been updated to
allow specifying just the major Python version, in order to increase
feature parity between the files, and avoid confusion if users try to
use the major version only syntax from `.python-version` in their
`runtime.txt`.

Refs #913.
Refs #932.
GUS-W-7671453.
GUS-W-16821357.
  • Loading branch information
edmorley committed Oct 14, 2024
1 parent a2cf258 commit 1f42ba7
Show file tree
Hide file tree
Showing 90 changed files with 631 additions and 727 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased]

- Added support for configuring the Python version using a `.python-version` file. Both the `3.N` and `3.N.N` version forms are supported (the former is recommended). The existing `runtime.txt` file will take precedence if both files are found, however, we recommend switching to `.python-version` since it is more commonly supported in the Python ecosystem. ([#1664](https://github.com/heroku/heroku-buildpack-python/pull/1664))
- Added support for specifying only the Python major version in `runtime.txt` instead of requiring the full Python version (for example `python-3.N` instead of `python-3.N.N`). ([#1664](https://github.com/heroku/heroku-buildpack-python/pull/1664))

## [v260] - 2024-10-10

Expand Down
107 changes: 49 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,61 +8,52 @@ This is the official [Heroku buildpack](https://devcenter.heroku.com/articles/bu

Recommended web frameworks include **Django** and **Flask**, among others. The recommended webserver is **Gunicorn**. There are no restrictions around what software can be used (as long as it's pip-installable). Web processes must bind to `$PORT`, and only the HTTP protocol is permitted for incoming connections.

See it in Action
----------------
```
$ ls
my-application requirements.txt runtime.txt
$ git push heroku main
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 276 bytes | 276.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Python app detected
remote: -----> Installing python
remote: -----> Installing pip
remote: -----> Installing SQLite3
remote: -----> Installing requirements with pip
remote: Collecting flask (from -r /tmp/build_c2c067ef79ff14c9bf1aed6796f9ed1f/requirements.txt (line 1))
remote: Downloading ...
remote: Installing collected packages: Werkzeug, click, MarkupSafe, Jinja2, itsdangerous, flask
remote: Successfully installed Jinja2-2.10 MarkupSafe-1.1.0 Werkzeug-0.14.1 click-7.0 flask-1.0.2 itsdangerous-1.1.0
remote:
remote: -----> Discovering process types
remote: Procfile declares types -> (none)
remote:
```

A `requirements.txt` must be present at the root of your application's repository to deploy.

To specify your python version, you also need a `runtime.txt` file - unless you are using the default Python runtime version.

Current default Python Runtime: Python 3.12.7

Alternatively, you can provide a `setup.py` file, or a `Pipfile`.
Using `pipenv` will generate `runtime.txt` at build time if one of the field `python_version` or `python_full_version` is specified in the `requires` section of your `Pipfile`.

Specify a Buildpack Version
---------------------------

You can specify the latest production release of this buildpack for upcoming builds of an existing application:

$ heroku buildpacks:set heroku/python


Specify a Python Runtime
------------------------

Supported runtime options include:

- `python-3.13.0` on all [supported stacks](https://devcenter.heroku.com/articles/stack#stack-support-details)
- `python-3.12.7` on all [supported stacks](https://devcenter.heroku.com/articles/stack#stack-support-details)
- `python-3.11.10` on all [supported stacks](https://devcenter.heroku.com/articles/stack#stack-support-details)
- `python-3.10.15` on all [supported stacks](https://devcenter.heroku.com/articles/stack#stack-support-details)
- `python-3.9.20` on all [supported stacks](https://devcenter.heroku.com/articles/stack#stack-support-details)
- `python-3.8.20` on Heroku-20 only
## Example Usage

## Getting Started

See the [Getting Started on Heroku with Python](https://devcenter.heroku.com/articles/getting-started-with-python) tutorial.

## Application Requirements

A `requirements.txt` or `Pipfile` file must be present in the root (top-level) directory of your app's source code.

## Configuration

### Python Version

We recommend that apps specify a Python version rather than relying on the buildpack's default Python version.

For example, to request the latest patch release of Python 3.13, create a `.python-version` file in
the root directory of your app containing:
`3.13`

The buildpack will look for a Python version in the following places (in descending order of precedence):

1. `runtime.txt` file (deprecated)
2. `.python-version` file (recommended)
3. The `python_full_version` field in the `Pipfile.lock` file
4. The `python_version` field in the `Pipfile.lock` file

If none of those are found, the buildpack will use a default Python version (currently Python 3.12)
for the first build of an app, and then subsequent builds of that app will be pinned to that version
unless the build cache is cleared or you request a different version.

The supported Python versions are:

- Python 3.13
- Python 3.12
- Python 3.11
- Python 3.10

These Python versions are deprecated on Heroku:

- Python 3.9
- Python 3.8 (only available on the [Heroku-20](https://devcenter.heroku.com/articles/heroku-20-stack) stack)

Python versions older than those listed above are no longer supported, since they have reached
end-of-life [upstream](https://devguide.python.org/versions/#supported-versions).

## Documentation

For more information about using Python on Heroku, see [Dev Center](https://devcenter.heroku.com/categories/python-support).
3 changes: 3 additions & 0 deletions bin/compile
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ fi
python_version::read_requested_python_version "${BUILD_DIR}" "${package_manager}" "${cached_python_version}" requested_python_version python_version_origin
meta_set "python_version_reason" "${python_version_origin}"

# TODO: More strongly recommend specifying a Python version (eg switch the messaging to
# be a warning instead, after version resolution, and mention .python-version inline)
# TODO: Add runtime.txt deprecation warning.
case "${python_version_origin}" in
default)
puts-step "No Python version was specified. Using the buildpack default: Python ${requested_python_version}"
Expand Down
4 changes: 3 additions & 1 deletion bin/steps/python
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ PYTHON_URL="${S3_BASE_URL}/python-${python_full_version}-ubuntu-${UBUNTU_VERSION
# TODO: Update this message to be more specific once Python 3.8 support is dropped.
if ! curl --output /dev/null --silent --head --fail --retry 3 --retry-connrefused --connect-timeout 10 "${PYTHON_URL}"; then
display_error <<-EOF
Error: Python ${python_full_version} is not available for this stack (${STACK}).
Error: Python ${python_full_version} isn't available for this stack (${STACK}).
For a list of the supported Python versions, see:
https://devcenter.heroku.com/articles/python-support#supported-runtimes
Expand All @@ -35,6 +35,8 @@ function warn_if_patch_update_available() {
# Extract the patch version component of the version strings (ie: the '5' in '3.10.5').
local requested_patch_number="${requested_full_version##*.}"
local latest_patch_number="${latest_patch_version##*.}"
# TODO: Update this message to suggest using the .python-version major version syntax to stay up to date,
# once runtime.txt is deprecated and sticky-versioning only pins to the major version.
if ((requested_patch_number < latest_patch_number)); then
puts-warn
puts-warn "A Python security update is available! Upgrade as soon as possible to: Python ${latest_patch_version}"
Expand Down
Loading

0 comments on commit 1f42ba7

Please sign in to comment.