From: W. Trevor King Date: Sun, 25 Jul 2010 01:48:06 +0000 (-0400) Subject: Added hooke.util.callback defining the @callback decorator. X-Git-Url: http://git.tremily.us/?p=hooke.git;a=commitdiff_plain;h=575278be55238c3d0c6445dab2118d2b38f67887 Added hooke.util.callback defining the @callback decorator. --- diff --git a/hooke/util/callback.py b/hooke/util/callback.py new file mode 100644 index 0000000..865ff38 --- /dev/null +++ b/hooke/util/callback.py @@ -0,0 +1,106 @@ +# Copyright + +"""Define the `@callback` decorator. + +See :pep:`318` for an introduction to decorators. +""" + + +def callback(method): + """Enable callbacks on `method`. + + This decorator should make it easy to setup callbacks in a rich + GUI. You only need to decorate potential hooks, and maintain a + single dict with all the callbacks for the class. This beats + passing each of the callbacks into the class' `__init__` function + individually. + + Examples + -------- + + Callbacks are called with the class instance, method instance, and + returned arguments of the method they're attached to. + + >>> def c(self, method, *args): + ... print '\\n '.join([ + ... 'callback:', + ... 'class: %s' % self, + ... 'method: %s' % method, + ... 'returned: %s' % args]) + + For some class, decorate any functions you're interested in + attaching callbacks too. Also, add a `_callbacks` attribute + holding the callbacks, keyed by function name. + + >>> class X (object): + ... def __init__(self): + ... self._callbacks = {'xyz': c} + ... + ... @callback + ... def xyz(self): + ... "xyz's docstring" + ... print 'usual xyz business' + ... return (0, 1, 1, 2, 3, 5) + ... + ... @callback + ... def abc(self): + ... "abc's docstring" + ... print 'usual abc business' + ... + >>> x = X() + + Here's our callback on `xyz`. + + >>> r = x.xyz() # doctest: +ELLIPSIS + usual xyz business + callback: + class: + method: > + returned: (0, 1, 1, 2, 3, 5) + >>> r + (0, 1, 1, 2, 3, 5) + + Note that we haven't attached a callback to `abc`. + + >>> r = x.abc() + usual abc business + + Now we attach the callback to `abc`. + + >>> x._callbacks['abc'] = c + >>> r = x.abc() # doctest: +ELLIPSIS + usual abc business + callback: + class: + method: > + returned: None + + You can also place an iterable in the `_callbacks` dict to run an + array of callbacks in series. + >>> def d(self, method, *args): + ... print 'callback d' + >>> x._callbacks['abc'] = [d, c, d] + >>> r = x.abc() # doctest: +ELLIPSIS + usual abc business + callback d + callback: + class: + method: > + returned: None + callback d + """ + def new_m(self, *args, **kwargs): + result = method(self, *args, **kwargs) + callback = self._callbacks.get(method.func_name, None) + nm = getattr(self, method.func_name) + try: + for cb in callback: + cb(self, nm, result) + except TypeError: + if callback != None: + callback(self, nm, result) + return result + new_m.func_name = method.func_name + new_m.func_doc = method.func_doc + new_m.original_method = method + return new_m