summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscoder <stefan_ml@behnel.de>2016-04-08 13:45:09 +0200
committerscoder <stefan_ml@behnel.de>2016-04-08 13:45:09 +0200
commit85a5d43e6aee3ac29015c0fbd3af7b6cf7f2010b (patch)
tree8289cae490a24362ab80075a721b5a5672379a13
parentaedf10b01d281ae79f06c4931778d2f47824f379 (diff)
parent88c535f0df7f2c5bd8fb9c9d478e1b0fb4f85657 (diff)
downloadpython-lxml-85a5d43e6aee3ac29015c0fbd3af7b6cf7f2010b.tar.gz
Merge pull request #191 from hjoukl/master
Fix losing float precision for lxml.objectify FloatElement.
-rw-r--r--doc/objectify.txt4
-rw-r--r--src/lxml/lxml.objectify.pyx15
-rw-r--r--src/lxml/tests/test_objectify.py44
3 files changed, 55 insertions, 8 deletions
diff --git a/doc/objectify.txt b/doc/objectify.txt
index 01768a20..65ef25f3 100644
--- a/doc/objectify.txt
+++ b/doc/objectify.txt
@@ -308,7 +308,7 @@ To simplify the generation of trees even further, you can use the E-factory:
>>> E = objectify.E
>>> root = E.root(
... E.a(5),
- ... E.b(6.1),
+ ... E.b(6.21),
... E.c(True),
... E.d("how", tell="me")
... )
@@ -316,7 +316,7 @@ To simplify the generation of trees even further, you can use the E-factory:
>>> print(etree.tostring(root, pretty_print=True))
<root xmlns:py="http://codespeak.net/lxml/objectify/pytype">
<a py:pytype="int">5</a>
- <b py:pytype="float">6.1</b>
+ <b py:pytype="float">6.21</b>
<c py:pytype="bool">true</c>
<d py:pytype="str" tell="me">how</d>
</root>
diff --git a/src/lxml/lxml.objectify.pyx b/src/lxml/lxml.objectify.pyx
index 371eb2c5..d79fcd82 100644
--- a/src/lxml/lxml.objectify.pyx
+++ b/src/lxml/lxml.objectify.pyx
@@ -896,11 +896,14 @@ cdef class PyType:
u"""PyType(self, name, type_check, type_class, stringify=None)
User defined type.
- Named type that contains a type check function and a type class that
- inherits from ObjectifiedDataElement. The type check must take a string
- as argument and raise ValueError or TypeError if it cannot handle the
- string value. It may be None in which case it is not considered for type
- guessing.
+ Named type that contains a type check function, a type class that
+ inherits from ObjectifiedDataElement and an optional "stringification"
+ function. The type check must take a string as argument and raise
+ ValueError or TypeError if it cannot handle the string value. It may be
+ None in which case it is not considered for type guessing. For registered
+ named types, the 'stringify' function (or unicode() if None) is used to
+ convert a Python object with type name 'name' to the string representation
+ stored in the XML tree.
Example::
@@ -1031,7 +1034,7 @@ cdef _registerPyTypes():
pytype = PyType(u'long', None, IntElement)
pytype.register()
- pytype = PyType(u'float', float, FloatElement)
+ pytype = PyType(u'float', float, FloatElement, repr)
pytype.xmlSchemaTypes = (u"double", u"float")
pytype.register()
diff --git a/src/lxml/tests/test_objectify.py b/src/lxml/tests/test_objectify.py
index b17e38c2..68b9d7a8 100644
--- a/src/lxml/tests/test_objectify.py
+++ b/src/lxml/tests/test_objectify.py
@@ -1016,6 +1016,50 @@ class ObjectifyTestCase(HelperTestCase):
value = objectify.DataElement(5.5)
self.assertEqual(hash(value), hash(5.5))
+ def test_type_float_precision(self):
+ # test not losing precision by shortened float str() value
+ # repr(2.305064300557): '2.305064300557'
+ # str(2.305064300557): '2.30506430056'
+ # "%57.54f" % 2.305064300557:
+ # ' 2.305064300556999956626214043353684246540069580078125000'
+ Element = self.Element
+ root = Element("{objectified}root")
+ s = "2.305064300557"
+ root.f = float(s)
+ self.assertTrue(isinstance(root.f, objectify.FloatElement))
+ self.assertEqual(root.f.text, s)
+ self.assertEqual(root.f.pyval, float(s))
+
+ def test_type_float_instantiation_precision(self):
+ # test precision preservation for FloatElement instantiation
+ s = "2.305064300557"
+ self.assertEqual(objectify.FloatElement(s), float(s))
+
+ def test_type_float_precision_consistency(self):
+ # test consistent FloatElement values for the different instantiation
+ # possibilities
+ Element = self.Element
+ root = Element("{objectified}root")
+ s = "2.305064300557"
+ f = float(s)
+ float_elem = objectify.FloatElement(s)
+ float_data_elem = objectify.DataElement(f)
+ root.float_child = float(f)
+ self.assertTrue(f == float_elem == float_data_elem == root.float_child)
+
+ def test_data_element_float_precision(self):
+ # test not losing precision by shortened float str() value
+ f = 2305064300557.0
+ value = objectify.DataElement(f)
+ self.assertTrue(isinstance(value, objectify.FloatElement))
+ self.assertEqual(value, f)
+
+ def test_data_element_float_hash_repr(self):
+ # test not losing precision by shortened float str() value
+ f = 2305064300557.0
+ value = objectify.DataElement(f)
+ self.assertEqual(hash(value), hash(f))
+
def test_data_element_xsitypes(self):
for xsi, objclass in xsitype2objclass.items():
# 1 is a valid value for all ObjectifiedDataElement classes