[Modeling-cvs] NotificationFramework CHANGES,1.5,1.6 setup.py,1.6,1.7 NotificationCenter.py,1.5,1.6
Status: Abandoned
Brought to you by:
sbigaret
From: <sbi...@us...> - 2003-05-25 19:06:53
|
Update of /cvsroot/modeling/NotificationFramework In directory sc8-pr-cvs1:/tmp/cvs-serv30958 Modified Files: CHANGES setup.py NotificationCenter.py Log Message: RFE #742511 Added the ability to register multiple callbacks for an object, triggered by env. variable NOTIFICATION_CENTER_MULTIPLE_CALLBACKS_PER_OBSERVER (it is NOT enabled by default) Fixed addObserver() which did not inform the user that a single callback can be registered per object --> this possibly led to buggy behaviour. Now it raises ValueError when this happens (this could make existing code fails because of that --be prepared to correct this. Added script tests/compare_perfs.py which calculates the performance penalty implied by the activation of the ability to handle multiple callbacks per object. Fixed: when generic and specific observers were registered, postNotification() made the list of observers grow (observers are copied back to the list). This slowed down the framework and a observer could then be notified more than once for the same notification. Fixed test_Notification.py and test_NotificationCenter.py: they couldn't be executed as stand-alone scripts. Added tests for RFE #742511 and fixed bugs Index: CHANGES =================================================================== RCS file: /cvsroot/modeling/NotificationFramework/CHANGES,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** CHANGES 14 Jan 2003 08:51:38 -0000 1.5 --- CHANGES 25 May 2003 19:06:49 -0000 1.6 *************** *** 1,2 **** --- 1,27 ---- + 0.5 [2003/05/25] + + - RFE #742511 Added the ability to register multiple callbacks for an + object, triggered by env. variable + NOTIFICATION_CENTER_MULTIPLE_CALLBACKS_PER_OBSERVER (it is NOT enabled + by default) + + Fixed addObserver() which did not inform the user that a single callback + can be registered per object --> this possibly led to buggy + behaviour. Now it raises ValueError when this happens (this could make + existing code fails because of that --be prepared to correct this. + + - Added script tests/compare_perfs.py which calculates the performance + penalty implied by the activation of the ability to handle multiple + callbacks per object + + - Fixed: when generic and specific observers were registered, + postNotification() made the list of observers grow (observers are copied + back to the list). This slowed down the framework and a observer could + then be notified more than once for the same notification. + + - Fixed test_Notification.py and test_NotificationCenter.py: they couldn't + be executed as stand-alone scripts. Added tests for RFE #742511 and fixed + bugs + 0.4 Now distributed under the GNU General Public License Index: setup.py =================================================================== RCS file: /cvsroot/modeling/NotificationFramework/setup.py,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** setup.py 14 Jan 2003 09:03:41 -0000 1.6 --- setup.py 25 May 2003 19:06:50 -0000 1.7 *************** *** 31,35 **** setup(name="NotificationFramework", ! version="0.4", licence ="GNU General Public License", description=short_description, --- 31,35 ---- setup(name="NotificationFramework", ! version="0.5", licence ="GNU General Public License", description=short_description, Index: NotificationCenter.py =================================================================== RCS file: /cvsroot/modeling/NotificationFramework/NotificationCenter.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** NotificationCenter.py 10 Jan 2003 10:45:44 -0000 1.5 --- NotificationCenter.py 25 May 2003 19:06:50 -0000 1.6 *************** *** 38,47 **** objects that a notification they are listening to was posted. To achieve this, objects should supply their reference, as well as a callback method ! at registration time. ! The only possible listeners are: class instances ('types.InstanceType'), ! code objects or functions (such as functions defined in a module). See ! method 'addObserver' for full details ; we will focus in the following on ! class instances. For listeners being class instances, callbacks' signature should minimally --- 38,48 ---- objects that a notification they are listening to was posted. To achieve this, objects should supply their reference, as well as a callback method ! at registration time (Note: by default only one callback is accepted per ! object; see discussion below). ! The possible listeners are: class instances ('types.InstanceType'), code ! objects or functions (such as functions defined in a module). See method ! 'addObserver' for full details ; we will focus in the following on class ! instances. For listeners being class instances, callbacks' signature should minimally *************** *** 89,92 **** --- 90,141 ---- and object is posted. + Registering more than one callback per object + + By default, addObserver() checks that only one callback is registered per + object; if you try to register a different one, you'll get a ValueError. + + Of course, a single object can listen to multiple notifications. In that + case, you'll probably register a given method, say, handleNotification(), + dedicated to this task:: + + def handleNotification(self, notification): + notification_name=notification.name() + if notification_name == "NOTIFICATION_ONE": + # code for notification one + elif notification_name == "NOTIFICATION_TWO" + # code for notification two + else: + # Unhandled notification + + However, sometimes you do not want this; for example, you want to bind + several methods of an object defined by a third-party module, and you do + not want to modify its code. Registering multiple callbacks is possible + when the environment variable + 'NOTIFICATION_CENTER_MULTIPLE_CALLBACKS_PER_OBSERVER' is defined and + its value is not an empty string. + + IMPORTANT: this variable should be set *prior* to any import statement + importing the NotificationCenter module, or it wont have any + effect at all. + + Example: + + bash -- + export NOTIFICATION_CENTER_MULTIPLE_CALLBACKS_PER_OBSERVER='y' + + python -- + + import os + os.environ['NOTIFICATION_CENTER_MULTIPLE_CALLBACKS_PER_OBSERVER']='y' + from NotificationFramework import NotificationCenter + + Why is it not the default/only behaviour? + + Handling multiple callbacks per object implies a performance + penalty. You can test which are the consequences on your platform with + the supplied script 'compare_perfs.py' in directory tests/ On my + machine, postNotification() is about 2.4x slower when the handling of + multiple callbacks per object is activated. + Notification names and objects *************** *** 102,106 **** Zope: some important notes ! TBD!!! --- 151,159 ---- Zope: some important notes ! TBD!!! In the meantime you can look at the code for methods ! observerCodeForZopePersistentObject() and ! observerCodeForZopeTemporaryObject(), and to ! tests/test_NotificationCenter. test_7_codeObjectObservers(). ! *************** *** 115,119 **** from threading import RLock import mems_lib, log ! import code, types, weakref # ZODB specific checks --- 168,172 ---- from threading import RLock import mems_lib, log ! import code, os, types, weakref # ZODB specific checks *************** *** 126,129 **** --- 179,185 ---- pass + env_handlesMultipleCallbackPerObserver='NOTIFICATION_CENTER_MULTIPLE_CALLBACKS_PER_OBSERVER' + __handlesMultipleCallbackPerObserver=os.environ.get(env_handlesMultipleCallbackPerObserver,None) + ## The following are private, do NOT access it directly! __observers__={} *************** *** 138,143 **** lock() try: ! # first the observers=__observers__.get(notification, []) # Then check for observers listening to the NotificationName, whatever the # object --- 194,211 ---- lock() try: ! # first the specific observers observers=__observers__.get(notification, []) + # We need a copy, or the next 'if' block will possibly change 'observers' + # which is BAD! + # (cf. test_10_post_to_generic_specific_observers_leaves_obsvs_untouched) + + # Additional note: since the module's methods are reentrant within a + # thread, it is possible for a listener to unregister itself when + # receiving the notification ; in that case and if we do not return a copy + # here, the sequence on which we iterate in postNotification() will be + # alterated *during* the iteration and this leads to an observer not being + # notified. + + observers=list(observers) # Then check for observers listening to the NotificationName, whatever the # object *************** *** 148,163 **** log.trace('_listToNotifyForNotification(%s)'%notification, 'Returns: %s'%str(observers)) ! # return a copy: since the module's methods are reentrant within a thread, ! # it is possible for a listener to unregister itself when receiving the ! # notification ; in that case and if we do not return a copy here, the ! # sequence on which we iterate in postNotification() will be alterated ! # *during* the iteration and this leads to an observer not being notified. ! return list(observers) finally: unlock() def _observers(): # for testing purposes mainly "PRIVATE, DO NOT CALL DIRECTLY" ! return __observers__.copy() # shallow copy ## --- 216,246 ---- log.trace('_listToNotifyForNotification(%s)'%notification, 'Returns: %s'%str(observers)) ! return observers finally: unlock() + if __handlesMultipleCallbackPerObserver: + def _callbacksForObserverAndNotification(observer, notification): + "Private: do not call directly" + lock() + try: + callback=__observers_callbacks__.get((observer, notification)) + callbacks=callback and [callback] or [] + if notification.object() is not None: + general_notification=Notification(notification.name()) + general_callback=__observers_callbacks__.get((observer, general_notification),None) + if general_callback: + callbacks.append(general_callback) + return callbacks + finally: + unlock() + def _observers(): # for testing purposes mainly "PRIVATE, DO NOT CALL DIRECTLY" ! obs={} ! for k,v in __observers__.items(): ! obs[k]=list(v) ! return obs ! ## *************** *** 175,178 **** --- 258,266 ---- first time they are supposed to receive a notification. + addObserver() raises ValueError when an attempt is made to register an + object that was previously registered with a different callback than the one + supplied. Please refer to the module's documentation for a complete + discussion on this topic. + Parameters: *************** *** 203,207 **** import code ! code=code.compile_command('import aModule;'\ 'observer_object=aModule.anObject') --- 291,295 ---- import code ! code=code.compile_command('import aModule;'+ 'observer_object=aModule.anObject') *************** *** 248,251 **** --- 336,346 ---- notification=Notification(notificationName, object) try: + if not __handlesMultipleCallbackPerObserver: + if __observers_callbacks__.get(observer, callback)!=callback: + raise ValueError, "object is already registered with a different callback (%s)%"%__observers_callbacks__.get(observer) + __observers_callbacks__[observer]=callback + else: + __observers_callbacks__[(observer,notification)]=callback + if __observers__.get(notification): if sameObserverRegistersOnce: *************** *** 256,260 **** else: __observers__[notification]=[observer] - __observers_callbacks__[observer]=callback finally: unlock() --- 351,354 ---- *************** *** 333,337 **** if param: log.trace('postNotification', 'Notifying: %s'%observer) ! apply(__observers_callbacks__[observer], param) finally: unlock() --- 427,436 ---- if param: log.trace('postNotification', 'Notifying: %s'%observer) ! if not __handlesMultipleCallbackPerObserver: ! apply(__observers_callbacks__[observer], param) ! else: ! for c in _callbacksForObserverAndNotification(observer, ! searchNotification): ! apply(c,param) finally: unlock() *************** *** 359,360 **** --- 458,460 ---- _code=code.compile_command(codeStr) return _code + |