1 """Multiple-producer-multiple-consumer signal-dispatching |
1 import weakref |
2 |
2 try: |
3 dispatcher is the core of the PyDispatcher system, |
3 set |
4 providing the primary API and the core logic for the |
4 except NameError: |
5 system. |
5 from sets import Set as set # Python 2.3 fallback |
6 |
6 |
7 Module attributes of note: |
7 from django.dispatch import saferef |
8 |
8 |
9 Any -- Singleton used to signal either "Any Sender" or |
9 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) |
10 "Any Signal". See documentation of the _Any class. |
10 |
11 Anonymous -- Singleton used to signal "Anonymous Sender" |
11 def _make_id(target): |
12 See documentation of the _Anonymous class. |
12 if hasattr(target, 'im_func'): |
13 |
13 return (id(target.im_self), id(target.im_func)) |
14 Internal attributes: |
14 return id(target) |
15 WEAKREF_TYPES -- tuple of types/classes which represent |
15 |
16 weak references to receivers, and thus must be de- |
16 class Signal(object): |
17 referenced on retrieval to retrieve the callable |
17 """Base class for all signals |
18 object |
18 |
19 connections -- { senderkey (id) : { signal : [receivers...]}} |
19 Internal attributes: |
20 senders -- { senderkey (id) : weakref(sender) } |
20 receivers -- { receriverkey (id) : weakref(receiver) } |
21 used for cleaning up sender references on sender |
|
22 deletion |
|
23 sendersBack -- { receiverkey (id) : [senderkey (id)...] } |
|
24 used for cleaning up receiver references on receiver |
|
25 deletion, (considerably speeds up the cleanup process |
|
26 vs. the original code.) |
|
27 """ |
|
28 import types, weakref |
|
29 from django.dispatch import saferef, robustapply, errors |
|
30 |
|
31 __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" |
|
32 __cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $" |
|
33 __version__ = "$Revision: 1.9 $"[11:-2] |
|
34 |
|
35 |
|
36 class _Parameter: |
|
37 """Used to represent default parameter values.""" |
|
38 def __repr__(self): |
|
39 return self.__class__.__name__ |
|
40 |
|
41 class _Any(_Parameter): |
|
42 """Singleton used to signal either "Any Sender" or "Any Signal" |
|
43 |
|
44 The Any object can be used with connect, disconnect, |
|
45 send, or sendExact to signal that the parameter given |
|
46 Any should react to all senders/signals, not just |
|
47 a particular sender/signal. |
|
48 """ |
21 """ |
49 Any = _Any() |
22 |
50 |
23 def __init__(self, providing_args=None): |
51 class _Anonymous(_Parameter): |
24 """providing_args -- A list of the arguments this signal can pass along in |
52 """Singleton used to signal "Anonymous Sender" |
25 a send() call. |
53 |
26 """ |
54 The Anonymous object is used to signal that the sender |
27 self.receivers = [] |
55 of a message is not specified (as distinct from being |
28 if providing_args is None: |
56 "any sender"). Registering callbacks for Anonymous |
29 providing_args = [] |
57 will only receive messages sent without senders. Sending |
30 self.providing_args = set(providing_args) |
58 with anonymous will only send messages to those receivers |
31 |
59 registered for Any or Anonymous. |
32 def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): |
60 |
33 """Connect receiver to sender for signal |
61 Note: |
34 |
62 The default sender for connect is Any, while the |
35 receiver -- a function or an instance method which is to |
63 default sender for send is Anonymous. This has |
36 receive signals. Receivers must be |
64 the effect that if you do not specify any senders |
37 hashable objects. |
65 in either function then all messages are routed |
38 |
66 as though there was a single sender (Anonymous) |
39 if weak is True, then receiver must be weak-referencable |
67 being used everywhere. |
40 (more precisely saferef.safeRef() must be able to create |
68 """ |
41 a reference to the receiver). |
69 Anonymous = _Anonymous() |
42 |
70 |
43 Receivers must be able to accept keyword arguments. |
71 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) |
44 |
72 |
45 If receivers have a dispatch_uid attribute, the receiver will |
73 connections = {} |
46 not be added if another receiver already exists with that |
74 senders = {} |
47 dispatch_uid. |
75 sendersBack = {} |
48 |
76 |
49 sender -- the sender to which the receiver should respond |
77 |
50 Must either be of type Signal, or None to receive events |
78 def connect(receiver, signal=Any, sender=Any, weak=True): |
51 from any sender. |
79 """Connect receiver to sender for signal |
52 |
80 |
53 weak -- whether to use weak references to the receiver |
81 receiver -- a callable Python object which is to receive |
54 By default, the module will attempt to use weak |
82 messages/signals/events. Receivers must be hashable |
55 references to the receiver objects. If this parameter |
83 objects. |
56 is false, then strong references will be used. |
84 |
57 |
85 if weak is True, then receiver must be weak-referencable |
58 dispatch_uid -- an identifier used to uniquely identify a particular |
86 (more precisely saferef.safeRef() must be able to create |
59 instance of a receiver. This will usually be a string, though it |
87 a reference to the receiver). |
60 may be anything hashable. |
88 |
61 |
89 Receivers are fairly flexible in their specification, |
62 returns None |
90 as the machinery in the robustApply module takes care |
63 """ |
91 of most of the details regarding figuring out appropriate |
64 from django.conf import settings |
92 subsets of the sent arguments to apply to a given |
65 |
93 receiver. |
66 # If DEBUG is on, check that we got a good receiver |
94 |
67 if settings.DEBUG: |
95 Note: |
68 import inspect |
96 if receiver is itself a weak reference (a callable), |
69 assert callable(receiver), "Signal receivers must be callable." |
97 it will be de-referenced by the system's machinery, |
70 |
98 so *generally* weak references are not suitable as |
71 # Check for **kwargs |
99 receivers, though some use might be found for the |
72 # Not all callables are inspectable with getargspec, so we'll |
100 facility whereby a higher-level library passes in |
73 # try a couple different ways but in the end fall back on assuming |
101 pre-weakrefed receiver references. |
74 # it is -- we don't want to prevent registration of valid but weird |
102 |
75 # callables. |
103 signal -- the signal to which the receiver should respond |
76 try: |
104 |
77 argspec = inspect.getargspec(receiver) |
105 if Any, receiver will receive any signal from the |
78 except TypeError: |
106 indicated sender (which might also be Any, but is not |
79 try: |
107 necessarily Any). |
80 argspec = inspect.getargspec(receiver.__call__) |
108 |
81 except (TypeError, AttributeError): |
109 Otherwise must be a hashable Python object other than |
82 argspec = None |
110 None (DispatcherError raised on None). |
83 if argspec: |
111 |
84 assert argspec[2] is not None, \ |
112 sender -- the sender to which the receiver should respond |
85 "Signal receivers must accept keyword arguments (**kwargs)." |
113 |
86 |
114 if Any, receiver will receive the indicated signals |
87 if dispatch_uid: |
115 from any sender. |
88 lookup_key = (dispatch_uid, _make_id(sender)) |
116 |
|
117 if Anonymous, receiver will only receive indicated |
|
118 signals from send/sendExact which do not specify a |
|
119 sender, or specify Anonymous explicitly as the sender. |
|
120 |
|
121 Otherwise can be any python object. |
|
122 |
|
123 weak -- whether to use weak references to the receiver |
|
124 By default, the module will attempt to use weak |
|
125 references to the receiver objects. If this parameter |
|
126 is false, then strong references will be used. |
|
127 |
|
128 returns None, may raise DispatcherTypeError |
|
129 """ |
|
130 if signal is None: |
|
131 raise errors.DispatcherTypeError( |
|
132 'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender) |
|
133 ) |
|
134 if weak: |
|
135 receiver = saferef.safeRef(receiver, onDelete=_removeReceiver) |
|
136 senderkey = id(sender) |
|
137 |
|
138 signals = connections.setdefault(senderkey, {}) |
|
139 |
|
140 # Keep track of senders for cleanup. |
|
141 # Is Anonymous something we want to clean up? |
|
142 if sender not in (None, Anonymous, Any): |
|
143 def remove(object, senderkey=senderkey): |
|
144 _removeSender(senderkey=senderkey) |
|
145 # Skip objects that can not be weakly referenced, which means |
|
146 # they won't be automatically cleaned up, but that's too bad. |
|
147 try: |
|
148 weakSender = weakref.ref(sender, remove) |
|
149 senders[senderkey] = weakSender |
|
150 except: |
|
151 pass |
|
152 |
|
153 receiverID = id(receiver) |
|
154 # get current set, remove any current references to |
|
155 # this receiver in the set, including back-references |
|
156 if signals.has_key(signal): |
|
157 receivers = signals[signal] |
|
158 _removeOldBackRefs(senderkey, signal, receiver, receivers) |
|
159 else: |
|
160 receivers = signals[signal] = [] |
|
161 try: |
|
162 current = sendersBack.get( receiverID ) |
|
163 if current is None: |
|
164 sendersBack[ receiverID ] = current = [] |
|
165 if senderkey not in current: |
|
166 current.append(senderkey) |
|
167 except: |
|
168 pass |
|
169 |
|
170 receivers.append(receiver) |
|
171 |
|
172 |
|
173 |
|
174 def disconnect(receiver, signal=Any, sender=Any, weak=True): |
|
175 """Disconnect receiver from sender for signal |
|
176 |
|
177 receiver -- the registered receiver to disconnect |
|
178 signal -- the registered signal to disconnect |
|
179 sender -- the registered sender to disconnect |
|
180 weak -- the weakref state to disconnect |
|
181 |
|
182 disconnect reverses the process of connect, |
|
183 the semantics for the individual elements are |
|
184 logically equivalent to a tuple of |
|
185 (receiver, signal, sender, weak) used as a key |
|
186 to be deleted from the internal routing tables. |
|
187 (The actual process is slightly more complex |
|
188 but the semantics are basically the same). |
|
189 |
|
190 Note: |
|
191 Using disconnect is not required to cleanup |
|
192 routing when an object is deleted, the framework |
|
193 will remove routes for deleted objects |
|
194 automatically. It's only necessary to disconnect |
|
195 if you want to stop routing to a live object. |
|
196 |
|
197 returns None, may raise DispatcherTypeError or |
|
198 DispatcherKeyError |
|
199 """ |
|
200 if signal is None: |
|
201 raise errors.DispatcherTypeError( |
|
202 'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender) |
|
203 ) |
|
204 if weak: receiver = saferef.safeRef(receiver) |
|
205 senderkey = id(sender) |
|
206 try: |
|
207 signals = connections[senderkey] |
|
208 receivers = signals[signal] |
|
209 except KeyError: |
|
210 raise errors.DispatcherKeyError( |
|
211 """No receivers found for signal %r from sender %r""" %( |
|
212 signal, |
|
213 sender |
|
214 ) |
|
215 ) |
|
216 try: |
|
217 # also removes from receivers |
|
218 _removeOldBackRefs(senderkey, signal, receiver, receivers) |
|
219 except ValueError: |
|
220 raise errors.DispatcherKeyError( |
|
221 """No connection to receiver %s for signal %s from sender %s""" %( |
|
222 receiver, |
|
223 signal, |
|
224 sender |
|
225 ) |
|
226 ) |
|
227 _cleanupConnections(senderkey, signal) |
|
228 |
|
229 def getReceivers( sender = Any, signal = Any ): |
|
230 """Get list of receivers from global tables |
|
231 |
|
232 This utility function allows you to retrieve the |
|
233 raw list of receivers from the connections table |
|
234 for the given sender and signal pair. |
|
235 |
|
236 Note: |
|
237 there is no guarantee that this is the actual list |
|
238 stored in the connections table, so the value |
|
239 should be treated as a simple iterable/truth value |
|
240 rather than, for instance a list to which you |
|
241 might append new records. |
|
242 |
|
243 Normally you would use liveReceivers( getReceivers( ...)) |
|
244 to retrieve the actual receiver objects as an iterable |
|
245 object. |
|
246 """ |
|
247 existing = connections.get(id(sender)) |
|
248 if existing is not None: |
|
249 return existing.get(signal, []) |
|
250 return [] |
|
251 |
|
252 def liveReceivers(receivers): |
|
253 """Filter sequence of receivers to get resolved, live receivers |
|
254 |
|
255 This is a generator which will iterate over |
|
256 the passed sequence, checking for weak references |
|
257 and resolving them, then returning all live |
|
258 receivers. |
|
259 """ |
|
260 for receiver in receivers: |
|
261 if isinstance( receiver, WEAKREF_TYPES): |
|
262 # Dereference the weak reference. |
|
263 receiver = receiver() |
|
264 if receiver is not None: |
|
265 yield receiver |
|
266 else: |
89 else: |
267 yield receiver |
90 lookup_key = (_make_id(receiver), _make_id(sender)) |
268 |
91 |
269 |
92 if weak: |
270 |
93 receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) |
271 def getAllReceivers( sender = Any, signal = Any ): |
94 |
272 """Get list of all receivers from global tables |
95 for r_key, _ in self.receivers: |
273 |
96 if r_key == lookup_key: |
274 This gets all dereferenced receivers which should receive |
97 break |
275 the given signal from sender, each receiver should |
98 else: |
276 be produced only once by the resulting generator |
99 self.receivers.append((lookup_key, receiver)) |
277 """ |
100 |
278 receivers = {} |
101 def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): |
279 # Get receivers that receive *this* signal from *this* sender. |
102 """Disconnect receiver from sender for signal |
280 # Add receivers that receive *any* signal from *this* sender. |
103 |
281 # Add receivers that receive *this* signal from *any* sender. |
104 receiver -- the registered receiver to disconnect. May be none if |
282 # Add receivers that receive *any* signal from *any* sender. |
105 dispatch_uid is specified. |
283 l = [] |
106 sender -- the registered sender to disconnect |
284 i = id(sender) |
107 weak -- the weakref state to disconnect |
285 if i in connections: |
108 dispatch_uid -- the unique identifier of the receiver to disconnect |
286 sender_receivers = connections[i] |
109 |
287 if signal in sender_receivers: |
110 disconnect reverses the process of connect. |
288 l.extend(sender_receivers[signal]) |
111 |
289 if signal is not Any and Any in sender_receivers: |
112 If weak references are used, disconnect need not be called. |
290 l.extend(sender_receivers[Any]) |
113 The receiver will be remove from dispatch automatically. |
291 |
114 |
292 if sender is not Any: |
115 returns None |
293 i = id(Any) |
116 """ |
294 if i in connections: |
117 |
295 sender_receivers = connections[i] |
118 if dispatch_uid: |
296 if sender_receivers is not None: |
119 lookup_key = (dispatch_uid, _make_id(sender)) |
297 if signal in sender_receivers: |
120 else: |
298 l.extend(sender_receivers[signal]) |
121 lookup_key = (_make_id(receiver), _make_id(sender)) |
299 if signal is not Any and Any in sender_receivers: |
122 |
300 l.extend(sender_receivers[Any]) |
123 for idx, (r_key, _) in enumerate(self.receivers): |
301 |
124 if r_key == lookup_key: |
302 for receiver in l: |
125 del self.receivers[idx] |
303 try: |
126 |
304 if not receiver in receivers: |
127 def send(self, sender, **named): |
|
128 """Send signal from sender to all connected receivers. |
|
129 |
|
130 sender -- the sender of the signal |
|
131 Either a specific object or None. |
|
132 |
|
133 named -- named arguments which will be passed to receivers. |
|
134 |
|
135 Returns a list of tuple pairs [(receiver, response), ... ]. |
|
136 |
|
137 If any receiver raises an error, the error propagates back |
|
138 through send, terminating the dispatch loop, so it is quite |
|
139 possible to not have all receivers called if a raises an |
|
140 error. |
|
141 """ |
|
142 |
|
143 responses = [] |
|
144 if not self.receivers: |
|
145 return responses |
|
146 |
|
147 for receiver in self._live_receivers(_make_id(sender)): |
|
148 response = receiver(signal=self, sender=sender, **named) |
|
149 responses.append((receiver, response)) |
|
150 return responses |
|
151 |
|
152 def send_robust(self, sender, **named): |
|
153 """Send signal from sender to all connected receivers catching errors |
|
154 |
|
155 sender -- the sender of the signal |
|
156 Can be any python object (normally one registered with |
|
157 a connect if you actually want something to occur). |
|
158 |
|
159 named -- named arguments which will be passed to receivers. |
|
160 These arguments must be a subset of the argument names |
|
161 defined in providing_args. |
|
162 |
|
163 Return a list of tuple pairs [(receiver, response), ... ], |
|
164 may raise DispatcherKeyError |
|
165 |
|
166 if any receiver raises an error (specifically any subclass of Exception), |
|
167 the error instance is returned as the result for that receiver. |
|
168 """ |
|
169 |
|
170 responses = [] |
|
171 if not self.receivers: |
|
172 return responses |
|
173 |
|
174 # Call each receiver with whatever arguments it can accept. |
|
175 # Return a list of tuple pairs [(receiver, response), ... ]. |
|
176 for receiver in self._live_receivers(_make_id(sender)): |
|
177 try: |
|
178 response = receiver(signal=self, sender=sender, **named) |
|
179 except Exception, err: |
|
180 responses.append((receiver, err)) |
|
181 else: |
|
182 responses.append((receiver, response)) |
|
183 return responses |
|
184 |
|
185 def _live_receivers(self, senderkey): |
|
186 """Filter sequence of receivers to get resolved, live receivers |
|
187 |
|
188 This checks for weak references |
|
189 and resolves them, then returning only live |
|
190 receivers. |
|
191 """ |
|
192 none_senderkey = _make_id(None) |
|
193 |
|
194 for (receiverkey, r_senderkey), receiver in self.receivers: |
|
195 if r_senderkey == none_senderkey or r_senderkey == senderkey: |
305 if isinstance(receiver, WEAKREF_TYPES): |
196 if isinstance(receiver, WEAKREF_TYPES): |
|
197 # Dereference the weak reference. |
306 receiver = receiver() |
198 receiver = receiver() |
307 # this should only (rough guess) be possible if somehow, deref'ing |
199 if receiver is not None: |
308 # triggered a wipe. |
200 yield receiver |
309 if receiver is None: |
|
310 continue |
|
311 receivers[receiver] = 1 |
|
312 yield receiver |
|
313 except TypeError: |
|
314 # dead weakrefs raise TypeError on hash... |
|
315 pass |
|
316 |
|
317 def send(signal=Any, sender=Anonymous, *arguments, **named): |
|
318 """Send signal from sender to all connected receivers. |
|
319 |
|
320 signal -- (hashable) signal value, see connect for details |
|
321 |
|
322 sender -- the sender of the signal |
|
323 |
|
324 if Any, only receivers registered for Any will receive |
|
325 the message. |
|
326 |
|
327 if Anonymous, only receivers registered to receive |
|
328 messages from Anonymous or Any will receive the message |
|
329 |
|
330 Otherwise can be any python object (normally one |
|
331 registered with a connect if you actually want |
|
332 something to occur). |
|
333 |
|
334 arguments -- positional arguments which will be passed to |
|
335 *all* receivers. Note that this may raise TypeErrors |
|
336 if the receivers do not allow the particular arguments. |
|
337 Note also that arguments are applied before named |
|
338 arguments, so they should be used with care. |
|
339 |
|
340 named -- named arguments which will be filtered according |
|
341 to the parameters of the receivers to only provide those |
|
342 acceptable to the receiver. |
|
343 |
|
344 Return a list of tuple pairs [(receiver, response), ... ] |
|
345 |
|
346 if any receiver raises an error, the error propagates back |
|
347 through send, terminating the dispatch loop, so it is quite |
|
348 possible to not have all receivers called if a raises an |
|
349 error. |
|
350 """ |
|
351 # Call each receiver with whatever arguments it can accept. |
|
352 # Return a list of tuple pairs [(receiver, response), ... ]. |
|
353 responses = [] |
|
354 for receiver in getAllReceivers(sender, signal): |
|
355 response = robustapply.robustApply( |
|
356 receiver, |
|
357 signal=signal, |
|
358 sender=sender, |
|
359 *arguments, |
|
360 **named |
|
361 ) |
|
362 responses.append((receiver, response)) |
|
363 return responses |
|
364 |
|
365 |
|
366 def sendExact( signal=Any, sender=Anonymous, *arguments, **named ): |
|
367 """Send signal only to those receivers registered for exact message |
|
368 |
|
369 sendExact allows for avoiding Any/Anonymous registered |
|
370 handlers, sending only to those receivers explicitly |
|
371 registered for a particular signal on a particular |
|
372 sender. |
|
373 """ |
|
374 responses = [] |
|
375 for receiver in liveReceivers(getReceivers(sender, signal)): |
|
376 response = robustapply.robustApply( |
|
377 receiver, |
|
378 signal=signal, |
|
379 sender=sender, |
|
380 *arguments, |
|
381 **named |
|
382 ) |
|
383 responses.append((receiver, response)) |
|
384 return responses |
|
385 |
|
386 |
|
387 def _removeReceiver(receiver): |
|
388 """Remove receiver from connections.""" |
|
389 if not sendersBack: |
|
390 # During module cleanup the mapping will be replaced with None |
|
391 return False |
|
392 backKey = id(receiver) |
|
393 for senderkey in sendersBack.get(backKey,()): |
|
394 try: |
|
395 signals = connections[senderkey].keys() |
|
396 except KeyError,err: |
|
397 pass |
|
398 else: |
|
399 for signal in signals: |
|
400 try: |
|
401 receivers = connections[senderkey][signal] |
|
402 except KeyError: |
|
403 pass |
|
404 else: |
201 else: |
405 try: |
202 yield receiver |
406 receivers.remove( receiver ) |
203 |
407 except Exception, err: |
204 def _remove_receiver(self, receiver): |
408 pass |
205 """Remove dead receivers from connections.""" |
409 _cleanupConnections(senderkey, signal) |
206 |
410 try: |
207 to_remove = [] |
411 del sendersBack[ backKey ] |
208 for key, connected_receiver in self.receivers: |
412 except KeyError: |
209 if connected_receiver == receiver: |
413 pass |
210 to_remove.append(key) |
414 |
211 for key in to_remove: |
415 def _cleanupConnections(senderkey, signal): |
212 for idx, (r_key, _) in enumerate(self.receivers): |
416 """Delete any empty signals for senderkey. Delete senderkey if empty.""" |
213 if r_key == key: |
417 try: |
214 del self.receivers[idx] |
418 receivers = connections[senderkey][signal] |
|
419 except: |
|
420 pass |
|
421 else: |
|
422 if not receivers: |
|
423 # No more connected receivers. Therefore, remove the signal. |
|
424 try: |
|
425 signals = connections[senderkey] |
|
426 except KeyError: |
|
427 pass |
|
428 else: |
|
429 del signals[signal] |
|
430 if not signals: |
|
431 # No more signal connections. Therefore, remove the sender. |
|
432 _removeSender(senderkey) |
|
433 |
|
434 def _removeSender(senderkey): |
|
435 """Remove senderkey from connections.""" |
|
436 _removeBackrefs(senderkey) |
|
437 |
|
438 connections.pop(senderkey, None) |
|
439 senders.pop(senderkey, None) |
|
440 |
|
441 |
|
442 def _removeBackrefs( senderkey): |
|
443 """Remove all back-references to this senderkey""" |
|
444 for receiver_list in connections.pop(senderkey, {}).values(): |
|
445 for receiver in receiver_list: |
|
446 _killBackref( receiver, senderkey ) |
|
447 |
|
448 |
|
449 def _removeOldBackRefs(senderkey, signal, receiver, receivers): |
|
450 """Kill old sendersBack references from receiver |
|
451 |
|
452 This guards against multiple registration of the same |
|
453 receiver for a given signal and sender leaking memory |
|
454 as old back reference records build up. |
|
455 |
|
456 Also removes old receiver instance from receivers |
|
457 """ |
|
458 try: |
|
459 index = receivers.index(receiver) |
|
460 # need to scan back references here and remove senderkey |
|
461 except ValueError: |
|
462 return False |
|
463 else: |
|
464 oldReceiver = receivers[index] |
|
465 del receivers[index] |
|
466 found = 0 |
|
467 signals = connections.get(signal) |
|
468 if signals is not None: |
|
469 for sig,recs in connections.get(signal,{}).iteritems(): |
|
470 if sig != signal: |
|
471 for rec in recs: |
|
472 if rec is oldReceiver: |
|
473 found = 1 |
|
474 break |
|
475 if not found: |
|
476 _killBackref( oldReceiver, senderkey ) |
|
477 return True |
|
478 return False |
|
479 |
|
480 |
|
481 def _killBackref( receiver, senderkey ): |
|
482 """Do the actual removal of back reference from receiver to senderkey""" |
|
483 receiverkey = id(receiver) |
|
484 receivers_list = sendersBack.get( receiverkey, () ) |
|
485 while senderkey in receivers_list: |
|
486 try: |
|
487 receivers_list.remove( senderkey ) |
|
488 except: |
|
489 break |
|
490 if not receivers_list: |
|
491 try: |
|
492 del sendersBack[ receiverkey ] |
|
493 except KeyError: |
|
494 pass |
|
495 return True |
|