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