Reputation: 3647
I'm trying to create a small addon for ElementTree
and Element
in python's xml.etree.ElementTree
which would allow for notation like:
root = ET.fromstring('<root><a><b attr1="2"/></a></root>')
element1 = root.a
element2 = root.a.b
attr = root.a.b.attr
I've tried this:
import xml.etree.ElementTree as _ET
def __getattr__(self, key):
return self.find('./' + key)
class ElementTree(_ET.ElementTree):
__getattr__ = __getattr__
class Element(_ET.Element):
__getattr__ = __getattr__
But this does not work because ET.parse
, ET.fromstring
, ET.ElementTree.find
etc will always return the internal Element
and ElementTree
objects.
Monkeypatching the classes does not work either:
>>> xml.etree.ElementTree.Element.__getattr__ = lambda x: x
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-bacb0a64c2e5> in <module>()
----> 1 xml.etree.ElementTree.Element.__getattr__ = lambda x: x
TypeError: can't set attributes of built-in/extension type 'xml.etree.ElementTree.Element'
Is this doable without copying the python etree module and then adding __getattr__
?
Related: How to inherit ElementTree.Element class in python?
Upvotes: 1
Views: 1679
Reputation: 2348
I had a similar issue and I accidentally found a quick workaround:
I imported xmlschema
in a version prior to Python 3.7 and had no issues monkeypatching ET.Element
with setattr
.
EDIT: As this was intriguing I have since looked into it further. Here is how to monkeypatch ElementTree.
Upvotes: 1
Reputation: 3647
Overriding the class is not possible due to import of the Element and XMLParser's C implementation:
# Import the C accelerators
try:
# Element is going to be shadowed by the C implementation. We need to keep
# the Python version of it accessible for some "creative" by external code
# (see tests)
_Element_Py = Element
# Element, SubElement, ParseError, TreeBuilder, XMLParser
from _elementtree import *
except ImportError:
pass
The module _elementtree
is found in Modules/_elementtree.c.
A solution would be to copy the python etree module, and change the import to:
_Element_Py = Element
I made a simple implementation of this: https://github.com/arve0/objectifiedetree
Use like this:
from objectifiedetree import *
tree = ET.parse('/path/to/file.xml')
# dot notation :-)
el = tree.xpath.to.your.element
# use normal etree attributes
print(el.attrib)
# access name crashes
attrib_el = el.find('./attrib')
Upvotes: 2