Created
January 27, 2016 07:57
-
-
Save serdmanczyk/6a22cb9868162d0d47bb to your computer and use it in GitHub Desktop.
Python Observer Metaclass
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
""" | |
Implements the observer patter via a metaclass | |
""" | |
import inspect | |
def __receive__(self, name, *args, **kwargs): | |
""" | |
Method attached to class on class creation used to pass | |
messages to assigned methods. | |
""" | |
if name in self.observers: | |
argspec = self.observers[name]['argspec'] | |
inargs = {arg:kwargs[arg] for arg in argspec if arg in kwargs} | |
self.observers[name]['func'](self, *args, **inargs) | |
class Observer(type): | |
""" | |
Observer metaclass. All inheriting classes will be able to | |
assigne methods to observer particular events via the @observes | |
decorator. When a message is received via | |
instance.receive("message_name", **parameters) the associated function | |
will be called with the arguments in **parameters that match the functions | |
argspec | |
""" | |
def __call__(cls, *args, **kwargs): | |
if not hasattr(cls, 'observers'): | |
cls.receive = __receive__ | |
observers = {} | |
for attr in vars(cls).values(): | |
if hasattr(attr, '__observes__'): | |
spec = inspect.getargspec(attr).args | |
argspec = [arg for arg in spec if arg != 'self'] | |
observers[attr.__observes__] = {'func': attr, 'argspec': argspec} | |
cls.observers = observers | |
return super(Observer, cls).__call__(*args, **kwargs) | |
def observes(event): | |
""" | |
Used to annotate a function with the event it observers. | |
The annotation is lost before class is fully composed, but | |
is held long enough for the metaclass to intercept and | |
derive the classes observer mapping | |
""" | |
def wrapper(func): | |
func.__observes__ = event | |
return func | |
return wrapper | |
class Friend(metaclass=Observer): | |
""" | |
A simple observer class example | |
""" | |
def __init__(self): | |
self.greetings_received = [] | |
self.consolations = [] | |
@observes("greeting") | |
def acnkowledge(self, message="hi"): | |
self.greetings_received.append(message) | |
@observes("consoling") | |
def greaving(self, consolation="it'll be alright", affirmation="pat on the back"): | |
self.consolations.append((consolation, affirmation)) | |
if __name__ == "__main__": | |
ted = Friend() | |
ted.receive("greeting", **{"message": "s'up", "postscript": "homie"}) | |
ted.receive("consoling", **{"consolation": "chin up mate"}) | |
ted.receive("consoling", **{"consolation": "thing'll get better", "affirmation": "hug"}) | |
print(ted.greetings_received) | |
print(ted.consolations) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment