Last active
December 10, 2015 09:39
-
-
Save tomhennigan/4415935 to your computer and use it in GitHub Desktop.
Delegate method pattern in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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