source file: /home/buildslave/tahoe/edgy/build/src/allmydata/util/observer.py
file stats: 54 lines, 54 executed: 100.0% covered
   1. # -*- test-case-name: allmydata.test.test_observer -*-
   2. 
   3. from twisted.internet import defer
   4. from foolscap.eventual import eventually
   5. 
   6. """The idiom we use is for the observed object to offer a method named
   7. 'when_something', which returns a deferred.  That deferred will be fired when
   8. something happens.  The way this is typically implemented is that the observed
   9. has an ObserverList whose when_fired method is called in the observed's
  10. 'when_something'."""
  11. 
  12. class OneShotObserverList:
  13.     """A one-shot event distributor."""
  14.     def __init__(self):
  15.         self._fired = False
  16.         self._result = None
  17.         self._watchers = []
  18.         self.__repr__ = self._unfired_repr
  19. 
  20.     def _unfired_repr(self):
  21.         return "<OneShotObserverList [%s]>" % (self._watchers, )
  22. 
  23.     def _fired_repr(self):
  24.         return "<OneShotObserverList -> %s>" % (self._result, )
  25. 
  26.     def _get_result(self):
  27.         return self._result
  28. 
  29.     def when_fired(self):
  30.         if self._fired:
  31.             return defer.succeed(self._get_result())
  32.         d = defer.Deferred()
  33.         self._watchers.append(d)
  34.         return d
  35. 
  36.     def fire(self, result):
  37.         assert not self._fired
  38.         self._fired = True
  39.         self._result = result
  40.         self._fire(result)
  41. 
  42.     def _fire(self, result):
  43.         for w in self._watchers:
  44.             eventually(w.callback, result)
  45.         del self._watchers
  46.         self.__repr__ = self._fired_repr
  47. 
  48.     def fire_if_not_fired(self, result):
  49.         if not self._fired:
  50.             self.fire(result)
  51. 
  52. class LazyOneShotObserverList(OneShotObserverList):
  53.     """
  54.     a variant of OneShotObserverList which does not retain
  55.     the result it handles, but rather retains a callable()
  56.     through which is retrieves the data if and when needed.
  57.     """
  58.     def __init__(self):
  59.         OneShotObserverList.__init__(self)
  60. 
  61.     def _get_result(self):
  62.         return self._result_producer()
  63. 
  64.     def fire(self, result_producer):
  65.         """
  66.         @param result_producer: a no-arg callable which
  67.         returns the data which is to be considered the
  68.         'result' for this observer list.  note that this
  69.         function may be called multiple times - once
  70.         upon initial firing, and potentially once more
  71.         for each subsequent when_fired() deferred created
  72.         """
  73.         assert not self._fired
  74.         self._fired = True
  75.         self._result_producer = result_producer
  76.         if self._watchers: # if not, don't call result_producer
  77.             self._fire(self._get_result())
  78. 
  79. class ObserverList:
  80.     """A simple class to distribute events to a number of subscribers."""
  81. 
  82.     def __init__(self):
  83.         self._watchers = []
  84. 
  85.     def subscribe(self, observer):
  86.         self._watchers.append(observer)
  87. 
  88.     def unsubscribe(self, observer):
  89.         self._watchers.remove(observer)
  90. 
  91.     def notify(self, *args, **kwargs):
  92.         for o in self._watchers:
  93.             eventually(o, *args, **kwargs)