[Zope-CVS] CVS: Packages/pypes/pypes - event.py:1.5

Casey Duncan casey at zope.com
Wed Mar 3 23:29:47 EST 2004


Update of /cvs-repository/Packages/pypes/pypes
In directory cvs.zope.org:/tmp/cvs-serv12779

Modified Files:
	event.py 
Log Message:
Use persistent weakrefs for event listener registrations
Add event service persistence tests


=== Packages/pypes/pypes/event.py 1.4 => 1.5 ===
--- Packages/pypes/pypes/event.py:1.4	Sun Feb  8 22:56:21 2004
+++ Packages/pypes/pypes/event.py	Wed Mar  3 23:29:45 2004
@@ -17,9 +17,10 @@
 
 from __future__ import generators
 import cPickle
-from types import TypeType, ClassType
+from types import ClassType
 from BTrees.OOBTree import OOBTree, OOTreeSet
 from persistent import Persistent
+from persistent.wref import WeakRef
 from zope.interface import implements
 from pypes.interfaces import IEventService
 from pypes.exceptions import EventRegistrationError
@@ -32,7 +33,10 @@
 class EventService(Persistent):
     """Persistent event service
     
-    Note: event message types registered must be pickleable
+    - Event message types must be types or classes
+    
+    - Listeners are referenced using persistent weakrefs. This means that
+      all registered listeners must be Persistent instances.
     """
     
     implements(IEventService)
@@ -43,12 +47,14 @@
     
     def registerListener(self, obj, method_name, message_type):
         method = getattr(obj, method_name)
+        if not isinstance(obj, Persistent):
+            raise TypeError, 'Event listener not persistent'
         if not callable(method):
             raise TypeError, 'Event listener method registered is not callable'
-        if not isinstance(message_type, (TypeType, ClassType)):
+        if not isinstance(message_type, (type, ClassType)):
             raise TypeError, 'Event listener message_type must be type or class'
         type_key = _msgTypeKey(message_type)
-        pair = (obj, method_name)
+        pair = (WeakRef(obj), method_name)
         try:
             reglist = self._listeners[type_key]
         except KeyError:
@@ -62,7 +68,7 @@
         type_key = _msgTypeKey(message_type)
         try:
             listeners = self._listeners[type_key]
-            listeners.remove((obj, method_name))
+            listeners.remove((WeakRef(obj), method_name))
         except (KeyError, ValueError):
             raise EventRegistrationError, (obj, method_name, message_type)
         else:
@@ -90,23 +96,23 @@
         
     def isListener(self, obj, message_type):
         try:
-            listeners = self._listeners[_msgTypeKey(message_type)]
+            reglist = self._listeners[_msgTypeKey(message_type)]
         except KeyError:
             return False
         else:
-            for listener, name in listeners:
-                if listener is obj:
+            for listener, name in self._iterRegistrations(reglist):
+                if obj is listener:
                     return True
         return False            
 
     def wouldReceive(self, obj, message_type):
         for super_type in _mro(message_type):
             try:
-                listeners = self._listeners[_msgTypeKey(super_type)]
+                reglist = self._listeners[_msgTypeKey(super_type)]
             except KeyError:
                 continue
-            for listener, name in listeners:
-                if listener is obj:
+            for listener, name in self._iterRegistrations(reglist):
+                if obj is listener:
                     return True
         return False
         
@@ -116,7 +122,13 @@
         except KeyError:
             # Nothing is registered for this type
             return iter(())
-        return iter(reglist)
+        return self._iterRegistrations(reglist)
+    
+    def _iterRegistrations(self, reglist):
+        for listener, name in reglist:
+            listener = listener()
+            if listener is not None:
+                yield listener, name
         
     def iterReceivers(self, message_type):
         rlists = []
@@ -129,7 +141,7 @@
             # Optimize for the case where only one type has registrants
             # We don't need to enforce uniqueness in this case the
             # list is already unique
-            return iter(rlists[0])
+            return self._iterRegistrations(rlists[0])
         elif rlists:
             return self._iterReceiversLists(rlists)
         else:
@@ -138,16 +150,22 @@
     def _iterReceiversLists(self, rlists):
         # Iterate the receivers for each lists in turn,
         # making sure each (obj, name) pair is only returned once
-        seenpairs = {}
+        seen = {}
         for receivers in rlists:
-            for pair in receivers:
-                if not seenpairs.has_key(pair):
-                    seenpairs[pair] = None
+            for listener, name in receivers:
+                listener = listener()
+                pair = (listener, name)
+                if listener is not None and pair not in seen:
+                    seen[pair] = True
                     yield pair
     
     def iterMessageTypes(self):
         for typekey in self._listeners.keys():
             yield _msgTypeFromKey(typekey)
+    
+    # XXX Need to add mechanism to clean stale listener references
+    # perhaps this should be opportunistic as part of registerListener
+    # or a separate method or both
 
 def _mro(typeobj):
     """Calculate and return the method resolution order. Works for




More information about the Zope-CVS mailing list