[lxml-dev] [objectify] patch/changes proposal: xsiannotate, deannotate

jholg at gmx.de jholg at gmx.de
Wed Apr 4 15:06:39 CEST 2007


Hi all,
I suggest

1. adding two functions to lxml.objectify:

def xsiannotate(element_or_tree, ignore_old=True):
    """Recursively annotates the elements of an XML tree with 'xsi:type'
    attributes.

    If the 'ignore_old' keyword argument is True (the default), current
    'xsi:type' attributes will be ignored and replaced.  Otherwise, they will be
    checked and only replaced if they no longer fit the current text value.
    """
[...]

Note: Will simply take the first schema type in PyType.xmlSchemaTypes list.

def deannotate(element_or_tree, pytype=True, xsi=True):
    """Recursively de-annotate the elements of an XML tree by removing 'pytype'
    and/or 'type' attributes.

    If the 'pytype' keyword argument is True (the default), 'pytype' attributes
    will be removed. If the 'xsi' keyword argument is True (the default),
    'xsi:type' attributes will be removed.
    """
[...]

2. Patching annotate() so that it allows for leaving pytype="str" as is if ignore_old=False. Currently it will start type-guessing/xsi-type lookup as PyType(str,...) uses no type_check function.

3. Modifying the objectify.Element() factory to default nsmap to
nsmap = { "py": PYTYPE_NAMESPACE, "xsi": XML_SCHEMA_INSTANCE_NS }
if it is None.
This keeps namespace-information in non-root nodes nice and clean with the cool new 1.3 lookup-if-ns-is-defined-up-in-the-tree functionality.

4. Patch DataElement so that it allows s.o. using an _xsitype argument that is not registered (or even plain wrong). Currently, this raises a KeyError, whereas using an unknown pytype defaults to StringElement.

5. Restructure pytype<-->XML Schema type mapping a bit, as e.g XML Schema type integer fits better to a Python long than a Python int regarding value space.
a) I propose the following for non-fractional:
    pytype = PyType('int', int, IntElement)
    pytype.xmlSchemaTypes = ("int", "short", "byte", "unsignedShort",
                             "unsignedByte",)
    
    pytype.register()

    pytype = PyType('long', long, LongElement)
    pytype.xmlSchemaTypes = ("integer", "nonPositiveInteger", "negativeInteger",
                             "long", "nonNegativeInteger", "unsignedLong",
                             "unsignedInt", "positiveInteger",)
    pytype.register()

(Anything that fits in 32bit becomes a Python int, everything else a Python long. Maybe slightly arbitrary, but ok for 32bit-machines :-)
This does not have big implications in practice, it's more or less for consistency.
One thing remains: xsiannotate()-ing an IntElement >=2**31 will still xsi:type that as "int", which is not really valid regarding schema types.
This could be addressed by using a more elaborate type_check for PyType("int",...) but I'm unsure about performance drawback and if it's worth the effort.

b) Add all (non-list) XML Schema datatypes that restrict "string" to PyType('str', ...)
As StringElement is the default these end up in StringElement anyway today.   Adding them can result in faster lookup as no type-guessing will be invoked, and just for completeness... It also does not hurt s.o. who defines some custom class that handles a special schema datatype as this will override the objectify default.
S.th. along the lines of

    pytype = PyType('str', None, StringElement)
    pytype.xmlSchemaTypes = ("string", "normalizedString", "token", "language",
                             "Name", "NCName", "ID", "IDREF", "ENTITY",
                             "NMTOKEN", )

What do you say?

I've attached the patch/doc/tests for the proposed behaviour, for discussion, based on trunk versions of 2007/04/03.

Holger

-- 
"Feel free" - 10 GB Mailbox, 100 FreeSMS/Monat ...
Jetzt GMX TopMail testen: http://www.gmx.net/de/go/topmail
-------------- next part --------------
*** ./src/lxml/objectify.pyx.ORIG	Tue Apr  3 16:19:47 2007
--- ./src/lxml/objectify.pyx	Wed Apr  4 12:15:36 2007
***************
*** 711,719 ****
          if text is None:
              return 0
          text = text.lower()
!         if text == 'false':
              return 0
!         elif text == 'true':
              return 1
          else:
              raise ValueError, "Invalid boolean value: '%s'" % text
--- 711,719 ----
          if text is None:
              return 0
          text = text.lower()
!         if text in ('false', '0'):
              return 0
!         elif text in ('true', '1'):
              return 1
          else:
              raise ValueError, "Invalid boolean value: '%s'" % text
***************
*** 882,894 ****
  
  cdef _registerPyTypes():
      pytype = PyType('int', int, IntElement)
!     pytype.xmlSchemaTypes = ("integer", "positiveInteger", "negativeInteger",
!                              "nonNegativeInteger", "nonPositiveInteger",
!                              "int", "unsignedInt", "short", "unsignedShort")
      pytype.register()
  
      pytype = PyType('long', long, LongElement)
!     pytype.xmlSchemaTypes = ("long", "unsignedLong")
      pytype.register()
  
      pytype = PyType('float', float, FloatElement)
--- 882,896 ----
  
  cdef _registerPyTypes():
      pytype = PyType('int', int, IntElement)
!     pytype.xmlSchemaTypes = ("int", "short", "byte", "unsignedShort",
!                              "unsignedByte",)
!     
      pytype.register()
  
      pytype = PyType('long', long, LongElement)
!     pytype.xmlSchemaTypes = ("integer", "nonPositiveInteger", "negativeInteger",
!                              "long", "nonNegativeInteger", "unsignedLong",
!                              "unsignedInt", "positiveInteger",)
      pytype.register()
  
      pytype = PyType('float', float, FloatElement)
***************
*** 900,906 ****
      pytype.register()
  
      pytype = PyType('str', None, StringElement)
!     pytype.xmlSchemaTypes = ("string", "normalizedString")
      pytype.register()
  
      pytype = PyType('none', None, NoneElement)
--- 902,910 ----
      pytype.register()
  
      pytype = PyType('str', None, StringElement)
!     pytype.xmlSchemaTypes = ("string", "normalizedString", "token", "language",
!                              "Name", "NCName", "ID", "IDREF", "ENTITY",
!                              "NMTOKEN", )
      pytype.register()
  
      pytype = PyType('none', None, NoneElement)
***************
*** 1425,1431 ****
      """Recursively annotates the elements of an XML tree with 'pytype'
      attributes.
  
!     If the 'ignore_old' keyword argument is True (the default), current
      attributes will be ignored and replaced.  Otherwise, they will be checked
      and only replaced if they no longer fit the current text value.
      """
--- 1429,1435 ----
      """Recursively annotates the elements of an XML tree with 'pytype'
      attributes.
  
!     If the 'ignore_old' keyword argument is True (the default), current 'pytype'
      attributes will be ignored and replaced.  Otherwise, they will be checked
      and only replaced if they no longer fit the current text value.
      """
***************
*** 1450,1461 ****
              c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
          if old_value is not None and old_value != TREE_PYTYPE:
              pytype = _PYTYPE_DICT.get(old_value)
!             if pytype is not None:
                  value = textOf(c_node)
                  try:
                      if not (<PyType>pytype).type_check(value):
                          pytype = None
!                 except ValueError:
                      pytype = None
  
      if pytype is None:
--- 1454,1467 ----
              c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
          if old_value is not None and old_value != TREE_PYTYPE:
              pytype = _PYTYPE_DICT.get(old_value)
!             # StrType does not have a typecheck but is the default anyway,
!             # so just accept it if given as type information
!             if pytype not in (None, StrType):
                  value = textOf(c_node)
                  try:
                      if not (<PyType>pytype).type_check(value):
                          pytype = None
!                 except IGNORABLE_ERRORS:
                      pytype = None
  
      if pytype is None:
***************
*** 1502,1507 ****
--- 1508,1639 ----
                            _cstr(pytype.name))
      tree.END_FOR_EACH_ELEMENT_FROM(c_node)
  
+ def xsiannotate(element_or_tree, ignore_old=True):
+     """Recursively annotates the elements of an XML tree with 'xsi:type'
+     attributes.
+ 
+     If the 'ignore_old' keyword argument is True (the default), current
+     'xsi:type' attributes will be ignored and replaced.  Otherwise, they will be
+     checked and only replaced if they no longer fit the current text value.
+     """
+     cdef _Element  element
+     cdef _Document doc
+     cdef int ignore
+     cdef tree.xmlNode* c_node
+     cdef tree.xmlNs*   c_ns
+     cdef python.PyObject* dict_result
+     element = cetree.rootNodeOrRaise(element_or_tree)
+     doc = element._doc
+     ignore = bool(ignore_old)
+ 
+     StrType = _PYTYPE_DICT.get('str')
+     c_node = element._c_node
+     tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+     xsitype = None
+     pytype = None
+     value  = None
+     if not ignore:
+         # check that old value is valid
+         xsitype = cetree.attributeValueFromNsName(c_node,
+                                                   _XML_SCHEMA_INSTANCE_NS,
+                                                   "type")
+         if xsitype is not None:
+             dict_result = python.PyDict_GetItem(_SCHEMA_TYPE_DICT, xsitype)
+             if dict_result is not NULL:
+                 pytype = <PyType>dict_result
+                 # StrType does not have a typecheck but is the default anyway,
+                 # so just accept it if given as type information
+                 if pytype not in (None, StrType):
+                     value = textOf(c_node)
+                     try:
+                         if not (<PyType>pytype).type_check(value):
+                             xsitype = None
+                     except IGNORABLE_ERRORS:
+                         xsitype = None
+                         
+     if xsitype is None:
+         # check for pytype hint
+         value = cetree.attributeValueFromNsName(
+             c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+ 
+         if value is not None:
+             if value != TREE_PYTYPE:
+                 pytype = _PYTYPE_DICT.get(value)
+                 if pytype not in (None, StrType):
+                     value = textOf(c_node)
+                     try:
+                         if not (<PyType>pytype).type_check(value):
+                             pytype = None
+                     except IGNORABLE_ERRORS:
+                         pytype = None
+                 if pytype is not None:
+                     try:
+                         # pytype->xsi:type is a 1:n mapping, simply take first item
+                         xsitype = (<PyType>pytype)._schema_types[0]
+                     except IndexError:
+                         xsitype = None
+             else:
+                 xsitype = TREE_PYTYPE
+     if xsitype is None:
+         # try to guess type
+         if cetree.findChildForwards(c_node, 0) is NULL:
+             # element has no children => data class
+             if value is None:
+                 value = textOf(c_node)
+             if value is not None:
+                 for type_check, tested_pytype in _TYPE_CHECKS:
+                     try:
+                         if type_check(value) is not False:
+                             pytype = tested_pytype
+                             break
+                     except IGNORABLE_ERRORS:
+                         pass
+                 else:
+                     pytype = StrType
+                 try:
+                     # pytype->xsi:type is a 1:n mapping so simply take the first
+                     xsitype = (<PyType>pytype)._schema_types[0]
+                 except IndexError:
+                     xsitype = None
+ 
+     if xsitype is None or xsitype == TREE_PYTYPE:
+         # delete attribute if it exists
+         cetree.delAttributeFromNsName(c_node, _XML_SCHEMA_INSTANCE_NS, "type")
+     else:
+         # update or create attribute
+         c_ns = cetree.findOrBuildNodeNs(doc, c_node, _XML_SCHEMA_INSTANCE_NS)
+         tree.xmlSetNsProp(c_node, c_ns, "type", _cstr(xsitype))
+     tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+ 
+ 
+ def deannotate(element_or_tree, pytype=True, xsi=True):
+     """Recursively de-annotate the elements of an XML tree by removing 'pytype'
+     and/or 'type' attributes.
+ 
+     If the 'pytype' keyword argument is True (the default), 'pytype' attributes
+     will be removed. If the 'xsi' keyword argument is True (the default),
+     'xsi:type' attributes will be removed.
+     """
+     cdef _Element  element
+     cdef tree.xmlNode* c_node
+     
+     element = cetree.rootNodeOrRaise(element_or_tree)
+     c_node = element._c_node
+     if pytype is True and xsi is True:
+         tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+         removed = cetree.delAttributeFromNsName(c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+         removed = cetree.delAttributeFromNsName(c_node, _XML_SCHEMA_INSTANCE_NS, "type")
+         tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+     elif pytype is True:
+         tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+         removed = cetree.delAttributeFromNsName(c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+         tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+     else:
+         tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+         removed = cetree.delAttributeFromNsName(c_node, _XML_SCHEMA_INSTANCE_NS, "type")
+         tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+         
+ 
  ################################################################################
  # Module level parser setup
  
***************
*** 1558,1563 ****
--- 1690,1697 ----
          _attributes = attrib
      if _pytype is None:
          _pytype = TREE_PYTYPE
+     if nsmap is None:
+         nsmap = { "py": PYTYPE_NAMESPACE, "xsi": XML_SCHEMA_INSTANCE_NS }
      _attributes[PYTYPE_ATTRIBUTE] = _pytype
      return _makeElement(_tag, None, _attributes, nsmap)
  
***************
*** 1566,1576 ****
      """Create a new element with a Python value and XML attributes taken from
      keyword arguments or a dictionary passed as second argument.
  
!     Automatically adds a 'pyval' attribute for the Python type of the value,
!     if the type can be identified.  If '_pyval' or '_xsi' are among the
      keyword arguments, they will be used instead.
      """
-     cdef _Element element
      if attrib is not None:
          if python.PyDict_Size(_attributes):
              attrib.update(_attributes)
--- 1700,1709 ----
      """Create a new element with a Python value and XML attributes taken from
      keyword arguments or a dictionary passed as second argument.
  
!     Automatically adds a 'pytype' attribute for the Python type of the value,
!     if the type can be identified.  If '_pytype' or '_xsi' are among the
      keyword arguments, they will be used instead.
      """
      if attrib is not None:
          if python.PyDict_Size(_attributes):
              attrib.update(_attributes)
***************
*** 1578,1584 ****
      if _xsi is not None:
          python.PyDict_SetItem(_attributes, XML_SCHEMA_INSTANCE_TYPE_ATTR, _xsi)
          if _pytype is None:
!             _pytype = _SCHEMA_TYPE_DICT[_xsi].name
  
      if python._isString(_value):
          strval = _value
--- 1711,1720 ----
      if _xsi is not None:
          python.PyDict_SetItem(_attributes, XML_SCHEMA_INSTANCE_TYPE_ATTR, _xsi)
          if _pytype is None:
!             # allow for s.o. using unregistered or even wrong xsi:type names
!             pytype_lookup = _SCHEMA_TYPE_DICT.get(_xsi)
!             if pytype_lookup is not None:
!                 _pytype = pytype_lookup.name
  
      if python._isString(_value):
          strval = _value
-------------- next part --------------
*** ./doc/objectify.txt.ORIG	Wed Apr  4 12:19:54 2007
--- ./doc/objectify.txt	Wed Apr  4 14:35:18 2007
***************
*** 693,698 ****
--- 693,753 ----
          s = '5' [StringElement]
            * xsi:type = 'string'
  
+ Again, there is a utility function ``xsiannotate()`` that recursively 
+ generates the "xsi:type" attribute for the elements of a tree::
+ 
+     >>> root = objectify.fromstring('''\
+     ...    <root><a>test</a><b>5</b><c>true</c></root>
+     ...    ''')
+     >>> print objectify.dump(root)
+     root = None [ObjectifiedElement]
+         a = 'test' [StringElement]
+         b = 5 [IntElement]
+         c = True [BoolElement]
+ 
+     >>> objectify.xsiannotate(root)
+ 
+     >>> print objectify.dump(root)
+     root = None [ObjectifiedElement]
+         a = 'test' [StringElement]
+           * xsi:type = 'string'
+         b = 5 [IntElement]
+           * xsi:type = 'int'
+         c = True [BoolElement]
+           * xsi:type = 'boolean'
+ 
+ Note, however, that ``xsiannotate()`` will always use the first XML Schema
+ datatype that is defined for any given Python type, see also
+ `Defining additional data classes`_.
+ 
+ The utility function ``deannotate()`` can be used to get rid of 'py:pytype'
+ and/or 'xsi:type' information::
+ 
+     >>> root = objectify.fromstring('''\
+     ... <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+     ...   <d xsi:type="double">5</d>
+     ...   <l xsi:type="long"  >5</l>
+     ...   <s xsi:type="string">5</s>
+     ... </root>''')
+     >>> objectify.annotate(root)
+     >>> print objectify.dump(root)
+     root = None [ObjectifiedElement]
+         d = 5.0 [FloatElement]
+           * xsi:type = 'double'
+           * py:pytype = 'float'
+         l = 5L [LongElement]
+           * xsi:type = 'long'
+           * py:pytype = 'long'
+         s = '5' [StringElement]
+           * xsi:type = 'string'
+           * py:pytype = 'str'
+     >>> objectify.deannotate(root)
+     >>> print objectify.dump(root)
+     root = None [ObjectifiedElement]
+         d = 5 [IntElement]
+         l = 5 [IntElement]
+         s = 5 [IntElement]
+ 
  For convenience, the ``DataElement()`` factory creates an Element with a
  Python value in one step.  You can pass the required Python type name or the
  XSI type name::
***************
*** 714,721 ****
      >>> root.x = objectify.DataElement(5, _xsi="integer")
      >>> print objectify.dump(root)
      root = None [ObjectifiedElement]
!         x = 5 [IntElement]
!           * py:pytype = 'int'
            * xsi:type = 'integer'
  
  There is a side effect of the type lookup.  If you assign a string value using
--- 769,776 ----
      >>> root.x = objectify.DataElement(5, _xsi="integer")
      >>> print objectify.dump(root)
      root = None [ObjectifiedElement]
!         x = 5L [LongElement]
!           * py:pytype = 'long'
            * xsi:type = 'integer'
  
  There is a side effect of the type lookup.  If you assign a string value using
-------------- next part --------------
*** ./src/lxml/tests/test_objectify.py.ORIG	Wed Apr  4 10:45:47 2007
--- ./src/lxml/tests/test_objectify.py	Wed Apr  4 12:18:13 2007
***************
*** 13,18 ****
--- 13,22 ----
  
  from lxml import objectify
  
+ XML_SCHEMA_INSTANCE_NS = "http://www.w3.org/2001/XMLSchema-instance"
+ XML_SCHEMA_INSTANCE_TYPE_ATTR = "{%s}type" % XML_SCHEMA_INSTANCE_NS
+ XML_SCHEMA_NIL_ATTR = "{%s}nil" % XML_SCHEMA_INSTANCE_NS
+ 
  xml_str = '''\
  <obj:root xmlns:obj="objectified" xmlns:other="otherNS">
    <obj:c1 a1="A1" a2="A2" other:a3="A3">
***************
*** 28,34 ****
      """Test cases for lxml.objectify
      """
      etree = etree
! 
      def XML(self, xml):
          return self.etree.XML(xml, self.parser)
  
--- 32,38 ----
      """Test cases for lxml.objectify
      """
      etree = etree
!     
      def XML(self, xml):
          return self.etree.XML(xml, self.parser)
  
***************
*** 356,375 ****
          XML = self.XML
          root = XML('''\
          <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
!           <a xsi:type="integer">5</a>
!           <a xsi:type="string">5</a>
!           <a xsi:type="float">5</a>
          </root>
          ''')
  
!         self.assert_(isinstance(root.a[0], objectify.IntElement))
!         self.assertEquals(5, root.a[0])
! 
!         self.assert_(isinstance(root.a[1], objectify.StringElement))
!         self.assertEquals("5", root.a[1])
! 
!         self.assert_(isinstance(root.a[2], objectify.FloatElement))
!         self.assertEquals(5.0, root.a[2])
  
      def test_type_str_sequence(self):
          XML = self.XML
--- 360,428 ----
          XML = self.XML
          root = XML('''\
          <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
!           <b xsi:type="boolean">true</b>
!           <b xsi:type="boolean">false</b>
!           <b xsi:type="boolean">1</b>
!           <b xsi:type="boolean">0</b>
! 
!           <f xsi:type="float">5</f>
!           <f xsi:type="double">5</f>
!         
!           <s xsi:type="string">5</s>
!           <s xsi:type="normalizedString">5</s>
!           <s xsi:type="token">5</s>
!           <s xsi:type="language">5</s>
!           <s xsi:type="Name">5</s>
!           <s xsi:type="NCName">5</s>
!           <s xsi:type="ID">5</s>
!           <s xsi:type="IDREF">5</s>
!           <s xsi:type="ENTITY">5</s>
!           <s xsi:type="NMTOKEN">5</s>
! 
!           <l xsi:type="integer">5</l>
!           <l xsi:type="nonPositiveInteger">5</l>
!           <l xsi:type="negativeInteger">5</l>
!           <l xsi:type="long">5</l>
!           <l xsi:type="nonNegativeInteger">5</l>
!           <l xsi:type="unsignedLong">5</l>
!           <l xsi:type="unsignedInt">5</l>
!           <l xsi:type="positiveInteger">5</l>
!           
!           <i xsi:type="int">5</i>
!           <i xsi:type="short">5</i>
!           <i xsi:type="byte">5</i>
!           <i xsi:type="unsignedShort">5</i>
!           <i xsi:type="unsignedByte">5</i>
! 
!           <n xsi:nil="true"/>
          </root>
          ''')
  
!         for b in root.b:
!             self.assert_(isinstance(b, objectify.BoolElement))
!         self.assertEquals(True, root.b[0])
!         self.assertEquals(False, root.b[1])
!         self.assertEquals(True, root.b[2])
!         self.assertEquals(False, root.b[3])
! 
!         for f in root.f:
!             self.assert_(isinstance(f, objectify.FloatElement))
!             self.assertEquals(5, f)
!             
!         for s in root.s:
!             self.assert_(isinstance(s, objectify.StringElement))
!             self.assertEquals("5", s)
! 
!         for l in root.l:
!             self.assert_(isinstance(l, objectify.LongElement))
!             self.assertEquals(5l, l)
! 
!         for i in root.i:
!             self.assert_(isinstance(i, objectify.IntElement))
!             self.assertEquals(5, i)
!             
!         self.assert_(isinstance(root.n, objectify.NoneElement))
!         self.assertEquals(None, root.n)
  
      def test_type_str_sequence(self):
          XML = self.XML
***************
*** 444,453 ****
          root.b = False
          self.assertFalse(root.b)
  
!     def test_type_annotation(self):
          XML = self.XML
          root = XML(u'''\
!         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <b>5</b>
            <b>test</b>
            <c>1.1</c>
--- 497,667 ----
          root.b = False
          self.assertFalse(root.b)
  
!     def test_pytype_annotation(self):
          XML = self.XML
          root = XML(u'''\
!         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
!         xmlns:py="http://codespeak.net/lxml/objectify/pytype">
!           <b>5</b>
!           <b>test</b>
!           <c>1.1</c>
!           <c>\uF8D2</c>
!           <x>true</x>
!           <n xsi:nil="true" />
!           <n></n>
!           <b xsi:type="double">5</b>
!           <b xsi:type="float">5</b>
!           <s xsi:type="string">23</s>
!           <s py:pytype="str">42</s>
!           <f py:pytype="float">300</f>
!           <l py:pytype="long">2</l>
!         </a>
!         ''')
!         objectify.annotate(root)
! 
!         child_types = [ c.get(objectify.PYTYPE_ATTRIBUTE)
!                         for c in root.iterchildren() ]
!         self.assertEquals("int",   child_types[0])
!         self.assertEquals("str",   child_types[1])
!         self.assertEquals("float", child_types[2])
!         self.assertEquals("str",   child_types[3])
!         self.assertEquals("bool",  child_types[4])
!         self.assertEquals("none",  child_types[5])
!         self.assertEquals(None,    child_types[6])
!         self.assertEquals("float", child_types[7])
!         self.assertEquals("float", child_types[8])
!         self.assertEquals("str", child_types[9])
!         self.assertEquals("int", child_types[10])
!         self.assertEquals("int", child_types[11])
!         self.assertEquals("int", child_types[12])
!         
!         self.assertEquals("true", root.n.get(XML_SCHEMA_NIL_ATTR))
! 
!     def test_pytype_annotation_use_old(self):
!         XML = self.XML
!         root = XML(u'''\
!         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
!         xmlns:py="http://codespeak.net/lxml/objectify/pytype">
!           <b>5</b>
!           <b>test</b>
!           <c>1.1</c>
!           <c>\uF8D2</c>
!           <x>true</x>
!           <n xsi:nil="true" />
!           <n></n>
!           <b xsi:type="double">5</b>
!           <b xsi:type="float">5</b>
!           <s xsi:type="string">23</s>
!           <s py:pytype="str">42</s>
!           <f py:pytype="float">300</f>
!           <l py:pytype="long">2</l>
!         </a>
!         ''')
!         objectify.annotate(root, ignore_old=False)
! 
!         child_types = [ c.get(objectify.PYTYPE_ATTRIBUTE)
!                         for c in root.iterchildren() ]
!         self.assertEquals("int",   child_types[0])
!         self.assertEquals("str",   child_types[1])
!         self.assertEquals("float", child_types[2])
!         self.assertEquals("str",   child_types[3])
!         self.assertEquals("bool",  child_types[4])
!         self.assertEquals("none",  child_types[5])
!         self.assertEquals(None,    child_types[6])
!         self.assertEquals("float", child_types[7])
!         self.assertEquals("float", child_types[8])
!         self.assertEquals("str", child_types[9])
!         self.assertEquals("str", child_types[10])
!         self.assertEquals("float", child_types[11])
!         self.assertEquals("long", child_types[12])
!         
!         self.assertEquals("true", root.n.get(XML_SCHEMA_NIL_ATTR))
! 
!     def test_xsitype_annotation(self):
!         XML = self.XML
!         root = XML(u'''\
!         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
!         xmlns:py="http://codespeak.net/lxml/objectify/pytype">
!           <b>5</b>
!           <b>test</b>
!           <c>1.1</c>
!           <c>\uF8D2</c>
!           <x>true</x>
!           <n xsi:nil="true" />
!           <n></n>
!           <b xsi:type="double">5</b>
!           <b xsi:type="float">5</b>
!           <s xsi:type="string">23</s>
!           <s py:pytype="str">42</s>
!           <f py:pytype="float">300</f>
!           <l py:pytype="long">2</l>
!         </a>
!         ''')
!         objectify.xsiannotate(root)
! 
!         child_types = [ c.get(XML_SCHEMA_INSTANCE_TYPE_ATTR)
!                         for c in root.iterchildren() ]
!         self.assertEquals("int",   child_types[0])
!         self.assertEquals("string",   child_types[1])
!         self.assertEquals("float", child_types[2])
!         self.assertEquals("string",   child_types[3])
!         self.assertEquals("boolean",  child_types[4])
!         self.assertEquals(None, child_types[5])
!         self.assertEquals(None,    child_types[6])
!         self.assertEquals("int", child_types[7])
!         self.assertEquals("int", child_types[8])
!         self.assertEquals("int", child_types[9])
!         self.assertEquals("string", child_types[10])
!         self.assertEquals("float", child_types[11])
!         self.assertEquals("integer", child_types[12])
! 
!         self.assertEquals("true", root.n.get(XML_SCHEMA_NIL_ATTR))
! 
!     def test_xsitype_annotation_use_old(self):
!         XML = self.XML
!         root = XML(u'''\
!         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
!         xmlns:py="http://codespeak.net/lxml/objectify/pytype">
!           <b>5</b>
!           <b>test</b>
!           <c>1.1</c>
!           <c>\uF8D2</c>
!           <x>true</x>
!           <n xsi:nil="true" />
!           <n></n>
!           <b xsi:type="double">5</b>
!           <b xsi:type="float">5</b>
!           <s xsi:type="string">23</s>
!           <s py:pytype="str">42</s>
!           <f py:pytype="float">300</f>
!           <l py:pytype="long">2</l>
!         </a>
!         ''')
!         objectify.xsiannotate(root, ignore_old=False)
! 
!         child_types = [ c.get(XML_SCHEMA_INSTANCE_TYPE_ATTR)
!                         for c in root.iterchildren() ]
!         self.assertEquals("int",   child_types[0])
!         self.assertEquals("string",   child_types[1])
!         self.assertEquals("float", child_types[2])
!         self.assertEquals("string",   child_types[3])
!         self.assertEquals("boolean",  child_types[4])
!         self.assertEquals(None, child_types[5])
!         self.assertEquals(None,    child_types[6])
!         self.assertEquals("double", child_types[7])
!         self.assertEquals("float", child_types[8])
!         self.assertEquals("string", child_types[9])
!         self.assertEquals("string", child_types[10])
!         self.assertEquals("float", child_types[11])
!         self.assertEquals("integer", child_types[12])
! 
!         self.assertEquals("true", root.n.get(XML_SCHEMA_NIL_ATTR))
! 
!     def test_deannotation(self):
!         XML = self.XML
!         root = XML(u'''\
!         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
!         xmlns:py="http://codespeak.net/lxml/objectify/pytype">
            <b>5</b>
            <b>test</b>
            <c>1.1</c>
***************
*** 456,464 ****
--- 670,756 ----
            <n xsi:nil="true" />
            <n></n>
            <b xsi:type="double">5</b>
+           <b xsi:type="float">5</b>
+           <s xsi:type="string">23</s>
+           <s py:pytype="str">42</s>
+           <f py:pytype="float">300</f>
+           <l py:pytype="long">2</l>
+         </a>
+         ''')
+         objectify.deannotate(root)
+ 
+         for c in root.getiterator():
+             self.assertEquals(None, c.get(XML_SCHEMA_INSTANCE_TYPE_ATTR))
+             self.assertEquals(None, c.get(objectify.PYTYPE_ATTRIBUTE))
+ 
+         self.assertEquals("true", root.n.get(XML_SCHEMA_NIL_ATTR))
+ 
+     def test_pytype_deannotation(self):
+         XML = self.XML
+         root = XML(u'''\
+         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns:py="http://codespeak.net/lxml/objectify/pytype">
+           <b>5</b>
+           <b>test</b>
+           <c>1.1</c>
+           <c>\uF8D2</c>
+           <x>true</x>
+           <n xsi:nil="true" />
+           <n></n>
+           <b xsi:type="double">5</b>
+           <b xsi:type="float">5</b>
+           <s xsi:type="string">23</s>
+           <s py:pytype="str">42</s>
+           <f py:pytype="float">300</f>
+           <l py:pytype="long">2</l>
+         </a>
+         ''')
+         objectify.xsiannotate(root)
+         objectify.deannotate(root, xsi=False)
+         
+         child_types = [ c.get(XML_SCHEMA_INSTANCE_TYPE_ATTR)
+                         for c in root.iterchildren() ]
+         self.assertEquals("int",   child_types[0])
+         self.assertEquals("string",   child_types[1])
+         self.assertEquals("float", child_types[2])
+         self.assertEquals("string",   child_types[3])
+         self.assertEquals("boolean",  child_types[4])
+         self.assertEquals(None, child_types[5])
+         self.assertEquals(None,    child_types[6])
+         self.assertEquals("int", child_types[7])
+         self.assertEquals("int", child_types[8])
+         self.assertEquals("int", child_types[9])
+         self.assertEquals("string", child_types[10])
+         self.assertEquals("float", child_types[11])
+         self.assertEquals("integer", child_types[12])
+ 
+         self.assertEquals("true", root.n.get(XML_SCHEMA_NIL_ATTR))
+ 
+         for c in root.getiterator():
+             self.assertEquals(None, c.get(objectify.PYTYPE_ATTRIBUTE))
+             
+     def test_xsitype_deannotation(self):
+         XML = self.XML
+         root = XML(u'''\
+         <a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns:py="http://codespeak.net/lxml/objectify/pytype">
+           <b>5</b>
+           <b>test</b>
+           <c>1.1</c>
+           <c>\uF8D2</c>
+           <x>true</x>
+           <n xsi:nil="true" />
+           <n></n>
+           <b xsi:type="double">5</b>
+           <b xsi:type="float">5</b>
+           <s xsi:type="string">23</s>
+           <s py:pytype="str">42</s>
+           <f py:pytype="float">300</f>
+           <l py:pytype="long">2</l>
          </a>
          ''')
          objectify.annotate(root)
+         objectify.deannotate(root, pytype=False)
  
          child_types = [ c.get(objectify.PYTYPE_ATTRIBUTE)
                          for c in root.iterchildren() ]
***************
*** 470,475 ****
--- 762,777 ----
          self.assertEquals("none",  child_types[5])
          self.assertEquals(None,    child_types[6])
          self.assertEquals("float", child_types[7])
+         self.assertEquals("float", child_types[8])
+         self.assertEquals("str", child_types[9])
+         self.assertEquals("int", child_types[10])
+         self.assertEquals("int", child_types[11])
+         self.assertEquals("int", child_types[12])
+         
+         self.assertEquals("true", root.n.get(XML_SCHEMA_NIL_ATTR))
+ 
+         for c in root.getiterator():
+             self.assertEquals(None, c.get(XML_SCHEMA_INSTANCE_TYPE_ATTR))
  
      def test_change_pytype_attribute(self):
          XML = self.XML
***************
*** 881,887 ****
          self.assertEquals(
              etree.tostring(new_root),
              etree.tostring(root))
- 
  
  def test_suite():
      suite = unittest.TestSuite()
--- 1183,1188 ----


More information about the lxml-dev mailing list