| 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 | |