Package Products :: Package ZenUtils :: Module observable
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenUtils.observable

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2009, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 or (at your 
  8  # option) any later version as published by the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  """ 
 15  The observable module provides an interface and an optional mixin class that 
 16  provide an Observer pattern for attribute based change notifications of an  
 17  object. 
 18   
 19  An object that is Observable (i.e. provides the IObservable interface) will 
 20  allow attribute observers, a.k.a. listeners, to be attached or detached for 
 21  specific attribute names. These observers will be notified whenever the  
 22  specified attribute changes value. 
 23   
 24  Observers can be any Python object that is callable. 
 25   
 26  An example of using the observer pattern in your implementation is: 
 27   
 28   
 29  class MyObject(MyParentClass, ObservableMixin): 
 30      def __init__(self): 
 31          super(MyObject, self).__init__() 
 32          self.name = "Super Duper Object" 
 33          self.age = 42 
 34   
 35      def doIt(self): 
 36          self.name = "I changed my name!" 
 37          self.age = 37 
 38   
 39  def ageListener(observable, attrName, oldValue, newValue): 
 40      print "Age has been changed from %d to %d" % (oldValue, newValue) 
 41   
 42  foo = MyObject() 
 43  foo.attachAttributeObserver("age", ageListener) 
 44  foo.doIt() 
 45   
 46   
 47  This implementation will likely only work with new style classes that have been 
 48  properly implemented. 
 49  """ 
 50   
 51  import Globals 
 52  import zope.interface 
 53   
54 -class IObservable(zope.interface.Interface):
55 """ 56 Classes that implement the IObservable interface agree to provide the 57 Observer pattern for object attribute changes. 58 """
59 - def attachAttributeObserver(self, name, observer):
60 """ 61 Attaches an observer that will be notified when the specified attribute 62 changes value. 63 64 @param name the attribute name to observer 65 @param observer the observer/listener to be notified 66 @type observer callable 67 """ 68 pass
69
70 - def detachAttributeObserver(self, name, observer):
71 """ 72 Removes an observer from watching the specified attribute. 73 74 @param name the attribute name to remove the observer from 75 @param observer the observer/listener to be removed 76 """ 77 pass
78
79 - def notifyAttributeChange(self, name, oldValue, newValue):
80 """ 81 Notify all registered observers that an attribute has changed value. 82 Register observers must be a Python callable and will receive the 83 following keyword arguments: 84 observerable - a reference to the observed object 85 attrName - the attribute name that has changed 86 oldValue - the previous value 87 newValue - the new value 88 89 @param name the attribute name that has changed 90 @param oldValue the old attribute value 91 @param newValue the new attribute value 92 """ 93 pass
94
95 -class ObservableMixin(object):
96 """ 97 A mixin class that provides an implementation of the IObservable interface 98 for any new-style class to use. This implementation will provide 99 notification for all attribute changes, except for the attributes used 100 to track the registered observers themselves. 101 """ 102 zope.interface.implements(IObservable) 103
104 - def __init__(self):
105 super(ObservableMixin, self).__init__() 106 self._observers = {}
107
108 - def attachAttributeObserver(self, name, observer):
109 if not callable(observer): 110 raise ValueError("observer must be callable") 111 112 if not name in self._observers: 113 self._observers[name] = [] 114 115 if observer not in self._observers[name]: 116 self._observers[name].append(observer)
117
118 - def detachAttributeObserver(self, name, observer):
119 try: 120 self._observers[name].remove(observer) 121 except (KeyError, ValueError): 122 pass
123
124 - def notifyAttributeChange(self, name, oldValue, newValue):
125 # don't bother notifying if we don't have an _observers attribute 126 # yet (during construction) 127 if hasattr(self, '_observers') and name in self._observers: 128 for observer in self._observers[name]: 129 observer(observable=self, 130 attrName=name, 131 oldValue=oldValue, 132 newValue=newValue)
133
134 - def __setattr__(self, name, newValue):
135 # override the __setattr__ method so that we can grab the previous 136 # value and then notify all of the observers of the change 137 oldValue = getattr(self, name, None) 138 super(ObservableMixin, self).__setattr__(name, newValue) 139 self.notifyAttributeChange(name, oldValue, newValue)
140