summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJörn Hees <joernhees@users.noreply.github.com>2016-10-15 14:27:08 +0200
committerGitHub <noreply@github.com>2016-10-15 14:27:08 +0200
commit7fa7e9628d131e7095210616b7665ecab1956d05 (patch)
tree6dd6e311ca1852a1e79b9aebfa06442cd43dc28b
parentfbf95a8ea506f95394c36a09241448e6900fbd77 (diff)
parentc93107fa1e8b707a2dc6ca41e4832e49c0dfa02e (diff)
downloadrdflib-7fa7e9628d131e7095210616b7665ecab1956d05.tar.gz
Merge pull request #658 from joernhees/turtle_infty_nan
improve serialization of inf/nan, closes #655
-rw-r--r--rdflib/term.py31
-rw-r--r--test/test_issue655.py71
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()