Skip to content

Commit 868bbed

Browse files
committed
Fix handling of version files that contain CRLFs
From looking at metrics, I noticed that the `cat --show-nonprinting` approach added in #1783 to aid the visualisation of invisible characters (such as ASCII control characters) also escapes carriage return characters (to `^M`), which we don't want. Instead, `sed` is now used which replaces anything not matching the `:print:` and `:space:` groups with the Unicode substitution character (`�`) - which means the classic buildpack now also matches the CNB's behaviour: https://github.com/heroku/buildpacks-python/blob/f2fdd00edf0f63b298cf88c82377885c88666440/src/python_version_file.rs#L16-L19 For a mapping of what `:print:` and `:space:` cover, see the chart at the bottom of this page: https://en.cppreference.com/w/cpp/string/byte/isprint GUS-W-18225347.
1 parent 7f983c3 commit 868bbed

File tree

5 files changed

+14
-4
lines changed

5 files changed

+14
-4
lines changed

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+
- Fix parsing of `runtime.txt` and `.python-version` files that contain CRLF characters. ([#1789](https://github.com/heroku/heroku-buildpack-python/pull/1789))
56

67
## [v283] - 2025-05-06
78

lib/python_version.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,15 @@ function python_version::read_requested_python_version() {
5252

5353
local runtime_txt_path="${build_dir}/runtime.txt"
5454
if [[ -f "${runtime_txt_path}" ]]; then
55-
contents="$(cat --show-nonprinting "${runtime_txt_path}")"
55+
contents="$(utils::read_file_with_special_chars_substituted "${runtime_txt_path}")"
5656
version="$(python_version::parse_runtime_txt "${contents}")"
5757
origin="runtime.txt"
5858
return 0
5959
fi
6060

6161
local python_version_file_path="${build_dir}/.python-version"
6262
if [[ -f "${python_version_file_path}" ]]; then
63-
contents="$(cat --show-nonprinting "${python_version_file_path}")"
63+
contents="$(utils::read_file_with_special_chars_substituted "${python_version_file_path}")"
6464
version="$(python_version::parse_python_version_file "${contents}")"
6565
origin=".python-version"
6666
return 0

lib/utils.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ function utils::get_requirement_version() {
1616
echo "${requirement_version}"
1717
}
1818

19+
# Replaces any invisible unwanted characters (such as ASCII control codes) with the Unicode
20+
# replacement character, so they are visible in error messages. Also removes any carriage
21+
# return characters, to prevent them interfering with the rendering of error messages that
22+
# include the raw file contents.
23+
function utils::read_file_with_special_chars_substituted() {
24+
local file="${1}"
25+
sed --regexp-extended --expression 's/[^[:print:][:space:]]/�/g' --expression 's/\r$//' "${file}"
26+
}
27+
1928
# Python bundles pip within its standard library, which we can use to install our chosen
2029
# pip version from PyPI, saving us from having to download the usual pip bootstrap script.
2130
function utils::bundled_pip_module_path() {
185 Bytes
Binary file not shown.

spec/hatchet/python_version_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@
322322
remote: ! isn't in the correct format.
323323
remote: !
324324
remote: ! The following version was found:
325-
remote: ! 3.12.0^[
325+
remote: ! �3.�12.0
326326
remote: !
327327
remote: ! However, the Python version must be specified as either:
328328
remote: ! 1. The major version only, for example: #{DEFAULT_PYTHON_MAJOR_VERSION} (recommended)
@@ -522,7 +522,7 @@
522522
remote: ! in the correct format.
523523
remote: !
524524
remote: ! The following file contents were found, which aren't valid:
525-
remote: ! python-3.12.0^[
525+
remote: ! python-3.12.0
526526
remote: !
527527
remote: ! However, the runtime.txt file is deprecated since it has been
528528
remote: ! replaced by the more widely supported .python-version file:

0 commit comments

Comments
 (0)