[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