Welcome, guest | Sign In | My Account | Store | Cart

Simplify the code when creating XHTML or XML hierarchies with ElementTree.

Usually, I have code like this:

table = ET.SubElement(body, 'table')
table.attrib['border'] = '1'

tr = ET.SubElement(table, 'tr')
ET.SubElement(tr, 'td').text = 'some text'

Using Python's __getattr__ and partial function evaluation allows to create an object that will yield a much simplified syntax.

Python, 15 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import xml.etree.ElementTree as ET
import functools

class XMLMaker(object):
    def __getattr__(self, _name):
        return functools.partial(self.make_node, _name)

    def make_node(self, _name, node, **kwargs):
        node = ET.SubElement(node, _name)
        for (key, value) in kwargs.items():
            if key == 'text':
                node.text = value
            else:
                node.attrib[key] = value
        return node

To create the same structure as above, I can write:

xm = XMLMaker()

table = xm.table(body, border='1')
tr    = xm.tr(table)
xm.td(tr, text='some text')

Saves some typing and makes the code clearer in my opinion.

2 comments

Daniel Lepage 9 years, 10 months ago  # | flag

If you're using lxml, you can get similar behavior from lxml.builder.E, albeit in a bottom-up instead of top-down direction:

from lxml.builder import E
td = E.td("some text")
tr = E.tr(td)
table = E.table(tr, border="1")

Or just

table = E.table(E.tr(E.td("some text")), border="1")

The main difference is that the args to E are the contents of the element, not the parent.

Alain Mellan (author) 9 years, 10 months ago  # | flag

Nice! I tend to prefer the top-down approach, maybe just because I'm now used to thinking this way. And lxml does not come standard with ActivePython, ElementTree does.