metaclass service bus decorator concept

While playing around with some idea’s for making PyProxy completely overridable ( and also potentially undebuggable ) I started playing around with metaclasses.

Below is from my toxic directory [ gist url ]

from collections import defaultdict
from functools import wraps

Just some preliminary basic utilities

A simple bus implementation, decoratedCalls is a dictionary of service calls, with a list
of callbacks to each service call hook.

decoratedCalls = defaultdict(list)
def call(name, *args, **kwargs):
    print name, args, kwargs
    if name in decoratedCalls:
        for cb in decoratedCalls[name]:
            cb(*args, **kwargs)

Syntactic suger to add clarity later as to what functions are being bound to what. Adding in
debug/logging hooks here could allow for easier tracing of what is called for what methods.

class Subscribe(object):
    def __init__(self, name): = name
    def __call__(self, f):
        return f

Here’s the actual bus decorator, very simple just wraps the decorated function with a pre and post bus calls.

class BusDecorator(object):
    def __init__(self, name): = name
    def __call__(self, f):
        def decorator(inst, *args, **kwargs):
            call("%s-pre" %, inst, args, kwargs)            
            retval = f(inst, *args, **kwargs)
            call("%s-post" %, inst, retval)            
            return retval
        return decorator

And here’s my Bus Metaclass that combines most of the above.

The ease of wrapping the target class is accomplished by cdict which is a dictionary of
every defined attribute of the target class. As you can see it’s trivial to spin
through and decorate every callable with the BusDecorator

class BusWrap(type):
    def __new__(mcs, clsname, bases, cdict):
        modName = cdict.get("__module__", "unknownclass")
        for name in cdict.keys():
            prefix = "%s.%s.%s" % ( modName, clsname, name)
            if callable(cdict[name]):                
                cdict[name] = BusDecorator(prefix)(cdict[name])
        return type.__new__(mcs, name, bases, cdict)

Now give a dirt simple class like

class Foo(object):
    __metaclass__ = BusWrap
    def __init__(self):
        print "init'd"
    def bar(self):
        print "bar"
    def blah(self):
        print "blah"
    def ich(self):
        print "ich"
    def ego(self):
        print "lego"
    def say(self, *args):
        print "Saying ", args

And two service handlers to pre being called and after Foo.ego is called

def preBar(inst, *args, **kwargs):
    if not hasattr(inst, "ext_info"):
        inst.ext_info = "Here"
def postEgo(inst, *args, **kwargs):
    if hasattr(inst, "ext_info"):
        print "Extended info is ", inst.ext_info

Our test shows….

x = Foo()
x.say("abba", "dabba")

this as output

__main__.Foo.__init__-pre (<__main__.__init__ object at 0x02637890>, (), {}) {}
__main__.Foo.__init__-post (<__main__.__init__ object at 0x02637890>, None) {} (<__main__.__init__ object at 0x02637890>, (), {}) {}
bar (<__main__.__init__ object at 0x02637890>, None) {}
__main__.Foo.blah-pre (<__main__.__init__ object at 0x02637890>, (), {}) {}
__main__.Foo.blah-post (<__main__.__init__ object at 0x02637890>, None) {}
__main__.Foo.ich-pre (<__main__.__init__ object at 0x02637890>, (), {}) {}
__main__.Foo.ich-post (<__main__.__init__ object at 0x02637890>, None) {}
__main__.Foo.ego-pre (<__main__.__init__ object at 0x02637890>, (), {}) {}
__main__.Foo.ego-post (<__main__.__init__ object at 0x02637890>, None) {}
Extended info is  Here
__main__.Foo.say-pre (<__main__.__init__ object at 0x02637890>, ('abba', 'dabba'), {}) {}
Saying  ('abba', 'dabba')
__main__.Foo.say-post (<__main__.__init__ object at 0x02637890>, None) {}