1
2
3
4 import weakref
5 import exceptions
6 import Queue
7 import time
8
9 (UPDATEVALUE, FIREEVENT) = (1, 2)
10
14
15
17 """Call slot with appropriate number of arguments"""
18 if hasattr(slot, '__call__'):
19
20 if hasattr( slot.__call__, 'im_func'):
21
22 slot = slot.__call__
23
24 if hasattr(slot, 'im_func'):
25
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
40 - def __init__(self, weakReceiver, dispatchMode):
41 self.weakReceiver = weakReceiver
42 self.dispatchMode = dispatchMode
43
44
46 slot = self.weakReceiver()
47
48 if slot is not None:
49 return robustApply(slot, arguments)
50
51
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
68
69
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
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)
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 )
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__
207 """Whether we are still a valid reference"""
208 return self() is not None
209
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
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 = {}
237 senders = {}
238
239
247
248
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
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 = ()):
326
327
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
342 try:
343 del connections[senderId]
344 del senders[senderId]
345 except KeyError:
346 pass
347
348
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
364 """Delete any empty signals for sender. Delete sender if empty."""
365 receivers = connections[senderId][signal]
366
367 if len(receivers) == 0:
368
369 signals = connections[senderId]
370 del signals[signal]
371
372 if len(signals) == 0:
373
374 _removeSender(senderId)
375