From f7bb07b5f68fede97754685dad076cd7b7442bac Mon Sep 17 00:00:00 2001 From: Tobias Deiminger Date: Sun, 13 Feb 2022 19:40:39 +0100 Subject: [PATCH] Use expected XSD spellings for xsi:double infinity and NaN (GH-338) W3C specification for xsd:double says > The special values positive and negative infinity and > not-a-number have lexical representations INF, -INF and NaN, > respectively. Thus case matters. The previously used float.__repr__ would generate "inf", "-inf", "nan". Now we prepend special handling to get "INF", "-INF", "NaN" instead (which is still pytype compatible). Includes minor non-functional alignments of related bool to text code, and tests to assert its XML schema conformance as well. --- src/lxml/objectify.pyx | 20 ++++++++++++++++---- src/lxml/tests/test_objectify.py | 9 +++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/lxml/objectify.pyx b/src/lxml/objectify.pyx index cacbe806a..376695a8b 100644 --- a/src/lxml/objectify.pyx +++ b/src/lxml/objectify.pyx @@ -38,6 +38,9 @@ import_lxml__etree() __version__ = etree.__version__ +cdef object _float_is_inf, _float_is_nan +from math import isinf as _float_is_inf, isnan as _float_is_nan + cdef object re import re @@ -1205,8 +1208,17 @@ cdef dict _PYTYPE_DICT = {} cdef dict _SCHEMA_TYPE_DICT = {} cdef list _TYPE_CHECKS = [] -cdef unicode _lower_bool(b): - return u"true" if b else u"false" +cdef unicode _xml_bool(value): + return u"true" if value else u"false" + +cdef unicode _xml_float(value): + if _float_is_inf(value): + if value > 0: + return u"INF" + return u"-INF" + if _float_is_nan(value): + return u"NaN" + return unicode(repr(value)) cdef _pytypename(obj): return u"str" if python._isString(obj) else _typename(obj) @@ -1230,11 +1242,11 @@ cdef _registerPyTypes(): pytype = PyType(u'long', None, IntElement) pytype.register() - pytype = PyType(u'float', _checkFloat, FloatElement, repr) # wraps _parseFloat for Python + pytype = PyType(u'float', _checkFloat, FloatElement, _xml_float) # wraps functions for Python pytype.xmlSchemaTypes = (u"double", u"float") pytype.register() - pytype = PyType(u'bool', _checkBool, BoolElement, _lower_bool) # wraps functions for Python + pytype = PyType(u'bool', _checkBool, BoolElement, _xml_bool) # wraps functions for Python pytype.xmlSchemaTypes = (u"boolean",) pytype.register() diff --git a/src/lxml/tests/test_objectify.py b/src/lxml/tests/test_objectify.py index 178ba256b..f50a34474 100644 --- a/src/lxml/tests/test_objectify.py +++ b/src/lxml/tests/test_objectify.py @@ -873,6 +873,10 @@ def test_data_element_bool(self): self.assertTrue(isinstance(value, objectify.BoolElement)) self.assertEqual(value, False) + def test_data_element_bool_text(self): + self.assertEqual(objectify.DataElement(False).text, "false") + self.assertEqual(objectify.DataElement(True).text, "true") + def test_type_str(self): Element = self.Element SubElement = self.etree.SubElement @@ -1115,6 +1119,11 @@ def test_data_element_float_hash_repr(self): value = objectify.DataElement(f) self.assertEqual(hash(value), hash(f)) + def test_data_element_float_special_value_text(self): + self.assertEqual(objectify.DataElement(float("inf")).text, "INF") + self.assertEqual(objectify.DataElement(float("-inf")).text, "-INF") + self.assertEqual(objectify.DataElement(float("nan")).text, "NaN") + def test_data_element_xsitypes(self): for xsi, objclass in xsitype2objclass.items(): # 1 is a valid value for all ObjectifiedDataElement classes