2025-10-10 09:46:41 +02:00

121 lines
3.9 KiB
Python

#!/usr/bin/env python
#coding:utf-8
# Purpose: observer pattern
# Created: 22.01.2011
# Copyright (C) 2011, Manfred Moitzi
# License: MIT license
from __future__ import unicode_literals, print_function, division
__author__ = "mozman <mozman@gmx.at>"
try:
from weakref import WeakSet
except ImportError:
from weakrefset import WeakSet
class Observer(object):
""" Simple implementation of the observer pattern for broadcasting messages
to objects.
For every event the subscriber object need an event handler called 'on_event_handler'
accepting the parameter 'msg'.
Because of the simple implementation of the algorithm it is necessary to
register the objects an not only the listener methods, because the methods of
different objects of the same calss have the same 'id' and managing the
listeners in a WeakSet is not possible for different objects (you could
only manage one table in one document instance).
Example for event: 'save'
# module 'one'
import observer
class Listener:
def on_save_handler(self, msg):
pass
def get_root(self):
return None
listener = Listener()
# subscribe to the 'global observer'
observer.subscribe('save', listener)
# module 'two'
import observer
# calls listener.on_save_handler(msg=None)
observer.broadcast('save', msg=None)
"""
def __init__(self):
self._listeners = dict()
def subscribe(self, event, listener_object):
event_handler_name = "on_%s_handler" % event
if not hasattr(listener_object, event_handler_name):
raise AttributeError("Listener object has no '%s' event handler." % event)
try:
event_listeners = self._listeners[event]
except KeyError:
event_listeners = WeakSet()
self._listeners[event] = event_listeners
event_listeners.add(listener_object)
def unsubscribe(self, event, listener_object):
# Unsubscribing for objects which will be destroyed is not neccessary,
# just unsubscribe objects that should not receive further messages.
event_listeners = self._listeners[event]
event_listeners.remove(listener_object)
def broadcast(self, event, msg=None, root=None):
""" Broadcast an 'event' and submit 'msg' to the listeners event handler.
If the 'event' should only reach objects of one document, use the 'root'
parameter (get 'root' with the get_xmlroot() method) and only objects with
the same 'root' element receives the event.
"""
event_handler_name = "on_%s_handler" % event
def get_event_handler(listener):
return getattr(listener, event_handler_name)
def send_to_all(listener):
handler = get_event_handler(listener)
handler(msg=msg)
def send_to_root(listener):
try:
listener_root = listener.get_xmlroot()
except AttributeError:
return
if listener_root is root:
handler = get_event_handler(listener)
handler(msg=msg)
try:
event_listeners = self._listeners[event]
except KeyError:
# ok, because there is just no listener for this event
# but mispelling of 'event' is an error trap
return
send = send_to_root if root else send_to_all
for listener in event_listeners:
send(listener)
def _has_listener(self, event):
# just for testing
return self._count_listeners(event) > 0
def _count_listeners(self, event):
# just for testing
try:
return len(self._listeners[event])
except KeyError:
return 0
_global_observer = Observer()
subscribe = _global_observer.subscribe
unsubscripe = _global_observer.unsubscribe
broadcast = _global_observer.broadcast