Provide a stable and consistent sort order for Range objects.

This matches postgres server-side behaviour and helps client applications that need to sort based on the primary key of tables where the primary key is or contains a range.
This commit is contained in:
Chris Withers 2014-02-12 08:11:59 +00:00 committed by Daniele Varrazzo
parent 434fbb02b1
commit f739576f0a
2 changed files with 72 additions and 10 deletions

View File

@ -133,12 +133,21 @@ class Range(object):
def __hash__(self):
return hash((self._lower, self._upper, self._bounds))
def __lt__(self, other):
raise TypeError(
'Range objects cannot be ordered; please refer to the PostgreSQL'
' documentation to perform this operation in the database')
# as the postgres docs describe for the server-side stuff,
# ordering is rather arbitrary, but will remain stable
# and consistent.
__le__ = __gt__ = __ge__ = __lt__
def __lt__(self, other):
if not isinstance(other, Range):
return False
return ((self._lower, self._upper, self._bounds) <
(other._lower, other._upper, other._bounds))
def __le__(self, other):
if not isinstance(other, Range):
return False
return ((self._lower, self._upper, self._bounds) <=
(other._lower, other._upper, other._bounds))
def register_range(pgrange, pyrange, conn_or_curs, globally=False):

View File

@ -1225,12 +1225,65 @@ class RangeTestCase(unittest.TestCase):
self.assertEqual(Range(10, 20), IntRange(10, 20))
self.assertEqual(PositiveIntRange(10, 20), IntRange(10, 20))
def test_not_ordered(self):
# as the postgres docs describe for the server-side stuff,
# ordering is rather arbitrary, but will remain stable
# and consistent.
def test_ordering_lt(self):
from psycopg2.extras import Range
self.assertRaises(TypeError, lambda: Range(empty=True) < Range(0,4))
self.assertRaises(TypeError, lambda: Range(1,2) > Range(0,4))
self.assertRaises(TypeError, lambda: Range(1,2) <= Range())
self.assertRaises(TypeError, lambda: Range(1,2) >= Range())
self.assertTrue(Range(empty=True) < Range(0, 4))
self.assertFalse(Range(1, 2) < Range(0, 4))
self.assertTrue(Range(0, 4) < Range(1, 2))
self.assertFalse(Range(1, 2) < Range())
self.assertTrue(Range() < Range(1, 2))
self.assertFalse(Range(1) < Range(upper=1))
self.assertFalse(Range() < Range())
self.assertFalse(Range(empty=True) < Range(empty=True))
self.assertFalse(Range(1, 2) < Range(1, 2))
self.assertTrue(1 < Range(1, 2))
self.assertFalse(Range(1, 2) < 1)
def test_ordering_gt(self):
from psycopg2.extras import Range
self.assertFalse(Range(empty=True) > Range(0, 4))
self.assertTrue(Range(1, 2) > Range(0, 4))
self.assertFalse(Range(0, 4) > Range(1, 2))
self.assertTrue(Range(1, 2) > Range())
self.assertFalse(Range() > Range(1, 2))
self.assertTrue(Range(1) > Range(upper=1))
self.assertFalse(Range() > Range())
self.assertFalse(Range(empty=True) > Range(empty=True))
self.assertFalse(Range(1, 2) > Range(1, 2))
self.assertFalse(1 > Range(1, 2))
self.assertTrue(Range(1, 2) > 1)
def test_ordering_le(self):
from psycopg2.extras import Range
self.assertTrue(Range(empty=True) <= Range(0, 4))
self.assertFalse(Range(1, 2) <= Range(0, 4))
self.assertTrue(Range(0, 4) <= Range(1, 2))
self.assertFalse(Range(1, 2) <= Range())
self.assertTrue(Range() <= Range(1, 2))
self.assertFalse(Range(1) <= Range(upper=1))
self.assertTrue(Range() <= Range())
self.assertTrue(Range(empty=True) <= Range(empty=True))
self.assertTrue(Range(1, 2) <= Range(1, 2))
self.assertTrue(1 <= Range(1, 2))
self.assertFalse(Range(1, 2) <= 1)
def test_ordering_ge(self):
from psycopg2.extras import Range
self.assertFalse(Range(empty=True) >= Range(0, 4))
self.assertTrue(Range(1, 2) >= Range(0, 4))
self.assertFalse(Range(0, 4) >= Range(1, 2))
self.assertTrue(Range(1, 2) >= Range())
self.assertFalse(Range() >= Range(1, 2))
self.assertTrue(Range(1) >= Range(upper=1))
self.assertTrue(Range() >= Range())
self.assertTrue(Range(empty=True) >= Range(empty=True))
self.assertTrue(Range(1, 2) >= Range(1, 2))
self.assertFalse(1 >= Range(1, 2))
self.assertTrue(Range(1, 2) >= 1)
def skip_if_no_range(f):