Source code for director.callbacks

from weakref import ref
import new

'''
CallbackRegistry is a class taken from matplotlib.cbook.

http://sourceforge.net/p/matplotlib/code/HEAD/tree/trunk/matplotlib/lib/matplotlib/cbook.py
'''

[docs]class CallbackRegistry: """ Handle registering and disconnecting for a set of signals and callbacks:: signals = 'eat', 'drink', 'be merry' def oneat(x): print 'eat', x def ondrink(x): print 'drink', x callbacks = CallbackRegistry(signals) ideat = callbacks.connect('eat', oneat) iddrink = callbacks.connect('drink', ondrink) #tmp = callbacks.connect('drunk', ondrink) # this will raise a ValueError callbacks.process('drink', 123) # will call oneat callbacks.process('eat', 456) # will call ondrink callbacks.process('be merry', 456) # nothing will be called callbacks.disconnect(ideat) # disconnect oneat callbacks.process('eat', 456) # nothing will be called In practice, one should always disconnect all callbacks when they are no longer needed to avoid dangling references (and thus memory leaks). However, real code in matplotlib rarely does so, and due to its design, it is rather difficult to place this kind of code. To get around this, and prevent this class of memory leaks, we instead store weak references to bound methods only, so when the destination object needs to die, the CallbackRegistry won't keep it alive. The Python stdlib weakref module can not create weak references to bound methods directly, so we need to create a proxy object to handle weak references to bound methods (or regular free functions). This technique was shared by Peter Parente on his `"Mindtrove" blog <http://mindtrove.info/articles/python-weak-references/>`_. """ def __init__(self, signals): '*signals* is a sequence of valid signals' self.signals = set() self.callbacks = dict() for s in signals: self.addSignal(s) self._cid = 0 def _check_signal(self, s): 'make sure *s* is a valid signal or raise a ValueError' if s not in self.signals: signals = list(self.signals) signals.sort() raise ValueError('Unknown signal "%s"; valid signals are %s'%(s, signals))
[docs] def addSignal(self, sig): if sig not in self.signals: self.signals.add(sig) self.callbacks[sig] = dict()
[docs] def connect(self, s, func): """ register *func* to be called when a signal *s* is generated func will be called """ self._check_signal(s) proxy = BoundMethodProxy(func) for cid, callback in self.callbacks[s].items(): # Clean out dead references if callback.inst is not None and callback.inst() is None: del self.callbacks[s][cid] elif callback == proxy: return cid self._cid += 1 self.callbacks[s][self._cid] = proxy return self._cid
[docs] def disconnect(self, cid): """ disconnect the callback registered with callback id *cid* """ for eventname, callbackd in self.callbacks.items(): try: del callbackd[cid] except KeyError: continue else: return
[docs] def process(self, s, *args, **kwargs): """ process signal *s*. All of the functions registered to receive callbacks on *s* will be called with *\*args* and *\*\*kwargs* """ self._check_signal(s) for cid, proxy in self.callbacks[s].items(): # Clean out dead references if proxy.inst is not None and proxy.inst() is None: del self.callbacks[s][cid] else: proxy(*args, **kwargs)
[docs] def getCallbacks(self, s): """ return callbacks registered to signal *s*. """ self._check_signal(s) callbacks = [] for cid, proxy in self.callbacks[s].items(): # Clean out dead references if proxy.inst is not None and proxy.inst() is None: del self.callbacks[s][cid] else: callbacks.append(proxy) return callbacks
[docs]class BoundMethodProxy(object): ''' Our own proxy object which enables weak references to bound and unbound methods and arbitrary callables. Pulls information about the function, class, and instance out of a bound method. Stores a weak reference to the instance to support garbage collection. @organization: IBM Corporation @copyright: Copyright (c) 2005, 2006 IBM Corporation @license: The BSD License Minor bugfixes by Michael Droettboom ''' def __init__(self, cb): try: try: self.inst = ref(cb.im_self) except TypeError: self.inst = None self.func = cb.im_func self.klass = cb.im_class except AttributeError: self.inst = None self.func = cb self.klass = None def __call__(self, *args, **kwargs): ''' Proxy for a call to the weak referenced object. Take arbitrary params to pass to the callable. Raises `ReferenceError`: When the weak reference refers to a dead object ''' if self.inst is not None and self.inst() is None: raise ReferenceError elif self.inst is not None: # build a new instance method with a strong reference to the instance mtd = new.instancemethod(self.func, self.inst(), self.klass) else: # not a bound method, just return the func mtd = self.func # invoke the callable and return the result return mtd(*args, **kwargs) def __eq__(self, other): ''' Compare the held function and instance with that held by another proxy. ''' try: if self.inst is None: return self.func == other.func and other.inst is None else: return self.func == other.func and self.inst() == other.inst() except Exception: return False def __ne__(self, other): ''' Inverse of __eq__. ''' return not self.__eq__(other)