Skip to content

Instantly share code, notes, and snippets.

@serdmanczyk
Created January 27, 2016 07:57
Show Gist options
  • Save serdmanczyk/6a22cb9868162d0d47bb to your computer and use it in GitHub Desktop.
Save serdmanczyk/6a22cb9868162d0d47bb to your computer and use it in GitHub Desktop.
Python Observer Metaclass
"""
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