From fb12effcc0124e56ddd8192e9133658a6ca3b4f4 Mon Sep 17 00:00:00 2001 From: HyperionBurn Date: Sun, 18 Oct 2026 17:10:25 +0400 Subject: [PATCH] Compare Bash version components numerically in the completion gate BashComplete._check_version() gates shell completion on Bash being at least 4.4, but it compared the major and minor components as strings. Lexicographic ordering places "10" before "4", so any Bash whose major or minor component reaches 10 is reported as older than 4.4 and completion is incorrectly disabled. Parse the components as integers instead. --- .../test_completion_bash_version.py | 54 +++++++++++++++++++ typer/_completion_classes.py | 4 +- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tests/test_completion/test_completion_bash_version.py diff --git a/tests/test_completion/test_completion_bash_version.py b/tests/test_completion/test_completion_bash_version.py new file mode 100644 index 0000000000..1d76a24680 --- /dev/null +++ b/tests/test_completion/test_completion_bash_version.py @@ -0,0 +1,54 @@ +import shutil +import subprocess +from unittest import mock + +import pytest +from typer import _click +from typer._completion_classes import BashComplete + + +def _check_version_with(bash_version: str) -> bool: + """Run BashComplete._check_version against a faked Bash and report + whether it warned that completion is unsupported. + """ + completed = subprocess.CompletedProcess( + args=["bash", "--norc", "-c", 'echo "${BASH_VERSION}"'], + returncode=0, + stdout=f"{bash_version}\n".encode(), + ) + with ( + mock.patch.object(shutil, "which", return_value="/usr/bin/bash"), + mock.patch.object(subprocess, "run", return_value=completed), + mock.patch.object(_click.utils, "echo") as echo, + ): + BashComplete._check_version() + return echo.called + + +@pytest.mark.parametrize( + "bash_version", + ["3.2.57", "4.0.0", "4.3.9", "4.3.99"], +) +def test_bash_too_old_is_reported_unsupported(bash_version: str): + assert _check_version_with(bash_version) is True + + +@pytest.mark.parametrize( + "bash_version", + ["4.4.0", "4.5.0", "4.9.0", "5.0.0", "5.3.0"], +) +def test_bash_new_enough_is_supported(bash_version: str): + assert _check_version_with(bash_version) is False + + +@pytest.mark.parametrize( + "bash_version", + ["4.10.0", "4.11.2", "5.10.0", "10.0.0", "12.3.4"], +) +def test_bash_two_digit_version_components_are_supported(bash_version: str): + """Version components must be compared numerically. + + "10" sorts before "4" as a string, so a lexicographic comparison wrongly + reports these versions as older than 4.4. + """ + assert _check_version_with(bash_version) is False diff --git a/typer/_completion_classes.py b/typer/_completion_classes.py index cfae02c9bf..4af5804f53 100644 --- a/typer/_completion_classes.py +++ b/typer/_completion_classes.py @@ -77,9 +77,9 @@ def _check_version() -> None: match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) if match is not None: - major, minor = match.groups() + major, minor = (int(part) for part in match.groups()) - if major < "4" or major == "4" and minor < "4": + if major < 4 or (major == 4 and minor < 4): _click.utils.echo( "Shell completion is not supported for Bash versions older than 4.4.", err=True,