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 | | """ |
49 | | Any = _Any() |
50 | | |
51 | | class _Anonymous(_Parameter): |
52 | | """Singleton used to signal "Anonymous Sender" |
53 | | |
54 | | The Anonymous object is used to signal that the sender |
55 | | of a message is not specified (as distinct from being |
56 | | "any sender"). Registering callbacks for Anonymous |
57 | | will only receive messages sent without senders. Sending |
58 | | with anonymous will only send messages to those receivers |
59 | | registered for Any or Anonymous. |
60 | | |
61 | | Note: |
62 | | The default sender for connect is Any, while the |
63 | | default sender for send is Anonymous. This has |
64 | | the effect that if you do not specify any senders |
65 | | in either function then all messages are routed |
66 | | as though there was a single sender (Anonymous) |
67 | | being used everywhere. |
68 | | """ |
69 | | Anonymous = _Anonymous() |
70 | | |
89 | | Receivers are fairly flexible in their specification, |
90 | | as the machinery in the robustApply module takes care |
91 | | of most of the details regarding figuring out appropriate |
92 | | subsets of the sent arguments to apply to a given |
93 | | receiver. |
94 | | |
95 | | Note: |
96 | | if receiver is itself a weak reference (a callable), |
97 | | it will be de-referenced by the system's machinery, |
98 | | so *generally* weak references are not suitable as |
99 | | receivers, though some use might be found for the |
100 | | facility whereby a higher-level library passes in |
101 | | pre-weakrefed receiver references. |
102 | | |
103 | | signal -- the signal to which the receiver should respond |
| 26 | Internal attributes: |
| 27 | receivers -- { receriverkey (id) : (weakref(receiver), |
| 28 | acceptable_named_args, accepts_kwargs } |
| 29 | sendersBack -- { receiverkey (id) : [senderkey (id)...] } |
| 30 | used for cleaning up receiver references on receiver |
| 31 | deletion, (considerably speeds up the cleanup process |
| 32 | vs. the original code.) |
| 33 | """ |
105 | | if Any, receiver will receive any signal from the |
106 | | indicated sender (which might also be Any, but is not |
107 | | necessarily Any). |
108 | | |
109 | | Otherwise must be a hashable Python object other than |
110 | | None (DispatcherError raised on None). |
111 | | |
112 | | sender -- the sender to which the receiver should respond |
| 35 | def __init__(self, argnames=[]): |
| 36 | """argnames -- A list of the arguments this signal can pass along in |
| 37 | a send() call. |
| 38 | """ |
| 39 | self.receivers = {} |
| 40 | self.sendersBack = {} |
| 41 | self.argnames = set(argnames) |
| 42 | |
| 43 | def connect(self, receiver, sender=None, weak=True): |
| 44 | """Connect receiver to sender for signal |
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. |
| 54 | Receivers must be able to accept the named arguments |
| 55 | declared in argnames. |
| 56 | |
| 57 | Note: |
| 58 | if receiver is itself a weak reference (a callable), |
| 59 | it will be de-referenced by the system's machinery, |
| 60 | so *generally* weak references are not suitable as |
| 61 | receivers, though some use might be found for the |
| 62 | facility whereby a higher-level library passes in |
| 63 | pre-weakrefed receiver references. |
| 64 | |
| 65 | sender -- the sender to which the receiver should respond |
| 66 | Must either be of type Signal, or None to receive events |
| 67 | from any sender. |
| 68 | |
| 69 | weak -- whether to use weak references to the receiver |
| 70 | By default, the module will attempt to use weak |
| 71 | references to the receiver objects. If this parameter |
| 72 | is false, then strong references will be used. |
| 73 | |
| 74 | returns None |
| 75 | """ |
| 76 | receiverkey = id(receiver) |
| 77 | allowed_args = receiver.func_code.co_varnames |
| 78 | accepts_kwargs = receiver.func_code.co_flags & 8 |
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. |
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. |
| 96 | def disconnect(self, receiver, sender=None, weak=True): |
| 97 | """Disconnect receiver from sender for signal |
| 98 | |
| 99 | receiver -- the registered receiver to disconnect |
| 100 | sender -- the registered sender to disconnect |
| 101 | weak -- the weakref state to disconnect |
| 102 | |
| 103 | disconnect reverses the process of connect, |
| 104 | the semantics for the individual elements are |
| 105 | logically equivalent to a tuple of |
| 106 | (receiver, signal, sender, weak) used as a key |
| 107 | to be deleted from the internal routing tables. |
| 108 | (The actual process is slightly more complex |
| 109 | but the semantics are basically the same). |
| 110 | |
| 111 | Note: |
| 112 | Using disconnect is not required to cleanup |
| 113 | routing when an object is deleted, the framework |
| 114 | will remove routes for deleted objects |
| 115 | automatically. It's only necessary to disconnect |
| 116 | if you want to stop routing to a live object. |
| 117 | |
| 118 | returns None, may raise DispatcherKeyError |
| 119 | """ |
| 120 | receiverkey = id(receiver) |
| 121 | if weak: |
| 122 | receiver = saferef.safeRef(receiver) |
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: |
267 | | yield receiver |
268 | | |
269 | | |
270 | | |
271 | | def getAllReceivers( sender = Any, signal = Any ): |
272 | | """Get list of all receivers from global tables |
273 | | |
274 | | This gets all dereferenced receivers which should receive |
275 | | the given signal from sender, each receiver should |
276 | | be produced only once by the resulting generator |
277 | | """ |
278 | | receivers = {} |
279 | | # Get receivers that receive *this* signal from *this* sender. |
280 | | # Add receivers that receive *any* signal from *this* sender. |
281 | | # Add receivers that receive *this* signal from *any* sender. |
282 | | # Add receivers that receive *any* signal from *any* sender. |
283 | | l = [] |
284 | | i = id(sender) |
285 | | if i in connections: |
286 | | sender_receivers = connections[i] |
287 | | if signal in sender_receivers: |
288 | | l.extend(sender_receivers[signal]) |
289 | | if signal is not Any and Any in sender_receivers: |
290 | | l.extend(sender_receivers[Any]) |
291 | | |
292 | | if sender is not Any: |
293 | | i = id(Any) |
294 | | if i in connections: |
295 | | sender_receivers = connections[i] |
296 | | if sender_receivers is not None: |
297 | | if signal in sender_receivers: |
298 | | l.extend(sender_receivers[signal]) |
299 | | if signal is not Any and Any in sender_receivers: |
300 | | l.extend(sender_receivers[Any]) |
301 | | |
302 | | for receiver in l: |
| 124 | # remove the backref to the sender |
317 | | def send(signal=Any, sender=Anonymous, *arguments, **named): |
318 | | """Send signal from sender to all connected receivers. |
| 131 | # if there are no more backrefs left, clean up the receiver |
| 132 | try: |
| 133 | if not senderkeys: |
| 134 | del self.sendersBack[receiverkey] |
| 135 | del self.receivers[receiverkey] |
| 136 | except KeyError: |
| 137 | return False |
| 138 | |
| 139 | def send(self, sender=None, **named): |
| 140 | """Send signal from sender to all connected receivers. |
| 141 | |
| 142 | sender -- the sender of the signal |
| 143 | Can be any python object (normally one registered with |
| 144 | a connect if you actually want something to occur). |
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 |
| 165 | senderkey = id(sender) |
| 166 | none_senderkey = id(None) |
| 167 | |
| 168 | # Call each receiver. |
| 169 | # Return a list of tuple pairs [(receiver, response), ... ]. |
| 170 | responses = [] |
| 171 | for receiverkey, receiver in self.liveReceivers(): |
| 172 | senderkeys = self.sendersBack[receiverkey] |
| 173 | if none_senderkey in senderkeys or senderkey in senderkeys: |
| 174 | response = self._callReceiver( |
| 175 | receiver, |
| 176 | receiverkey, |
| 177 | sender=sender, |
| 178 | signal=self, |
| 179 | **named) |
| 180 | responses.append((receiver, response)) |
| 181 | return responses |
| 183 | def sendRobust(self, sender, **named): |
| 184 | """Send signal from sender to all connected receivers catching errors |
| 185 | |
| 186 | sender -- the sender of the signal |
| 187 | Can be any python object (normally one registered with |
| 188 | a connect if you actually want something to occur). |
| 189 | |
| 190 | named -- named arguments which will be passed to receivers. |
| 191 | These arguments must be a subset of the argument names |
| 192 | defined in argnames, or a DispatcherTypeError will be |
| 193 | raised. |
| 194 | |
| 195 | Return a list of tuple pairs [(receiver, response), ... ], |
| 196 | may raise DispatcherKeyError |
| 197 | |
| 198 | if any receiver raises an error (specifically any subclass of Exception), |
| 199 | the error instance is returned as the result for that receiver. |
| 200 | """ |
| 201 | # enforce the API |
| 202 | for arg in named.keys(): |
| 203 | if arg not in self.argnames: |
| 204 | raise DispatcherTypeError, "send got an unexpected keyword argument '%s'" % arg |
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: |
405 | | try: |
406 | | receivers.remove( receiver ) |
407 | | except Exception, err: |
408 | | pass |
409 | | _cleanupConnections(senderkey, signal) |
410 | | try: |
411 | | del sendersBack[ backKey ] |
412 | | except KeyError: |
413 | | pass |
414 | | |
415 | | def _cleanupConnections(senderkey, signal): |
416 | | """Delete any empty signals for senderkey. Delete senderkey if empty.""" |
417 | | try: |
418 | | receivers = connections[senderkey][signal] |
419 | | except: |
420 | | pass |
421 | | else: |
422 | | if not receivers: |
423 | | # No more connected receivers. Therefore, remove the signal. |
| 206 | # Call each receiver with whatever arguments it can accept. |
| 207 | # Return a list of tuple pairs [(receiver, response), ... ]. |
| 208 | responses = [] |
| 209 | for receiverkey, receiver in self.liveReceivers(): |
434 | | def _removeSender(senderkey): |
435 | | """Remove senderkey from connections.""" |
436 | | _removeBackrefs(senderkey) |
| 223 | def liveReceivers(self): |
| 224 | """Filter sequence of receivers to get resolved, live receivers |
| 225 | |
| 226 | This is a generator which will iterate over |
| 227 | the passed sequence, checking for weak references |
| 228 | and resolving them, then returning all live |
| 229 | receivers. |
| 230 | """ |
| 231 | for (receiverkey, receiver) in self.receivers.items(): |
| 232 | receiver = receiver[0] |
| 233 | if isinstance(receiver, WEAKREF_TYPES): |
| 234 | # Dereference the weak reference. |
| 235 | receiver = receiver() |
| 236 | if receiver is not None: |
| 237 | yield receiverkey, receiver |
| 238 | else: |
| 239 | yield receiverkey, receiver |
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 |
| 241 | def _callReceiver(self, receiver, receiverkey, **named): |
| 242 | receiver_info = self.receivers[receiverkey] |
| 243 | if not receiver_info[2]: |
| 244 | acceptable_args = receiver_info[1] |
| 245 | for arg in named.keys(): |
| 246 | if arg not in acceptable_args: |
| 247 | del named[arg] |
| 248 | return receiver(**named) |
495 | | return True |
| 266 | |
| 267 | def connect(receiver, signal, sender=None, weak=True): |
| 268 | """For backward compatibility only. See Signal.connect() |
| 269 | """ |
| 270 | return signal.connect(receiver, sender, weak) |
| 271 | |
| 272 | def disconnect(receiver, signal, sender=None, weak=True): |
| 273 | """For backward compatibility only. See Signal.disconnect() |
| 274 | """ |
| 275 | signal.disconnect(receiver, sender, weak) |
| 276 | |
| 277 | def send(signal, sender=None, **named): |
| 278 | """For backward compatibility only. See Signal.send() |
| 279 | """ |
| 280 | return signal.send(sender=sender, **named) |
| 281 | |
| 282 | def sendExact(signal, sender, **named ): |
| 283 | """This function is deprecated, as it now has the same |
| 284 | meaning as send. |
| 285 | """ |
| 286 | return signal.send(sender=sender, **named) |
| 287 | |
| 288 | No newline at end of file |