1#!/usr/bin/env python 2#coding:utf-8 3# Purpose: observer pattern 4# Created: 22.01.2011 5# Copyright (C) 2011, Manfred Moitzi 6# License: MIT license 7from __future__ import unicode_literals, print_function, division 8__author__ = "mozman <mozman@gmx.at>" 9 10try: 11 from weakref import WeakSet 12except ImportError: 13 from weakrefset import WeakSet 14 15class Observer(object): 16 """ Simple implementation of the observer pattern for broadcasting messages 17 to objects. 18 19 For every event the subscriber object need an event handler called 'on_event_handler' 20 accepting the parameter 'msg'. 21 22 Because of the simple implementation of the algorithm it is necessary to 23 register the objects an not only the listener methods, because the methods of 24 different objects of the same calss have the same 'id' and managing the 25 listeners in a WeakSet is not possible for different objects (you could 26 only manage one table in one document instance). 27 28 Example for event: 'save' 29 # module 'one' 30 import observer 31 32 class Listener: 33 def on_save_handler(self, msg): 34 pass 35 def get_root(self): 36 return None 37 38 listener = Listener() 39 # subscribe to the 'global observer' 40 observer.subscribe('save', listener) 41 42 # module 'two' 43 import observer 44 # calls listener.on_save_handler(msg=None) 45 observer.broadcast('save', msg=None) 46 """ 47 48 def __init__(self): 49 self._listeners = dict() 50 51 def subscribe(self, event, listener_object): 52 event_handler_name = "on_%s_handler" % event 53 if not hasattr(listener_object, event_handler_name): 54 raise AttributeError("Listener object has no '%s' event handler." % event) 55 try: 56 event_listeners = self._listeners[event] 57 except KeyError: 58 event_listeners = WeakSet() 59 self._listeners[event] = event_listeners 60 event_listeners.add(listener_object) 61 62 def unsubscribe(self, event, listener_object): 63 # Unsubscribing for objects which will be destroyed is not neccessary, 64 # just unsubscribe objects that should not receive further messages. 65 event_listeners = self._listeners[event] 66 event_listeners.remove(listener_object) 67 68 def broadcast(self, event, msg=None, root=None): 69 """ Broadcast an 'event' and submit 'msg' to the listeners event handler. 70 71 If the 'event' should only reach objects of one document, use the 'root' 72 parameter (get 'root' with the get_xmlroot() method) and only objects with 73 the same 'root' element receives the event. 74 """ 75 76 event_handler_name = "on_%s_handler" % event 77 def get_event_handler(listener): 78 return getattr(listener, event_handler_name) 79 80 def send_to_all(listener): 81 handler = get_event_handler(listener) 82 handler(msg=msg) 83 84 def send_to_root(listener): 85 try: 86 listener_root = listener.get_xmlroot() 87 except AttributeError: 88 return 89 90 if listener_root is root: 91 handler = get_event_handler(listener) 92 handler(msg=msg) 93 94 try: 95 event_listeners = self._listeners[event] 96 except KeyError: 97 # ok, because there is just no listener for this event 98 # but mispelling of 'event' is an error trap 99 return 100 101 send = send_to_root if root else send_to_all 102 for listener in event_listeners: 103 send(listener) 104 105 def _has_listener(self, event): 106 # just for testing 107 return self._count_listeners(event) > 0 108 109 def _count_listeners(self, event): 110 # just for testing 111 try: 112 return len(self._listeners[event]) 113 except KeyError: 114 return 0 115 116_global_observer = Observer() 117 118subscribe = _global_observer.subscribe 119unsubscripe = _global_observer.unsubscribe 120broadcast = _global_observer.broadcast 121