diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index f1062a8cd052a55..0fcbe32c06ae355 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -2002,6 +2002,21 @@ def __str__(self): def __hash__(self): return hash((self._ip, self._scope_id)) + def __lt__(self, other): + if not isinstance(other, IPv6Address): + return NotImplemented + if self._ip != other._ip: + return self._ip < other._ip + self_scope = self._scope_id + other_scope = other._scope_id + if self_scope is None and other_scope is None: + return False + if other_scope is None: + return False + if self_scope is None: + return True + return self_scope < other_scope + def __eq__(self, other): address_equal = super().__eq__(other) if address_equal is NotImplemented: diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 3f017b97dc28a38..e8a5bec7e7bd34a 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -986,6 +986,17 @@ def test_same_type_ordering(self): self.assertFalse(lhs >= rhs) self.assertFalse(rhs <= lhs) + def test_scoped_ipv6_ordering_same_ip(self): + # gh-151769: addresses with the same integer but different + # scope_id must be ordered; None < any string + unscoped = self.v6addr + scoped = ipaddress.IPv6Address(str(unscoped) + '%eth0') + self.assertNotEqual(unscoped, scoped) + self.assertLess(unscoped, scoped) + self.assertGreater(scoped, unscoped) + # sorted() must be deterministic + self.assertEqual(sorted([scoped, unscoped]), [unscoped, scoped]) + def test_containment(self): for obj in self.v4_addresses: self.assertIn(obj, self.v4net) diff --git a/Misc/NEWS.d/next/Library/2026-06-22-00-00-00.gh-issue-151769.XxYyZz.rst b/Misc/NEWS.d/next/Library/2026-06-22-00-00-00.gh-issue-151769.XxYyZz.rst new file mode 100644 index 000000000000000..c3ae5c2948eb6b2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-22-00-00-00.gh-issue-151769.XxYyZz.rst @@ -0,0 +1,3 @@ +Fix :class:`ipaddress.IPv6Address` ordering to account for ``scope_id``, +so addresses with the same IP but different scope IDs compare as ordered +and :func:`sorted` is deterministic.