From bb5ac226d6aed609d2082cad9f53f558fe2f5c2c Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:26:51 +0100 Subject: [PATCH] fix: find a python3.x beside an old python3 --- install.sh | 16 +++++++++++++++- tests/test_install.py | 29 +++++++++++++++++++++++++++++ wiki/installer.md | 4 ++-- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/install.sh b/install.sh index d5303ddf2..b814f7f24 100755 --- a/install.sh +++ b/install.sh @@ -130,11 +130,25 @@ fi # A Python can be built without ssl, unicodedata or zlib, and the installer # needs all three: a python3 lacking ssl was chosen over a full python and # every download then failed as if the network were down. +# A python3.X on PATH counts too, newest first: Buster and Ubuntu 18.04 +# keep python3 at 3.7 or 3.6 beside the python3.8 the advice below has the +# user install, and the same advice looped. +versioned_pythons() { + ( + IFS=: + for dir in $PATH; do + for path in "$dir"/python3.[0-9]*; do + [ -x "$path" ] && basename -- "$path" + done + done + ) | grep -E '^python3\.[0-9]+$' | sort -t . -k 2,2nr -u +} + find_python() { PYTHON="" PYTHON_LACKING="" PYTHON_OLD="" - for command_name in python3 python; do + for command_name in python3 python $(versioned_pythons); do command -v "$command_name" >/dev/null 2>&1 || continue if ! "$command_name" -c 'import sys; raise SystemExit(sys.version_info < (3, 8))' \ 2>/dev/null; then diff --git a/tests/test_install.py b/tests/test_install.py index f878dc20e..b9b23f1af 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -2334,6 +2334,35 @@ class TestAvailablePlatforms(unittest.TestCase): self.assertEqual(sorted(p.name for p in root.iterdir()), ["bin", "started"]) + @unittest.skipIf(os.name == "nt", "the sh bootstrap is POSIX") + def test_a_versioned_python_is_found_beside_an_old_python3(self): + """Buster keeps python3 at 3.7: the user installs python3.8 as the + advice says, and the bootstrap, trying python3 and python alone, + gave the same advice again.""" + root = Path(_tempdir(self)) + shutil.copy(REPO_ROOT / "install.sh", root / "install.sh") + (root / "install.py").write_text("import sys\nprint('ran', sys.version_info >= (3, 8))\n") + fake_bin = root / "bin" + fake_bin.mkdir() + old = ("#!/bin/sh\ncase \"$*\" in *SystemExit*) exit 1 ;; esac\n" + "echo 3.7.3\n") + for name in ("python3", "python"): + (fake_bin / name).write_text(old) + (fake_bin / name).chmod(0o755) + tools = root / "tools" + tools.mkdir() + for tool in ("sh", "grep", "sort", "basename", "dirname", "uname", "sed", "tr"): + found = shutil.which(tool) + if found: + (tools / tool).symlink_to(found) + (fake_bin / "python3.11").symlink_to(sys.executable) + proc = subprocess.run( + [shutil.which("sh"), str(root / "install.sh")], + env={"PATH": f"{fake_bin}{os.pathsep}{tools}", "HOME": str(root)}, + capture_output=True, text=True, timeout=60, stdin=subprocess.DEVNULL) + self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr) + self.assertIn("ran True", proc.stdout) + def test_a_failed_download_says_so_in_one_message(self): """Offline, the one-liner printed curl's progress table and "curl: (6) ...", then exited 6, a code the table does not list.""" diff --git a/wiki/installer.md b/wiki/installer.md index e2b234b6d..1433949b7 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -1634,8 +1634,8 @@ site build refuses to publish a copy whose pin is not the SHA-256 of the deploys, so a failed check keeps the last good pair served. Any change to `install.py` therefore means recomputing that pin in both wrappers. -They locate a Python 3.8 or newer interpreter (`python3` then `python` on -POSIX, `py -3` then `python3` then `python` on Windows). Without one, +They locate a Python 3.8 or newer interpreter (`python3`, `python`, then each +`python3.X` on PATH, newest first, on POSIX, `py -3` then `python3` then `python` on Windows). Without one, `install.sh` names the command that installs it on the system it runs on (`pkg install python` in Termux, which it offers to run since Termux installs without root, `xcode-select --install` on macOS, apt, dnf, pacman, zypper or apk