Package SpecClient :: Module SpecEventsDispatcher
[hide private]
[frames] | no frames]

Source Code for Module SpecClient.SpecEventsDispatcher

  1  #$Id: SpecEventsDispatcher.py,v 1.4 2005/09/27 13:54:19 guijarro Exp $ 
  2   
  3  #import logging 
  4  import weakref 
  5  import exceptions 
  6  import Queue 
  7  import time 
  8   
  9  (UPDATEVALUE, FIREEVENT) = (1, 2) 
 10   
11 -class SpecClientDispatcherError(exceptions.Exception):
12 - def __init__(self, args=None):
13 self.args = args
14 15
16 -def robustApply(slot, arguments = ()):
17 """Call slot with appropriate number of arguments""" 18 if hasattr(slot, '__call__'): 19 # Slot is a class instance ? 20 if hasattr( slot.__call__, 'im_func'): # or hasattr( slot.__call__, 'im_code'): WARNING:im_code does not seem to exist? 21 # Reassign slot to the actual method that will be called 22 slot = slot.__call__ 23 24 if hasattr(slot, 'im_func'): 25 # an instance method 26 n_args = slot.im_func.func_code.co_argcount - 1 27 else: 28 try: 29 n_args = slot.func_code.co_argcount 30 except: 31 raise SpecClientDispatcherError, 'Unknown slot type %s %s' % (repr(slot), type(slot)) 32 33 if len(arguments) < n_args: 34 raise SpecClientDispatcherError, 'Not enough arguments for calling slot %s (need: %d, given: %d)' % (repr(slot), n_args, len(arguments)) 35 else: 36 return slot(*arguments[0:n_args])
37 38
39 -class Receiver:
40 - def __init__(self, weakReceiver, dispatchMode):
41 self.weakReceiver = weakReceiver 42 self.dispatchMode = dispatchMode
43 44
45 - def __call__(self, arguments):
46 slot = self.weakReceiver() #get the strong reference 47 48 if slot is not None: 49 return robustApply(slot, arguments)
50 51
52 -class Event:
53 - def __init__(self, sender, signal, arguments):
54 self.receivers = [] 55 senderId = id(sender) 56 signal = str(signal) 57 self.args = arguments 58 59 try: 60 self.receivers = connections[senderId][signal] 61 except: 62 pass
63 64
65 -class EventsQueue(Queue.Queue):
66 - def __init__(self):
67 Queue.Queue.__init__(self, 0)
68 69
70 - def get(self):
71 """Remove and return an item from the queue.""" 72 try: 73 return Queue.Queue.get(self, False) 74 except Queue.Empty: 75 raise IndexError
76 77
78 - def put(self, event):
79 """Put an event into the queue.""" 80 receiversList = event.receivers 81 82 self.mutex.acquire() 83 try: 84 was_empty = not self._qsize() 85 86 for r in receiversList: 87 if not was_empty: 88 if r.dispatchMode == UPDATEVALUE: 89 for i in range(len(self.queue)): 90 _r, args = self.queue[i] 91 if r == _r: 92 del self.queue[i] 93 break 94 95 self._put( (r, event.args) ) 96 finally: 97 self.mutex.release()
98 99
100 -class BoundMethodWeakRef(object):
101 """'Safe' and reusable weak references to instance methods 102 103 BoundMethodWeakRef objects provide a mechanism for 104 referencing a bound method without requiring that the 105 method object itself (which is normally a transient 106 object) is kept alive. Instead, the BoundMethodWeakref 107 object keeps weak references to both the object and the 108 function which together define the instance method. 109 110 Attributes: 111 key -- the identity key for the reference, calculated 112 by the class's calculateKey method applied to the 113 target instance method 114 deletionMethods -- sequence of callable objects taking 115 single argument, a reference to this object which 116 will be called when *either* the target object or 117 target function is garbage collected (i.e. when 118 this object becomes invalid). These are specified 119 as the onDelete parameters of safeRef calls. 120 weakSelf -- weak reference to the target object 121 weakFunc -- weak reference to the target function 122 123 Class Attributes: 124 _allInstances -- class attribute pointing to all live 125 BoundMethodWeakref objects indexed by the class's 126 calculateKey(target) method applied to the target 127 objects. This weak value dictionary is used to 128 short-circuit creation so that multiple references 129 to the same (object, function) pair produce the 130 same BoundMethodWeakref instance. 131 132 """ 133 _allInstances = weakref.WeakValueDictionary()
134 - def __new__( cls, target, onDelete=None, *arguments,**named ):
135 """Create new instance or return current instance 136 137 Basically this method of construction allows us to 138 short-circuit creation of references to already- 139 referenced instance methods. The key corresponding 140 to the target is calculated, and if there is already 141 an existing reference, that is returned, with its 142 deletionMethods attribute updated. Otherwise the 143 new instance is created and registered in the table 144 of already-referenced methods. 145 """ 146 key = cls.calculateKey(target) 147 current =cls._allInstances.get(key) 148 if current is not None: 149 current.deletionMethods.append( onDelete) 150 return current 151 else: 152 base = super( BoundMethodWeakRef, cls).__new__( cls ) 153 cls._allInstances[key] = base 154 base.__init__( target, onDelete, *arguments,**named) 155 return base
156 - def __init__(self, target, onDelete=None):
157 """Return a weak-reference-like instance for a bound method 158 159 target -- the instance-method target for the weak 160 reference, must have im_self and im_func attributes 161 and be reconstructable via: 162 target.im_func.__get__( target.im_self ) 163 which is true of built-in instance methods. 164 onDelete -- optional callback which will be called 165 when this weak reference ceases to be valid 166 (i.e. either the object or the function is garbage 167 collected). Should take a single argument, 168 which will be passed a pointer to this object. 169 """ 170 def remove(weak, self=self): 171 """Set self.isDead to true when method or instance is destroyed""" 172 methods = self.deletionMethods[:] 173 del self.deletionMethods[:] 174 try: 175 del self.__class__._allInstances[ self.key ] 176 except KeyError: 177 pass 178 179 for function in methods: 180 try: 181 if callable( function ): 182 function( self ) 183 except: 184 pass
185 186 self.deletionMethods = [onDelete] 187 self.key = self.calculateKey( target ) 188 self.weakSelf = weakref.ref(target.im_self ,remove) 189 self.weakFunc = weakref.ref(target.im_func, remove)
190 - def calculateKey( cls, target ):
191 """Calculate the reference key for this reference 192 193 Currently this is a two-tuple of the id()'s of the 194 target object and the target function respectively. 195 """ 196 return (id(target.im_self),id(target.im_func))
197 calculateKey = classmethod( calculateKey )
198 - def __str__(self):
199 """Give a friendly representation of the object""" 200 return """%s( %s.%s )"""%( 201 self.__class__.__name__, 202 self.weakSelf(), 203 self.weakFunc().__name__, 204 )
205 __repr__ = __str__
206 - def __nonzero__( self ):
207 """Whether we are still a valid reference""" 208 return self() is not None
209
210 - def __cmp__( self, other ):
211 """Compare with another reference""" 212 if not isinstance (other,self.__class__): 213 return cmp( self.__class__, type(other) ) 214 return cmp( self.key, other.key)
215
216 - def __call__(self):
217 """Return a strong reference to the bound method 218 219 If the target cannot be retrieved, then will 220 return None, otherwise returns a bound instance 221 method for our object and function. 222 223 Note: 224 You may call this method any number of times, 225 as it does not invalidate the reference. 226 """ 227 target = self.weakSelf() 228 if target is not None: 229 function = self.weakFunc() 230 if function is not None: 231 return function.__get__(target) 232 return None
233 234 235 eventsToDispatch = EventsQueue() 236 connections = {} # { senderId0: { signal0: [receiver0, ..., receiverN], signal1: [...], ... }, senderId1: ... } 237 senders = {} # { senderId: sender, ... } 238 239
240 -def callableObjectRef(object):
241 """Return a safe weak reference to a callable object""" 242 if hasattr(object, 'im_self'): 243 if object.im_self is not None: 244 # turn a bound method into a BoundMethodWeakReference instance 245 return BoundMethodWeakRef(object, _removeReceiver) 246 return weakref.ref(object, _removeReceiver)
247 248
249 -def connect(sender, signal, slot, dispatchMode = UPDATEVALUE):
250 if sender is None or signal is None: 251 return 252 253 if not callable(slot): 254 return 255 256 senderId = id(sender) 257 signal = str(signal) 258 signals = {} 259 260 if senderId in connections: 261 signals = connections[senderId] 262 else: 263 connections[senderId] = signals 264 265 def remove(object, senderId=senderId): 266 _removeSender(senderId)
267 268 try: 269 weakSender = weakref.ref(sender, remove) 270 senders[senderId] = weakSender 271 except: 272 pass 273 274 receivers = [] 275 276 if signal in signals: 277 receivers = signals[signal] 278 else: 279 signals[signal] = receivers 280 281 weakReceiver = callableObjectRef(slot) 282 283 for r in receivers: 284 if r.weakReceiver == weakReceiver: 285 r.dispatchMode = dispatchMode 286 return 287 288 receivers.append(Receiver(weakReceiver, dispatchMode)) 289 290
291 -def disconnect(sender, signal, slot):
292 if sender is None or signal is None: 293 return 294 295 if not callable(slot): 296 return 297 298 senderId = id(sender) 299 signal = str(signal) 300 301 try: 302 signals = connections[senderId] 303 except KeyError: 304 return 305 else: 306 try: 307 receivers = signals[signal] 308 except KeyError: 309 return 310 else: 311 weakReceiver = callableObjectRef(slot) 312 313 toDel = None 314 for r in receivers: 315 if r.weakReceiver == weakReceiver: 316 toDel = r 317 break 318 if toDel is not None: 319 receivers.remove(toDel) 320 321 _cleanupConnections(senderId, signal)
322 323
324 -def emit(sender, signal, arguments = ()):
325 eventsToDispatch.put(Event(sender, signal, arguments))
326 327
328 -def dispatch():
329 t0 = time.time() 330 while 1: 331 try: 332 receiver, args = eventsToDispatch.get() 333 except IndexError: 334 break 335 else: 336 receiver(args) 337 if (time.time()-t0) >= 1: 338 break
339 340
341 -def _removeSender(senderId):
342 try: 343 del connections[senderId] 344 del senders[senderId] 345 except KeyError: 346 pass
347 348
349 -def _removeReceiver(weakReceiver):
350 """Remove receiver from connections""" 351 for senderId in connections.keys(): 352 for signal in connections[senderId].keys(): 353 receivers = connections[senderId][signal] 354 355 for r in receivers: 356 if r.weakReceiver == weakReceiver: 357 receivers.remove(r) 358 break 359 360 _cleanupConnections(senderId, signal)
361 362
363 -def _cleanupConnections(senderId, signal):
364 """Delete any empty signals for sender. Delete sender if empty.""" 365 receivers = connections[senderId][signal] 366 367 if len(receivers) == 0: 368 # no more receivers 369 signals = connections[senderId] 370 del signals[signal] 371 372 if len(signals) == 0: 373 # no more signals 374 _removeSender(senderId)
375