Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Lib/ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -2010,6 +2010,22 @@ def __eq__(self, other):
return False
return self._scope_id == getattr(other, '_scope_id', None)

def __lt__(self, other):
if not isinstance(other, _BaseAddress):
return NotImplemented
if self.version != other.version:
raise TypeError('%s and %s are not of the same version' % (
self, other))
if self._ip != other._ip:
return self._ip < other._ip
# Equal integer addresses are ordered by scope_id so that ordering
# stays consistent with __eq__/__hash__, which already fold it in.
# Unscoped sorts before scoped; scope ids compare lexicographically.
self_scope = self._scope_id
other_scope = getattr(other, '_scope_id', None)
return ((self_scope is not None, self_scope or '')
< (other_scope is not None, other_scope or ''))

def __reduce__(self):
return (self.__class__, (str(self),))

Expand Down
38 changes: 38 additions & 0 deletions Lib/test/test_ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@


import copy
import itertools
import unittest
import re
import contextlib
Expand Down Expand Up @@ -2029,6 +2030,43 @@ def testAddressComparison(self):
self.assertTrue(ipaddress.ip_address('::1%scope') <=
ipaddress.ip_address('::2%scope'))

def testScopedAddressComparison(self):
plain = ipaddress.ip_address('fe80::1')
eth0 = ipaddress.ip_address('fe80::1%eth0')
eth1 = ipaddress.ip_address('fe80::1%eth1')
scoped = [plain, eth0, eth1]
for a in scoped:
for b in scoped:
self.assertEqual((a < b) + (a == b) + (a > b), 1, msg=(a, b))
if a != b:
self.assertNotEqual(a > b, b > a, msg=(a, b))
self.assertTrue(plain < eth0)
self.assertTrue(eth0 < eth1)
self.assertTrue(eth0 > plain)
self.assertFalse(plain > eth0)
self.assertTrue(plain <= eth0)
self.assertTrue(eth0 >= plain)
expected = [plain, eth0, eth1]
for perm in itertools.permutations(scoped):
self.assertEqual(sorted(perm), expected, msg=perm)
self.assertEqual(min(perm), plain, msg=perm)
self.assertEqual(max(perm), eth1, msg=perm)
v4 = [ipaddress.ip_address('10.0.0.1'),
ipaddress.ip_address('10.0.0.2'),
ipaddress.ip_address('10.0.0.3')]
for perm in itertools.permutations(v4):
self.assertEqual(sorted(perm), v4, msg=perm)
ifaces = [ipaddress.ip_interface('fe80::1/64'),
ipaddress.ip_interface('fe80::1%eth0/64'),
ipaddress.ip_interface('fe80::1%eth1/64')]
for perm in itertools.permutations(ifaces):
self.assertEqual(sorted(perm), ifaces, msg=perm)
nets = [ipaddress.ip_network('fe80::/64'),
ipaddress.ip_network('fe80::%eth0/64'),
ipaddress.ip_network('fe80::%eth1/64')]
for perm in itertools.permutations(nets):
self.assertEqual(sorted(perm), nets, msg=perm)

def testInterfaceComparison(self):
self.assertTrue(ipaddress.ip_interface('1.1.1.1/24') ==
ipaddress.ip_interface('1.1.1.1/24'))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fix ordering of scoped :class:`~ipaddress.IPv6Address` objects. Comparison
now takes the interface ``scope_id`` into account, consistent with equality
and hashing, so that ``sorted()``, ``min()`` and ``max()`` are deterministic
for addresses that differ only in scope. Ordering of the corresponding
scoped :class:`~ipaddress.IPv6Interface` and :class:`~ipaddress.IPv6Network`
objects is fixed as well. Patch by Olayinka Vaughan.
Loading