Skip to content

Instantly share code, notes, and snippets.

@tomhennigan
Last active December 10, 2015 09:39
Show Gist options
  • Save tomhennigan/4415935 to your computer and use it in GitHub Desktop.
Save tomhennigan/4415935 to your computer and use it in GitHub Desktop.
Delegate method pattern in python
def notify_delegates(method):
"""Decorator to call delegate methods. When decorating a method it will
call `onBeforeMethodName` and `onAfterMethodName` (uppercasing the first
letter of the method name).
Delegate methods are called before and after the actual method is called.
On the after method the return value from the method is passed in as the
`ret_value` keyword arg."""
# Figure out delegate method names.
method_name = method.__name__
delegate_method_suffix = method_name[0].upper() + method_name[1:]
before_method = 'onBefore' + delegate_method_suffix
exception_method = 'onExceptionIn' + delegate_method_suffix
after_method = 'onAfter' + delegate_method_suffix
def wrapper(self, *args, **kwargs):
delegates = self.getDelegatesForMethod(method_name)
# Call the before methods.
for delegate in delegates:
if hasattr(delegate, before_method):
getattr(delegate, before_method)(*args, **kwargs)
try:
return_value = method(self, *args, **kwargs)
except Exception, e:
kwargs['exception'] = e
for delegate in delegates:
if hasattr(delegate, exception_method):
getattr(delegate, exception_method)(*args, **kwargs)
# Raise the exception.
raise e
# Call the after methods.
kwargs['ret_value'] = return_value
for delegate in delegates:
if hasattr(delegate, after_method):
getattr(delegate, after_method)(*args, **kwargs)
return return_value
return wrapper
class DelegateProviderMixin:
"""Mixin for a class that has delegates. Any method can be wrapped for
delegates using the `@notify_delegates` decorator."""
__delegates = []
def addDelegate(self, delegate):
"""Adds a delegate specifically listening on all delegate methods it
can respond to."""
self.__delegates.append((delegate, None))
def addDelegateForMethod(self, delegate, method):
"""Adds a delegate specifically listening on a certain method."""
self.__delegates.append((delegate, [method]))
def addDelegateForMethods(self, delegate, methods):
"""Adds a delegate specifically listening on certain methods."""
self.__delegates.append((delegate, methods))
def removeDelegate(self, delegate):
"""Removes all hooks for the given delegate on the current object."""
to_remove = []
for index, (delegate_test, _methods) in enumerate(self.__delegates):
if delegate == delegate_test:
to_remove.append(index)
for index in to_remove:
del self.__delegates[index]
def getDelegatesForMethod(self, method):
"""Returns all delegates that are subscribed to all methods or to just
the specific method. Delegates are returned in insertion order and only
appear once regardless of how many times they have been added to this
object."""
delegates = []
for delegate, delegate_methods in self.__delegates:
if not delegate_methods or method in delegate_methods:
if not delegate in delegates:
delegates.append(delegate)
return delegates
class Something(DelegateProviderMixin):
@notify_delegates
def doSomething(self):
print 'doSomething'
def doSomethingElse(self):
print 'doSomethingElse'
@notify_delegates
def throwAnException(self):
raise Exception('oh hai')
class SomethingDelegate:
def onBeforeDoSomething(self):
print '- onBeforeDoSomething'
def onAfterDoSomething(self, ret_value):
print '- onAfterDoSomething -ret_value=%r' % ret_value
def onExceptionInThrowAnException(self, exception):
print ' - onExceptionInThrowAnException -exception=%r' % exception
if __name__ == '__main__':
a = Something()
d = SomethingDelegate()
a.addDelegate(d)
a.doSomething()
a.doSomethingElse()
a.throwAnException()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment