[lxml-dev] Objectify nodes can't have real attributes

Tres Seaver tseaver at palladion.com
Wed Feb 14 03:24:59 CET 2007


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

For a project I'm currently working on, I want to create nodes by
parsing an XML document, and then bind a Zope3 interface to the node, in
order to allow for compmonent lookup.  With classic elementtree nodes,
and with lxml.etree nodes, I can.  However, the nodes produced by
lxml.objectify don't allow this.  Following is a doctest which
demonstrates what I'd like (the last stanza contains the tests which break).

First, set up a marker interface::

  >>> from zope.interface import Interface, directlyProvides
  >>> class IFoo(Interface):
  ...     pass

Now, test that we can mark an elementtree node::

  >>> from elementtree.ElementTree import XML
  >>> node = XML('<node/>')
  >>> IFoo.providedBy(node)
  False
  >>> directlyProvides(node, IFoo)
  >>> IFoo.providedBy(node)
  True

Now test an lxml.etree node::

  >>> from lxml.etree import XML
  >>> node = XML('<node/>')
  >>> IFoo.providedBy(node)
  False
  >>> directlyProvides(node, IFoo)
  Traceback (most recent call last):
  ...
  AttributeError: 'etree._Element' object has no attribute '__provides__'

OK, so we need to override the node class used by the parser::

  >>> from lxml.etree import ElementBase
  >>> from zope.interface import implements
  >>> class MyNode(ElementBase):
  ...     implements(IFoo)
  >>> from lxml.etree import XMLParser, ElementDefaultClassLookup
  >>> lookup = ElementDefaultClassLookup(element=MyNode)
  >>> parser = XMLParser()
  >>> parser.setElementClassLookup(lookup)
  >>> node = XML('<node/>', parser)
  >>> isinstance(node, MyNode)
  True
  >>> IFoo.providedBy(node)
  True

Before we stamp the node with a custom interface, its interfaces come
from its class::

  >>> node.__provides__
  <implementedBy __builtin__.MyNode>
  >>> before = node.__provides__
  >>> list(before)
  [<InterfaceClass __builtin__.IFoo>]

Afterwards, they are stored on the instance::

  >>> class IBar(Interface):
  ...     pass
  >>> IBar.providedBy(node)
  False
  >>> directlyProvides(node, IBar)
  >>> IBar.providedBy(node)
  True
  >>> node.__provides__ # doctest: +ELLIPSIS
  <zope.interface.Provides object at ...>
  >>> after = node.__provides__
  >>> list(after)
  [<InterfaceClass __builtin__.IBar>, <InterfaceClass __builtin__.IFoo>]

Now, let's try with lxml's objectify nodes::

  >>> from lxml.objectify import XML
  >>> from lxml.objectify import ObjectifiedElement
  >>> node = XML('<node/>')
  >>> IFoo.providedBy(node)
  False

In this case, we can *call* ``directlyProvides``, but it doesn't do what
we want:  instead of binding the interface, it creates a child node!::

  >>> directlyProvides(node, IFoo)
  >>> type(node.__provides__)
  <type 'objectify.StringElement'>

As with etree nodes we need to override the node class used by the parser::

  >>> from lxml.etree import XML # objectify version won't take a parser
  >>> from lxml.objectify import ObjectifiedElement
  >>> class MyTreeNode(ObjectifiedElement):
  ...     implements(IFoo)
  >>> from lxml.objectify import ObjectifyElementClassLookup
  >>> lookup = ObjectifyElementClassLookup(tree_class=MyTreeNode)
  >>> parser = XMLParser(remove_blank_text=True)
  >>> parser.setElementClassLookup(lookup)
  >>> node = XML('<node/>', parser)
  >>> node.__provides__
  <implementedBy __builtin__.MyTreeNode>
  >>> IFoo.providedBy(node)
  True

However, we still can't assign correctly to ``__provides__``::

  >>> class IBar(Interface):
  ...     pass
  >>> IBar.providedBy(node)
  False
  >>> before = node.__provides__
  >>> list(before)
  [<InterfaceClass __builtin__.IFoo>]
  >>> directlyProvides(node, IBar)
  >>> after = node.__provides__

However, both these tests break::

  >>> list(after)
  [<InterfaceClass __builtin__.IBar>, <InterfaceClass __builtin__.IFoo>]
  >>> IBar.providedBy(node)
  True

We might try using a node class which has a slot for ``__provides__``::

  >>> class MySlottedTreeNode(ObjectifiedElement):
  ...     __slots__ = ('__provides__',)
  ...     implements(IFoo)
  >>> lookup = ObjectifyElementClassLookup(tree_class=MyTreeNode)
  >>> parser = XMLParser(remove_blank_text=True)
  >>> parser.setElementClassLookup(lookup)
  >>> node = XML('<node/>', parser)
  >>> node.__provides__
  <implementedBy __builtin__.MyTreeNode>
  >>> IFoo.providedBy(node)
  True
  >>> IBar.providedBy(node)
  False
  >>> before = node.__provides__
  >>> list(before)
  [<InterfaceClass __builtin__.IFoo>]
  >>> directlyProvides(node, IBar)
  >>> after = node.__provides__

However, still no joy:  both these break::

  >>> list(after)
  [<InterfaceClass __builtin__.IBar>, <InterfaceClass __builtin__.IFoo>]
  >>> IBar.providedBy(node)
  True



Tres.
- --
===================================================================
Tres Seaver          +1 540-429-0999          tseaver at palladion.com
Palladion Software   "Excellence by Design"    http://palladion.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFF0nL7+gerLs4ltQ4RAlmLAJ9cAykd6TUiX128agDpT7PBI+FOGACaA8P1
/WcuQLZcCaZuddFt4IV0Gok=
=SLMx
-----END PGP SIGNATURE-----



More information about the lxml-dev mailing list