Skip to content

Commit 2769642

Browse files
authored
Merge pull request #862 from joernhees/total-order-patch
Total order patch patch
2 parents 0ab51d4 + 8946f9c commit 2769642

File tree

2 files changed

+82
-29
lines changed

2 files changed

+82
-29
lines changed

rdflib/term.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -760,31 +760,31 @@ def __gt__(self, other):
760760
This tries to implement this:
761761
http://www.w3.org/TR/sparql11-query/#modOrderBy
762762
763-
In short, Literals with compatible data-types are orderd in value space,
764-
i.e.
763+
In short, Literals with compatible data-types are ordered in value
764+
space, i.e.
765765
>>> from rdflib import XSD
766766
767-
>>> Literal(1)>Literal(2) # int/int
767+
>>> Literal(1) > Literal(2) # int/int
768768
False
769-
>>> Literal(2.0)>Literal(1) # double/int
769+
>>> Literal(2.0) > Literal(1) # double/int
770770
True
771771
>>> from decimal import Decimal
772772
>>> Literal(Decimal("3.3")) > Literal(2.0) # decimal/double
773773
True
774774
>>> Literal(Decimal("3.3")) < Literal(4.0) # decimal/double
775775
True
776-
>>> Literal('b')>Literal('a') # plain lit/plain lit
776+
>>> Literal('b') > Literal('a') # plain lit/plain lit
777777
True
778-
>>> Literal('b')>Literal('a', datatype=XSD.string) # plain lit/xsd:string
778+
>>> Literal('b') > Literal('a', datatype=XSD.string) # plain lit/xsd:str
779779
True
780780
781781
Incompatible datatype mismatches ordered by DT
782782
783-
>>> Literal(1)>Literal("2") # int>string
783+
>>> Literal(1) > Literal("2") # int>string
784784
False
785785
786786
Langtagged literals by lang tag
787-
>>> Literal("a", lang="en")>Literal("a", lang="fr")
787+
>>> Literal("a", lang="en") > Literal("a", lang="fr")
788788
False
789789
"""
790790
if other is None:
@@ -814,10 +814,10 @@ def __gt__(self, other):
814814
else:
815815
return self.language > other.language
816816

817-
if self.value != None and other.value != None:
818-
if type(self.value) in _NO_TOTAL_ORDER_TYPES:
819-
comparator = _NO_TOTAL_ORDER_TYPES[type(self.value)]
820-
return comparator(self.value) > comparator(other.value)
817+
if self.value is not None and other.value is not None:
818+
if type(self.value) in _TOTAL_ORDER_CASTERS:
819+
caster = _TOTAL_ORDER_CASTERS[type(self.value)]
820+
return caster(self.value) > caster(other.value)
821821
return self.value > other.value
822822

823823
if text_type(self) != text_type(other):
@@ -1409,13 +1409,21 @@ def _writeXML(xmlnode):
14091409
_XSD_DECIMAL,
14101410
)
14111411

1412-
# these are not guranteed to sort because it is not possible
1413-
# to calculate a total order over all valid members of the type
1414-
# the function must partition the type into subtypes that do have total orders
1415-
_NO_TOTAL_ORDER_TYPES = {
1416-
datetime:lambda value:bool(value.tzinfo),
1417-
time:lambda value:bool(value.tzinfo),
1418-
xml.dom.minidom.Document:lambda value:value.toxml(),
1412+
# the following types need special treatment for reasonable sorting because
1413+
# certain instances can't be compared to each other. We treat this by
1414+
# partitioning and then sorting within those partitions.
1415+
_TOTAL_ORDER_CASTERS = {
1416+
datetime: lambda value: (
1417+
# naive vs. aware
1418+
value.tzinfo is not None and value.tzinfo.utcoffset(value) is not None,
1419+
value
1420+
),
1421+
time: lambda value: (
1422+
# naive vs. aware
1423+
value.tzinfo is not None and value.tzinfo.utcoffset(None) is not None,
1424+
value
1425+
),
1426+
xml.dom.minidom.Document: lambda value: value.toxml(),
14191427
}
14201428

14211429
def _castPythonToLiteral(obj):

test/test_term.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
some more specific Literal tests are in test_literal.py
33
"""
44

5-
import unittest
65
import base64
6+
import unittest
7+
import random
78

89
from rdflib.term import URIRef, BNode, Literal, _is_valid_unicode
910
from rdflib.graph import QuotedGraph, Graph
@@ -57,16 +58,37 @@ def test_base64_values(self):
5758

5859
def test_total_order(self):
5960
types = {
60-
XSD.dateTime:('0001-01-01T00:00:00', '0001-01-01T00:00:00Z',
61-
'0001-01-01T00:00:00-00:00'),
62-
XSD.date:('0001-01-01', '0001-01-01Z', '0001-01-01-00:00'),
63-
XSD.time:('00:00:00', '00:00:00Z', '00:00:00-00:00'),
64-
XSD.gYear:('0001', '0001Z', '0001-00:00'), # interval
65-
XSD.gYearMonth:('0001-01', '0001-01Z', '0001-01-00:00'),
61+
XSD.dateTime: (
62+
'2001-01-01T00:00:00',
63+
'2001-01-01T00:00:00Z',
64+
'2001-01-01T00:00:00-00:00'
65+
),
66+
XSD.date: (
67+
'2001-01-01',
68+
'2001-01-01Z',
69+
'2001-01-01-00:00'
70+
),
71+
XSD.time: (
72+
'00:00:00',
73+
'00:00:00Z',
74+
'00:00:00-00:00'
75+
),
76+
XSD.gYear: (
77+
'2001',
78+
'2001Z',
79+
'2001-00:00'
80+
), # interval
81+
XSD.gYearMonth: (
82+
'2001-01',
83+
'2001-01Z',
84+
'2001-01-00:00'
85+
),
6686
}
67-
literals = [Literal(literal, datatype=type)
68-
for type, literals in types.items()
69-
for literal in literals]
87+
literals = [
88+
Literal(literal, datatype=t)
89+
for t, literals in types.items()
90+
for literal in literals
91+
]
7092
try:
7193
sorted(literals)
7294
orderable = True
@@ -77,6 +99,29 @@ def test_total_order(self):
7799
orderable = False
78100
self.assertTrue(orderable)
79101

102+
# also make sure that within a datetime things are still ordered:
103+
l1 = [
104+
Literal(l, datatype=XSD.dateTime)
105+
for l in [
106+
'2001-01-01T00:00:00',
107+
'2001-01-01T01:00:00',
108+
'2001-01-01T01:00:01',
109+
'2001-01-02T01:00:01',
110+
'2001-01-01T00:00:00Z',
111+
'2001-01-01T00:00:00-00:00',
112+
'2001-01-01T01:00:00Z',
113+
'2001-01-01T01:00:00-00:00',
114+
'2001-01-01T00:00:00-01:30',
115+
'2001-01-01T01:00:00-01:30',
116+
'2001-01-02T01:00:01Z',
117+
'2001-01-02T01:00:01-00:00',
118+
'2001-01-02T01:00:01-01:30'
119+
]
120+
]
121+
l2 = list(l1)
122+
random.shuffle(l2)
123+
self.assertListEqual(l1, sorted(l2))
124+
80125

81126
class TestValidityFunctions(unittest.TestCase):
82127

0 commit comments

Comments
 (0)