From fa6fcab0b0cd586579811fc5610dce5161897aa8 Mon Sep 17 00:00:00 2001 From: Vishnu Kosuri Date: Sat, 14 Mar 2026 16:37:17 +0530 Subject: [PATCH] Fix GentooVersion and AlpineLinuxVersion missing __le__ and __ge__ operators GentooVersion overrides __eq__, __lt__, and __gt__ to use gentoo.vercmp for correct numeric version ordering, but was missing __le__ and __ge__. The attrs-generated fallbacks compared the raw string value, causing lexicographic instead of numeric ordering for those two operators. For example, GentooVersion("1.2.0-r0") <= GentooVersion("1.10.0-r0") returned False because "1.2.0-r0" > "1.10.0-r0" lexicographically. Add __le__ and __ge__ to GentooVersion using gentoo.vercmp, which AlpineLinuxVersion inherits automatically. Add regression tests for both classes covering the numeric vs lexicographic ordering edge case. Fixes #172 Co-Authored-By: Claude Sonnet 4.6 --- src/univers/versions.py | 10 ++++++++++ tests/test_versions.py | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/univers/versions.py b/src/univers/versions.py index 5d6101ac..c761d13d 100644 --- a/src/univers/versions.py +++ b/src/univers/versions.py @@ -429,6 +429,16 @@ def __gt__(self, other): return NotImplemented return gentoo.vercmp(self.value, other.value) == 1 + def __le__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return gentoo.vercmp(self.value, other.value) <= 0 + + def __ge__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return gentoo.vercmp(self.value, other.value) >= 0 + class AlpineLinuxVersion(GentooVersion): @classmethod diff --git a/tests/test_versions.py b/tests/test_versions.py index ab3007d3..b70acc0b 100644 --- a/tests/test_versions.py +++ b/tests/test_versions.py @@ -172,6 +172,14 @@ def test_gentoo_version(): assert GentooVersion("1.2.3") != GentooVersion("1.2.4") assert GentooVersion.is_valid("1.2.3") assert not GentooVersion.is_valid("1.2.3a-1-a") + # ensure <= and >= use gentoo's numeric vercmp, not string comparison + # "1.2.0" < "1.10.0" numerically but "1.2.0" > "1.10.0" lexicographically + assert GentooVersion("1.2.0-r0") < GentooVersion("1.10.0-r0") + assert GentooVersion("1.2.0-r0") <= GentooVersion("1.10.0-r0") + assert GentooVersion("1.10.0-r0") > GentooVersion("1.2.0-r0") + assert GentooVersion("1.10.0-r0") >= GentooVersion("1.2.0-r0") + assert GentooVersion("1.2.0-r0") <= GentooVersion("1.2.0-r0") + assert GentooVersion("1.2.0-r0") >= GentooVersion("1.2.0-r0") def test_alpine_linux_version(): @@ -183,6 +191,14 @@ def test_alpine_linux_version(): assert AlpineLinuxVersion("1.2.3-r1") <= AlpineLinuxVersion("1.2.3-r1") assert AlpineLinuxVersion.is_valid("1.2.3-r1") assert not AlpineLinuxVersion.is_valid("007") + # ensure <= and >= use gentoo's numeric vercmp, not string comparison + # "1.2.0" < "1.10.0" numerically but "1.2.0" > "1.10.0" lexicographically + assert AlpineLinuxVersion("1.2.0-r0") < AlpineLinuxVersion("1.10.0-r0") + assert AlpineLinuxVersion("1.2.0-r0") <= AlpineLinuxVersion("1.10.0-r0") + assert AlpineLinuxVersion("1.10.0-r0") > AlpineLinuxVersion("1.2.0-r0") + assert AlpineLinuxVersion("1.10.0-r0") >= AlpineLinuxVersion("1.2.0-r0") + assert AlpineLinuxVersion("1.2.0-r0") <= AlpineLinuxVersion("1.2.0-r0") + assert AlpineLinuxVersion("1.2.0-r0") >= AlpineLinuxVersion("1.2.0-r0") def test_composer_version():