I am writing a program to transform a collection of XML documents into HTML. The documents require similar but not identical transformations, so I am hoping to abstract most of the details into a generic BaseParser class and then write an single subclass for each document that encapsulates the document-specific transformations. I am using the Python standard library xml.etree.ElementTree package for event-based parsing.
I would like to be able to write code like this, where the logic of the function is bundled together with when it should be called.
class CustomParser(BaseParser):
@on_tag('word', {'lang':'en'})
def found_en_word(self, tag, attrs):
# do something
For this to work, the decorator needs register the found_en_word function in a class variable (or an instance variable, though it would be redundant for each instance to have its own copy), so that the control flow can be separated in the BaseParser class.
My current solution, shown below, is to use a metaclass to create a callbacks dictionary on the class.
class Meta(type):
def __new__(cls, clsname, bases, dct):
callbacks = {}
for key, value in dct.items():
if hasattr(value, '_on_tag'):
callbacks[value._on_tag] = value
ret = type(clsname, bases, dct)
ret.callbacks = callbacks
return ret
def on_tag(tag, attrs=None):
def decorator(f):
f._on_tag = (tag, attrs)
return f
return decorator
class BaseParser(metaclass=Meta):
...
Unfortunately it doesn't look like the metaclass is inherited the way I had hoped: it seems that the metaclass is used to construct a modified BaseParser class, from which CustomParser just inherits normally.
Can this construction be implemented, with or without metaclasses, in Python?