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)