Commits

Anonymous committed d87d842

Fix empty element bug 7 - now primitives simply return defaults, this is how it should be for strings, however for int/float it is a bit off the usual way, but nevertheleess keep it like this + implement min/maxOccurs check

  • Participants
  • Parent commits 9332425

Comments (0)

Files changed (3)

File osa/xmltypes.py

             element : etree.Element
                 Element to recover from.
         """
+        # removed with bug 7, we do not check for this for primitives,
+        # so stay consistent for complex as well
         # element is nill
-        if element.get('{%s}nil' % xmlnamespace.NS_XSI, "false") == "true":
-            return
+        #if element.get('{%s}nil' % xmlnamespace.NS_XSI, "false") == "true":
+            #return
 
         all_children_names = []
         for child in self._children:
 
             # used for conversion. for primitive types we receive back built-ins
             inst = self._children[ind]['type']()
+            # we do not distinguish xs:nil="true" explicitly here, this will have
+            # empty text in any case, this is not strict standard, but ...
             subvalue = inst.from_xml(subel)
 
-            # check conversion
-            if subvalue is None:
-                if self._children[ind]['min'] != 0 and \
-                   self._children[ind]['nillable'] is False:
-                    raise ValueError("Non-nillable %s element is nil." % name)
+            # removed for bug 7
+            #if subvalue is None:
+                #if self._children[ind]['min'] != 0 and \
+                   #self._children[ind]['nillable'] is False:
+                    #raise ValueError("Non-nillable %s element is nil." % name)
             # None, i.e. nillables, should also be placed here 
             if self._children[ind]['max'].__class__.__name__ != "int" or\
                self._children[ind]['max'] > 1:
         # now all children were processed, so remove them to save memory
         element.clear()
 
+        # do a simplistic validation that all expected elements were present,
+        # this is not strict, but ...
+        for child in self._children:
+            val = getattr(self, child['name'], None)
+            numValues = 0
+            if val is not None:
+                numValues = 1
+            if isinstance(val, list):
+                numValues = len(val)
+            if numValues < child['min']:
+                raise ValueError("Number of elements '%s' %d is less then minOccurs %d."\
+                                 %(child['name'], numValues, child['min']))
+            if child['max'].__class__.__name__ == "int" and\
+               numValues > child['max']:
+                raise ValueError("Number of elements '%s' %d is more then maxOccurs %d."\
+                                 %(child['name'], numValues, child['max']))
+
         return self
 
     def to_file(self, fname):
         if element.text:
             return element.text
         else:
-            return None
+            return ""
 
 
 class XMLBase64Binary(XMLType, str):
         if element.text:
             return base64.b64decode(element.text)
         else:
-            return None
+            return ""
 
 
 class XMLInteger(XMLType, int):
                 return int(element.text)
             except:
                 return long(element.text)
-        return None
+        return 0
 
 
 class XMLDouble(XMLType, float):
     def from_xml(self, element):
         if element.text:
             return float(element.text)
-        return None
+        return 0
 
 
 class XMLBoolean(XMLType, str):
     def from_xml(cls, element):
         if element.text:
             return (element.text.lower() in ['true', '1'])
-        return None
+        return False
 
 
 class XMLAny(XMLType, str):
     def from_xml(self, element):
         if element.text:
             return Decimal(element.text)
-        return None
+        return Decimal(0)
 
 
 class XMLDate(XMLType):
     def from_xml(self, element):
         """expect ISO formatted dates"""
         if not(element.text):
-            return None
+            return date(1970, 1, 1)
         text = element.text
         pos = text.find("UTC")
         if pos != -1:
 
     def from_xml(self, element):
         if not(element.text):
-            return None
+            return datetime(1970, 1, 1)
         text = element.text
         pos = text.find("UTC")
         if pos != -1:

File tests/test_xmltypes_complex.py

 
 ns_test = 'test_namespace'
 
+Arr = ComplexTypeMeta('Arr', (), {
+                "_children":[
+                    {'name':"ch", "type":XMLInteger, "min":3, "max": 10, "nillable":False, "fullname":"ch"},
+                        ], "__doc__": "an info"})
+
 Address = ComplexTypeMeta('Address', (), {
                 "_children":[
                     {'name':"street", "type":XMLString, "min":1, "max": 1, "nillable":False, "fullname":"street"},
         r = Address().from_xml(element)
         self.assertEqual(a.street, r.street)
         self.assertEqual(a.city, r.city)
-        self.assertEqual(r.zip, None)
+        self.assertEqual(r.zip, 0) # bug 7 None)
         self.assertEqual(a.lattitude, r.lattitude)
         self.assertEqual(a.longitude, r.longitude)
         self.assertEqual(a.since, r.since)
         self.assertEqual(5, len(element.getchildren()))
         element[0].text=None  # street is not nillable 
         element[0].set('{%s}nil' % xmlnamespace.NS_XSI, 'true')
-        self.assertRaises(ValueError, Address().from_xml, element)
+        # bug 7
+        # self.assertNotRaises(ValueError, Address().from_xml, element)
+        Address().from_xml(element)
 
         element = etree.Element('test')
         a.to_xml( element, "{%s}%s" %(ns_test, "atach"))
         element.clear()
         self.assertEqual(0, len(element.getchildren()))
         element.set('{%s}nil' % xmlnamespace.NS_XSI, 'true')
-        r = Address().from_xml(element)
-        self.assertTrue(r is None)
+        self.assertRaises(ValueError,  Address().from_xml, element)
+
+    def test_min_max_check(self):
+        a = Arr()
+        a.ch = [1,2,3,4]
+
+        element = etree.Element('test')
+        a.to_xml( element, "{%s}%s" %(ns_test, "atach"))
+        element = element[0]
+
+        # working
+        r = Arr().from_xml(element)
+
+        # < minOccrus
+        element = etree.Element('test')
+        a.to_xml( element, "{%s}%s" %(ns_test, "atach"))
+        element = element[0]
+        element.remove(element[-1])
+        element.remove(element[-1])
+        self.assertEqual(2, len(element.getchildren()))
+        self.assertRaises(ValueError,  Arr().from_xml, element)
+
+        # > maxOccurs
+        element = etree.Element('test')
+        a.to_xml( element, "{%s}%s" %(ns_test, "atach"))
+        element = element[0]
+        for i in range(7):
+            element.append(element[-1])
+        self.assertEqual(11, len(element.getchildren()))
+        self.assertRaises(ValueError,  Arr().from_xml, element)

File tests/test_xmltypes_primitive.py

         value = XMLString().from_xml(element)
         self.assertEqual(value, 'value')
 
+        #empty element test, bug 7
+        element.text = None
+        value = XMLString().from_xml(element)
+        self.assertEqual(value, "")
+
     def test_stringenumeration(self):
         XMLStringEnumeration._allowedValues = ["me", "you"]
         s1 = XMLStringEnumeration("me")
         element.text="he"
         self.assertRaises(ValueError, XMLStringEnumeration().from_xml, element)
 
+        #empty element test, bug 7
+        element.text = None
+        XMLStringEnumeration._allowedValues.append("")
+        value = XMLStringEnumeration().from_xml(element)
+        self.assertEqual(value, "")
+
     def test_datetime(self):
         d = XMLDateTime(datetime.now())
 
         dt = XMLDateTime().from_xml(element)
         self.assertEqual(d.value, dt)
 
+        #empty element test, bug 7
+        element.text = None
+        value = XMLDateTime().from_xml(element)
+        self.assertEqual(value, datetime(1970,1,1))
+
     def test_date(self):
         x = datetime.now()
         x = x.date()
         self.assertEqual(element.text, d.value.isoformat())
         dt = XMLDate().from_xml(element)
         self.assertEqual(d.value, dt)
+         
+        #empty element test, bug 7
+        element.text = None
+        value = XMLDate().from_xml(element)
+        self.assertEqual(value, date(1970,1,1))
 
     def test_integer(self):
         integer = XMLInteger(12)
         value = XMLInteger().from_xml(element)
         self.assertEqual(value, integer)
 
+        #empty element test, bug 7
+        element.text = None
+        value = XMLInteger().from_xml(element)
+        self.assertEqual(value, 0)
+
     def test_large_integer(self):
         integer = XMLInteger(128375873458473)
 
 
         f2 = XMLDouble().from_xml(element)
         self.assertEqual(f2, f)
+         
+        #empty element test, bug 7
+        element.text = None
+        value = XMLDouble().from_xml(element)
+        self.assertEqual(value, 0)
 
     def test_unicode(self):
         s = XMLString('\x34\x55\x65\x34')
         b = etree.Element('test')
         b.text = ''
         b = XMLBoolean().from_xml(b)
-        self.assertEqual(b, None)
+        self.assertEqual(b, False)
 
     def test_any(self):
         #test any from_xml, the other way is