The plugin architecture bashout: Grok!
André Roberge made a small specification of a program to demonstrate plugin architectures, and made a set of demonstrations of different ways of making plugins. Of course, I think that the best way is to use the Zope Component Architecture. One way would be to use it directly, and register the components in python. I demonstrated that (for a simpler, but similar case) a year ago. Direct configuration does have it’s drawbacks, though as it’s done on import time, and hence may depend on import order, for example.
So, the component architecture has a configuration langauge called ZCML to counter this. Chris McDonough made an example of the calculator used by André with the component architecture and ZCML. This clearly demonstrated the drawback with ZCML. You configure the conde away from the code. Also, most people don’t like XML. So I decided to redo the example with Grok, or more specifically, grokcore.component, which is the part of Grok that handles the basic component architecture stuff.
One surprising thing is that grokcore.component uses less modules than Chris’s example does. We don’t need zope.publisher, zope.location and zope.i18n for example. This is surprising, because grokcore.component does actually use a little ZCML-stub to get started. So, how does the Grok code look? Well, the tokenizer function looks just like Chris’ of course, except that I use an adapter to look up the literal token for ints as well. I had some idea there to extend the program with support for floats, but I dodn’t get that far.
def tokenize(program): for number, operator in re.findall("\s*(?:(\d+)|(\*\*|.))", program): if number: yield ILiteral(int(number)) else: try: yield component.getUtility(IOperator, operator) except LookupError: raise SyntaxError("unknown operator: %r" % operator) yield end_token()
I’m guessing my interfaces.py file looks just like Chris’ too, I haven’t actually looked at it. But instead of registering the components in ZCML, I do it in the python code. The add token originally looks like this:
class operator_add_token(object): """ plugin """ lbp = 10 def nud(self, context): return context.expression(100) def led(self, context, left): return left + context.expression(10)
I changed it to:
class operator_add_token(grok.GlobalUtility): grok.implements(IOperator) grok.name('+') lbp = 10 def nud(self): return expression(100) def led(self, left): return left + expression(10)
Note that I’ve added the same information as the ZCML has. I say that this is an IOperator, and I name it ‘+’, and i subclass it from GlobalUtility, to tell Grok that this should be registered as a utility. So this does exactly the same as Chris ZCML based code, but without all the XML, and for some reason with less useless dependecies installed.
Grok rules! Work on 1.0 is going strong now, expect it out in the beginning of next year!