Skip to content

Commit

Permalink
Support major Python version syntax in runtime.txt (#322)
Browse files Browse the repository at this point in the history
Historically the `runtime.txt` file has only supported specifying an
exact Python version, in the form `python-3.X.Y`.

This adds support for the `python-3.X` form too, which means
the app will automatically receive new Python patch updates
during subsequent builds (similar to what's already supported
for the `.python-version` file).

This means the Python CNB's `runtime.txt` supported syntax
now matches that supported by the classic Python buildpack
(which gained support for the major version form as part of
adding support for the `.python-version` file).

The `runtime.txt` file remains deprecated (a deprecation
warning will be added shortly), however, in the meantime
this improves parity between the classic buildpack and CNB.

GUS-W-17660224.
  • Loading branch information
edmorley authored Jan 23, 2025
1 parent 1bb68e5 commit aef5e69
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added `runtime.txt` support for the `python-3.X` major Python version form. ([#322](https://github.com/heroku/buildpacks-python/pull/322))
- Enabled `libcnb`'s `trace` feature. ([#320](https://github.com/heroku/buildpacks-python/pull/320))

## [0.23.0] - 2025-01-13
Expand Down
19 changes: 12 additions & 7 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::layers::python::PythonLayerError;
use crate::package_manager::DeterminePackageManagerError;
use crate::python_version::{
RequestedPythonVersion, RequestedPythonVersionError, ResolvePythonVersionError,
DEFAULT_PYTHON_FULL_VERSION, DEFAULT_PYTHON_VERSION, NEWEST_SUPPORTED_PYTHON_3_MINOR_VERSION,
DEFAULT_PYTHON_VERSION, NEWEST_SUPPORTED_PYTHON_3_MINOR_VERSION,
OLDEST_SUPPORTED_PYTHON_3_MINOR_VERSION,
};
use crate::python_version_file::ParsePythonVersionFileError;
Expand Down Expand Up @@ -205,17 +205,22 @@ fn on_requested_python_version_error(error: RequestedPythonVersionError) {
log_error(
"Invalid Python version in runtime.txt",
formatdoc! {"
The Python version specified in 'runtime.txt' is not in the correct format.
The Python version specified in 'runtime.txt' isn't in
the correct format.
The following file contents were found:
{cleaned_contents}
However, the file contents must begin with a 'python-' prefix, followed by the
version specified as '<major>.<minor>.<patch>'. Comments are not supported.
However, the version must be specified as either:
1. 'python-<major>.<minor>' (recommended, for automatic updates)
2. 'python-<major>.<minor>.<patch>' (to pin to an exact version)
Remember to include the 'python-' prefix. Comments aren't
supported.
For example, to request Python {DEFAULT_PYTHON_FULL_VERSION}, update the 'runtime.txt' file so it
contains exactly:
python-{DEFAULT_PYTHON_FULL_VERSION}
For example, to request the latest version of Python {DEFAULT_PYTHON_VERSION},
update the 'runtime.txt' file so it contains:
python-{DEFAULT_PYTHON_VERSION}
"},
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/python_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub(crate) const DEFAULT_PYTHON_VERSION: RequestedPythonVersion = RequestedPytho
patch: None,
origin: PythonVersionOrigin::BuildpackDefault,
};

#[cfg(test)]
pub(crate) const DEFAULT_PYTHON_FULL_VERSION: PythonVersion = LATEST_PYTHON_3_13;

pub(crate) const OLDEST_SUPPORTED_PYTHON_3_MINOR_VERSION: u16 = 9;
Expand Down
23 changes: 16 additions & 7 deletions src/runtime_txt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::python_version::{PythonVersionOrigin, RequestedPythonVersion};

/// Parse the contents of a `runtime.txt` file into a [`RequestedPythonVersion`].
///
/// The file is expected to contain a string of form `python-X.Y.Z`.
/// The file is expected to contain a string of form `python-X.Y.Z` or `python-X.Y`.
/// Any leading or trailing whitespace will be removed.
pub(crate) fn parse(contents: &str) -> Result<RequestedPythonVersion, ParseRuntimeTxtError> {
// All leading/trailing whitespace is trimmed, since that's what the classic buildpack
Expand Down Expand Up @@ -30,6 +30,12 @@ pub(crate) fn parse(contents: &str) -> Result<RequestedPythonVersion, ParseRunti
patch: Some(patch),
origin: PythonVersionOrigin::RuntimeTxt,
}),
[major, minor] => Ok(RequestedPythonVersion {
major,
minor,
patch: None,
origin: PythonVersionOrigin::RuntimeTxt,
}),
_ => Err(ParseRuntimeTxtError {
cleaned_contents: cleaned_contents.clone(),
}),
Expand All @@ -48,6 +54,15 @@ mod tests {

#[test]
fn parse_valid() {
assert_eq!(
parse("python-1.2"),
Ok(RequestedPythonVersion {
major: 1,
minor: 2,
patch: None,
origin: PythonVersionOrigin::RuntimeTxt
})
);
assert_eq!(
parse("python-1.2.3"),
Ok(RequestedPythonVersion {
Expand Down Expand Up @@ -139,12 +154,6 @@ mod tests {
cleaned_contents: "python-1".to_string(),
})
);
assert_eq!(
parse("python-1.2"),
Err(ParseRuntimeTxtError {
cleaned_contents: "python-1.2".to_string(),
})
);
assert_eq!(
parse("python-1.2.3.4"),
Err(ParseRuntimeTxtError {
Expand Down
17 changes: 11 additions & 6 deletions tests/python_version_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,17 +392,22 @@ fn runtime_txt_invalid_version() {
context.pack_stderr,
&formatdoc! {"
[Error: Invalid Python version in runtime.txt]
The Python version specified in 'runtime.txt' is not in the correct format.
The Python version specified in 'runtime.txt' isn't in
the correct format.
The following file contents were found:
python-an.invalid.version
However, the file contents must begin with a 'python-' prefix, followed by the
version specified as '<major>.<minor>.<patch>'. Comments are not supported.
However, the version must be specified as either:
1. 'python-<major>.<minor>' (recommended, for automatic updates)
2. 'python-<major>.<minor>.<patch>' (to pin to an exact version)
Remember to include the 'python-' prefix. Comments aren't
supported.
For example, to request Python {DEFAULT_PYTHON_FULL_VERSION}, update the 'runtime.txt' file so it
contains exactly:
python-{DEFAULT_PYTHON_FULL_VERSION}
For example, to request the latest version of Python {DEFAULT_PYTHON_VERSION},
update the 'runtime.txt' file so it contains:
python-{DEFAULT_PYTHON_VERSION}
"}
);
});
Expand Down

0 comments on commit aef5e69

Please sign in to comment.