From db7ebab2ce56286db9ec5fd143e8df0e51a4dc95 Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Thu, 23 Jan 2025 14:30:53 +0000 Subject: [PATCH] Support major Python version syntax in `runtime.txt` 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. 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. --- CHANGELOG.md | 1 + src/errors.rs | 19 ++++++++++++------- src/python_version.rs | 2 ++ src/runtime_txt.rs | 23 ++++++++++++++++------- tests/python_version_test.rs | 17 +++++++++++------ 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14cbc39..44020a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/errors.rs b/src/errors.rs index 47c614f..bea4f45 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -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; @@ -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 '..'. Comments are not supported. + However, the version must be specified as either: + 1. 'python-.' (recommended, for automatic updates) + 2. 'python-..' (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} "}, ); } diff --git a/src/python_version.rs b/src/python_version.rs index f11c25d..75913fa 100644 --- a/src/python_version.rs +++ b/src/python_version.rs @@ -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; diff --git a/src/runtime_txt.rs b/src/runtime_txt.rs index 70b7f9d..69260f9 100644 --- a/src/runtime_txt.rs +++ b/src/runtime_txt.rs @@ -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 { // All leading/trailing whitespace is trimmed, since that's what the classic buildpack @@ -30,6 +30,12 @@ pub(crate) fn parse(contents: &str) -> Result Ok(RequestedPythonVersion { + major, + minor, + patch: None, + origin: PythonVersionOrigin::RuntimeTxt, + }), _ => Err(ParseRuntimeTxtError { cleaned_contents: cleaned_contents.clone(), }), @@ -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 { @@ -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 { diff --git a/tests/python_version_test.rs b/tests/python_version_test.rs index 3fd4f98..ace838b 100644 --- a/tests/python_version_test.rs +++ b/tests/python_version_test.rs @@ -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 '..'. Comments are not supported. + However, the version must be specified as either: + 1. 'python-.' (recommended, for automatic updates) + 2. 'python-..' (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} "} ); });