diff options
| author | Jörn Hees <joernhees@users.noreply.github.com> | 2016-10-15 14:27:08 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-10-15 14:27:08 +0200 |
| commit | 7fa7e9628d131e7095210616b7665ecab1956d05 (patch) | |
| tree | 6dd6e311ca1852a1e79b9aebfa06442cd43dc28b | |
| parent | fbf95a8ea506f95394c36a09241448e6900fbd77 (diff) | |
| parent | c93107fa1e8b707a2dc6ca41e4832e49c0dfa02e (diff) | |
| download | rdflib-7fa7e9628d131e7095210616b7665ecab1956d05.tar.gz | |
Merge pull request #658 from joernhees/turtle_infty_nan
improve serialization of inf/nan, closes #655
| -rw-r--r-- | rdflib/term.py | 31 | ||||
| -rw-r--r-- | test/test_issue655.py | 71 |
2 files changed, 102 insertions, 0 deletions
diff --git a/rdflib/term.py b/rdflib/term.py index e2dd387d..37c0e84a 100644 --- a/rdflib/term.py +++ b/rdflib/term.py @@ -38,6 +38,7 @@ __all__ = [ import logging logger = logging.getLogger(__name__) import warnings +import math import base64 import xml.dom.minidom @@ -1195,6 +1196,15 @@ class Literal(Identifier): ''' if use_plain and self.datatype in _PLAIN_LITERAL_TYPES: if self.value is not None: + # If self is inf or NaN, we need a datatype + # (there is no plain representation) + if self.datatype in _NUMERIC_INF_NAN_LITERAL_TYPES: + try: + v = float(self) + if math.isinf(v) or math.isnan(v): + return self._literal_n3(False, qname_callback) + except ValueError: + return self._literal_n3(False, qname_callback) # this is a bit of a mess - # in py >=2.6 the string.format function makes this easier @@ -1221,6 +1231,20 @@ class Literal(Identifier): quoted_dt = qname_callback(datatype) if not quoted_dt: quoted_dt = "<%s>" % datatype + if datatype in _NUMERIC_INF_NAN_LITERAL_TYPES: + try: + v = float(self) + if math.isinf(v): + # py string reps: float: 'inf', Decimal: 'Infinity" + # both need to become "INF" in xsd datatypes + encoded = encoded.replace('inf', 'INF').replace( + 'Infinity', 'INF') + if math.isnan(v): + encoded = encoded.replace('nan', 'NaN') + except ValueError: + # if we can't cast to float something is wrong, but we can + # still serialize. Warn user about it + warnings.warn("Serializing weird numerical %r" % self) language = self.language if language: @@ -1388,6 +1412,13 @@ _PLAIN_LITERAL_TYPES = ( _XSD_DECIMAL, ) +# these have special INF and NaN XSD representations +_NUMERIC_INF_NAN_LITERAL_TYPES = ( + URIRef(_XSD_PFX + 'float'), + _XSD_DOUBLE, + _XSD_DECIMAL, +) + def _castPythonToLiteral(obj): """ diff --git a/test/test_issue655.py b/test/test_issue655.py new file mode 100644 index 00000000..1c640709 --- /dev/null +++ b/test/test_issue655.py @@ -0,0 +1,71 @@ +from decimal import Decimal +import unittest +from rdflib import Graph, Namespace, URIRef, Literal, XSD +from rdflib.compare import to_isomorphic + + +class TestIssue655(unittest.TestCase): + + def test_issue655(self): + # make sure that inf and nan are serialized correctly + dt = XSD['double'].n3() + self.assertEqual( + Literal(float("inf"))._literal_n3(True), + '"INF"^^%s' % dt + ) + self.assertEqual( + Literal(float("-inf"))._literal_n3(True), + '"-INF"^^%s' % dt + ) + self.assertEqual( + Literal(float("nan"))._literal_n3(True), + '"NaN"^^%s' % dt + ) + + dt = XSD['decimal'].n3() + self.assertEqual( + Literal(Decimal("inf"))._literal_n3(True), + '"INF"^^%s' % dt + ) + self.assertEqual( + Literal(Decimal("-inf"))._literal_n3(True), + '"-INF"^^%s' % dt + ) + self.assertEqual( + Literal(Decimal("nan"))._literal_n3(True), + '"NaN"^^%s' % dt + ) + + self.assertEqual( + Literal("inf", datatype=XSD['decimal'])._literal_n3(True), + '"INF"^^%s' % dt + ) + + # assert that non-numerical aren't changed + self.assertEqual( + Literal('inf')._literal_n3(True), + '"inf"' + ) + self.assertEqual( + Literal('nan')._literal_n3(True), + '"nan"' + ) + + PROV = Namespace('http://www.w3.org/ns/prov#') + + bob = URIRef("http://example.org/object/Bob") + + # g1 is a simple graph with an infinite and a nan values + g1 = Graph() + g1.add((bob, PROV.value, Literal(float("inf")))) + g1.add((bob, PROV.value, Literal(float("nan")))) + + # Build g2 out of the deserialisation of g1 serialisation + g2 = Graph() + g2.parse(data=g1.serialize(format='turtle'), format='turtle') + + self.assertTrue(to_isomorphic(g1) == to_isomorphic(g2)) + + +if __name__ == "__main__": + unittest.main() |
