|
@@ -6,24 +6,24 @@ system.
|
|
|
|
|
|
Module attributes of note:
|
|
|
|
|
|
- Any -- Singleton used to signal either "Any Sender" or
|
|
|
- "Any Signal". See documentation of the _Any class.
|
|
|
- Anonymous -- Singleton used to signal "Anonymous Sender"
|
|
|
- See documentation of the _Anonymous class.
|
|
|
+ Any -- Singleton used to signal either "Any Sender" or
|
|
|
+ "Any Signal". See documentation of the _Any class.
|
|
|
+ Anonymous -- Singleton used to signal "Anonymous Sender"
|
|
|
+ See documentation of the _Anonymous class.
|
|
|
|
|
|
Internal attributes:
|
|
|
- WEAKREF_TYPES -- tuple of types/classes which represent
|
|
|
- weak references to receivers, and thus must be de-
|
|
|
- referenced on retrieval to retrieve the callable
|
|
|
- object
|
|
|
- connections -- { senderkey (id) : { signal : [receivers...]}}
|
|
|
- senders -- { senderkey (id) : weakref(sender) }
|
|
|
- used for cleaning up sender references on sender
|
|
|
- deletion
|
|
|
- sendersBack -- { receiverkey (id) : [senderkey (id)...] }
|
|
|
- used for cleaning up receiver references on receiver
|
|
|
- deletion, (considerably speeds up the cleanup process
|
|
|
- vs. the original code.)
|
|
|
+ WEAKREF_TYPES -- tuple of types/classes which represent
|
|
|
+ weak references to receivers, and thus must be de-
|
|
|
+ referenced on retrieval to retrieve the callable
|
|
|
+ object
|
|
|
+ connections -- { senderkey (id) : { signal : [receivers...]}}
|
|
|
+ senders -- { senderkey (id) : weakref(sender) }
|
|
|
+ used for cleaning up sender references on sender
|
|
|
+ deletion
|
|
|
+ sendersBack -- { receiverkey (id) : [senderkey (id)...] }
|
|
|
+ used for cleaning up receiver references on receiver
|
|
|
+ deletion, (considerably speeds up the cleanup process
|
|
|
+ vs. the original code.)
|
|
|
"""
|
|
|
from __future__ import generators
|
|
|
import weakref
|
|
@@ -33,39 +33,42 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
|
|
__cvsid__ = "Id: dispatcher.py,v 1.1 2010/03/30 15:45:55 mcfletch Exp"
|
|
|
__version__ = "Revision: 1.1"
|
|
|
|
|
|
+
|
|
|
class _Parameter:
|
|
|
- """Used to represent default parameter values."""
|
|
|
- def __repr__(self):
|
|
|
- return self.__class__.__name__
|
|
|
+ """Used to represent default parameter values."""
|
|
|
+ def __repr__(self):
|
|
|
+ return self.__class__.__name__
|
|
|
+
|
|
|
|
|
|
class _Any(_Parameter):
|
|
|
- """Singleton used to signal either "Any Sender" or "Any Signal"
|
|
|
+ """Singleton used to signal either "Any Sender" or "Any Signal"
|
|
|
|
|
|
- The Any object can be used with connect, disconnect,
|
|
|
- send, or sendExact to signal that the parameter given
|
|
|
- Any should react to all senders/signals, not just
|
|
|
- a particular sender/signal.
|
|
|
- """
|
|
|
+ The Any object can be used with connect, disconnect,
|
|
|
+ send, or sendExact to signal that the parameter given
|
|
|
+ Any should react to all senders/signals, not just
|
|
|
+ a particular sender/signal.
|
|
|
+ """
|
|
|
Any = _Any()
|
|
|
|
|
|
+
|
|
|
class _Anonymous(_Parameter):
|
|
|
- """Singleton used to signal "Anonymous Sender"
|
|
|
-
|
|
|
- The Anonymous object is used to signal that the sender
|
|
|
- of a message is not specified (as distinct from being
|
|
|
- "any sender"). Registering callbacks for Anonymous
|
|
|
- will only receive messages sent without senders. Sending
|
|
|
- with anonymous will only send messages to those receivers
|
|
|
- registered for Any or Anonymous.
|
|
|
-
|
|
|
- Note:
|
|
|
- The default sender for connect is Any, while the
|
|
|
- default sender for send is Anonymous. This has
|
|
|
- the effect that if you do not specify any senders
|
|
|
- in either function then all messages are routed
|
|
|
- as though there was a single sender (Anonymous)
|
|
|
- being used everywhere.
|
|
|
- """
|
|
|
+ """Singleton used to signal "Anonymous Sender"
|
|
|
+
|
|
|
+ The Anonymous object is used to signal that the sender
|
|
|
+ of a message is not specified (as distinct from being
|
|
|
+ "any sender"). Registering callbacks for Anonymous
|
|
|
+ will only receive messages sent without senders. Sending
|
|
|
+ with anonymous will only send messages to those receivers
|
|
|
+ registered for Any or Anonymous.
|
|
|
+
|
|
|
+ Note:
|
|
|
+ The default sender for connect is Any, while the
|
|
|
+ default sender for send is Anonymous. This has
|
|
|
+ the effect that if you do not specify any senders
|
|
|
+ in either function then all messages are routed
|
|
|
+ as though there was a single sender (Anonymous)
|
|
|
+ being used everywhere.
|
|
|
+ """
|
|
|
Anonymous = _Anonymous()
|
|
|
|
|
|
WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
|
|
@@ -76,419 +79,428 @@ sendersBack = {}
|
|
|
|
|
|
|
|
|
def connect(receiver, signal=Any, sender=Any, weak=True):
|
|
|
- """Connect receiver to sender for signal
|
|
|
-
|
|
|
- receiver -- a callable Python object which is to receive
|
|
|
- messages/signals/events. Receivers must be hashable
|
|
|
- objects.
|
|
|
-
|
|
|
- if weak is True, then receiver must be weak-referencable
|
|
|
- (more precisely saferef.safeRef() must be able to create
|
|
|
- a reference to the receiver).
|
|
|
-
|
|
|
- Receivers are fairly flexible in their specification,
|
|
|
- as the machinery in the robustApply module takes care
|
|
|
- of most of the details regarding figuring out appropriate
|
|
|
- subsets of the sent arguments to apply to a given
|
|
|
- receiver.
|
|
|
-
|
|
|
- Note:
|
|
|
- if receiver is itself a weak reference (a callable),
|
|
|
- it will be de-referenced by the system's machinery,
|
|
|
- so *generally* weak references are not suitable as
|
|
|
- receivers, though some use might be found for the
|
|
|
- facility whereby a higher-level library passes in
|
|
|
- pre-weakrefed receiver references.
|
|
|
-
|
|
|
- signal -- the signal to which the receiver should respond
|
|
|
-
|
|
|
- if Any, receiver will receive any signal from the
|
|
|
- indicated sender (which might also be Any, but is not
|
|
|
- necessarily Any).
|
|
|
-
|
|
|
- Otherwise must be a hashable Python object other than
|
|
|
- None (DispatcherError raised on None).
|
|
|
-
|
|
|
- sender -- the sender to which the receiver should respond
|
|
|
-
|
|
|
- if Any, receiver will receive the indicated signals
|
|
|
- from any sender.
|
|
|
-
|
|
|
- if Anonymous, receiver will only receive indicated
|
|
|
- signals from send/sendExact which do not specify a
|
|
|
- sender, or specify Anonymous explicitly as the sender.
|
|
|
-
|
|
|
- Otherwise can be any python object.
|
|
|
-
|
|
|
- weak -- whether to use weak references to the receiver
|
|
|
- By default, the module will attempt to use weak
|
|
|
- references to the receiver objects. If this parameter
|
|
|
- is false, then strong references will be used.
|
|
|
-
|
|
|
- returns None, may raise DispatcherTypeError
|
|
|
- """
|
|
|
- if signal is None:
|
|
|
- raise errors.DispatcherTypeError(
|
|
|
- 'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
|
|
- )
|
|
|
- if weak:
|
|
|
- receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
|
|
|
- senderkey = id(sender)
|
|
|
- if senderkey in connections:
|
|
|
- signals = connections[senderkey]
|
|
|
- else:
|
|
|
- connections[senderkey] = signals = {}
|
|
|
- # Keep track of senders for cleanup.
|
|
|
- # Is Anonymous something we want to clean up?
|
|
|
- if sender not in (None, Anonymous, Any):
|
|
|
- def remove(object, senderkey=senderkey):
|
|
|
- _removeSender(senderkey=senderkey)
|
|
|
- # Skip objects that can not be weakly referenced, which means
|
|
|
- # they won't be automatically cleaned up, but that's too bad.
|
|
|
- try:
|
|
|
- weakSender = weakref.ref(sender, remove)
|
|
|
- senders[senderkey] = weakSender
|
|
|
- except:
|
|
|
- pass
|
|
|
-
|
|
|
- receiverID = id(receiver)
|
|
|
- # get current set, remove any current references to
|
|
|
- # this receiver in the set, including back-references
|
|
|
- if signal in signals:
|
|
|
- receivers = signals[signal]
|
|
|
- _removeOldBackRefs(senderkey, signal, receiver, receivers)
|
|
|
- else:
|
|
|
- receivers = signals[signal] = []
|
|
|
- try:
|
|
|
- current = sendersBack.get( receiverID )
|
|
|
- if current is None:
|
|
|
- sendersBack[ receiverID ] = current = []
|
|
|
- if senderkey not in current:
|
|
|
- current.append(senderkey)
|
|
|
- except:
|
|
|
- pass
|
|
|
-
|
|
|
- receivers.append(receiver)
|
|
|
-
|
|
|
+ """Connect receiver to sender for signal
|
|
|
+
|
|
|
+ receiver -- a callable Python object which is to receive
|
|
|
+ messages/signals/events. Receivers must be hashable
|
|
|
+ objects.
|
|
|
+
|
|
|
+ if weak is True, then receiver must be weak-referencable
|
|
|
+ (more precisely saferef.safeRef() must be able to create
|
|
|
+ a reference to the receiver).
|
|
|
+
|
|
|
+ Receivers are fairly flexible in their specification,
|
|
|
+ as the machinery in the robustApply module takes care
|
|
|
+ of most of the details regarding figuring out appropriate
|
|
|
+ subsets of the sent arguments to apply to a given
|
|
|
+ receiver.
|
|
|
+
|
|
|
+ Note:
|
|
|
+ if receiver is itself a weak reference (a callable),
|
|
|
+ it will be de-referenced by the system's machinery,
|
|
|
+ so *generally* weak references are not suitable as
|
|
|
+ receivers, though some use might be found for the
|
|
|
+ facility whereby a higher-level library passes in
|
|
|
+ pre-weakrefed receiver references.
|
|
|
+
|
|
|
+ signal -- the signal to which the receiver should respond
|
|
|
+
|
|
|
+ if Any, receiver will receive any signal from the
|
|
|
+ indicated sender (which might also be Any, but is not
|
|
|
+ necessarily Any).
|
|
|
+
|
|
|
+ Otherwise must be a hashable Python object other than
|
|
|
+ None (DispatcherError raised on None).
|
|
|
+
|
|
|
+ sender -- the sender to which the receiver should respond
|
|
|
+
|
|
|
+ if Any, receiver will receive the indicated signals
|
|
|
+ from any sender.
|
|
|
+
|
|
|
+ if Anonymous, receiver will only receive indicated
|
|
|
+ signals from send/sendExact which do not specify a
|
|
|
+ sender, or specify Anonymous explicitly as the sender.
|
|
|
+
|
|
|
+ Otherwise can be any python object.
|
|
|
+
|
|
|
+ weak -- whether to use weak references to the receiver
|
|
|
+ By default, the module will attempt to use weak
|
|
|
+ references to the receiver objects. If this parameter
|
|
|
+ is false, then strong references will be used.
|
|
|
+
|
|
|
+ returns None, may raise DispatcherTypeError
|
|
|
+ """
|
|
|
+ if signal is None:
|
|
|
+ raise errors.DispatcherTypeError(
|
|
|
+ 'Signal cannot be None (receiver=%r sender=%r)' % (receiver,
|
|
|
+ sender)
|
|
|
+ )
|
|
|
+ if weak:
|
|
|
+ receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
|
|
|
+ senderkey = id(sender)
|
|
|
+ if senderkey in connections:
|
|
|
+ signals = connections[senderkey]
|
|
|
+ else:
|
|
|
+ connections[senderkey] = signals = {}
|
|
|
+ # Keep track of senders for cleanup.
|
|
|
+ # Is Anonymous something we want to clean up?
|
|
|
+ if sender not in (None, Anonymous, Any):
|
|
|
+ def remove(object, senderkey=senderkey):
|
|
|
+ _removeSender(senderkey=senderkey)
|
|
|
+ # Skip objects that can not be weakly referenced, which means
|
|
|
+ # they won't be automatically cleaned up, but that's too bad.
|
|
|
+ try:
|
|
|
+ weakSender = weakref.ref(sender, remove)
|
|
|
+ senders[senderkey] = weakSender
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ receiverID = id(receiver)
|
|
|
+ # get current set, remove any current references to
|
|
|
+ # this receiver in the set, including back-references
|
|
|
+ if signal in signals:
|
|
|
+ receivers = signals[signal]
|
|
|
+ _removeOldBackRefs(senderkey, signal, receiver, receivers)
|
|
|
+ else:
|
|
|
+ receivers = signals[signal] = []
|
|
|
+ try:
|
|
|
+ current = sendersBack.get(receiverID)
|
|
|
+ if current is None:
|
|
|
+ sendersBack[receiverID] = current = []
|
|
|
+ if senderkey not in current:
|
|
|
+ current.append(senderkey)
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ receivers.append(receiver)
|
|
|
|
|
|
|
|
|
def disconnect(receiver, signal=Any, sender=Any, weak=True):
|
|
|
- """Disconnect receiver from sender for signal
|
|
|
-
|
|
|
- receiver -- the registered receiver to disconnect
|
|
|
- signal -- the registered signal to disconnect
|
|
|
- sender -- the registered sender to disconnect
|
|
|
- weak -- the weakref state to disconnect
|
|
|
-
|
|
|
- disconnect reverses the process of connect,
|
|
|
- the semantics for the individual elements are
|
|
|
- logically equivalent to a tuple of
|
|
|
- (receiver, signal, sender, weak) used as a key
|
|
|
- to be deleted from the internal routing tables.
|
|
|
- (The actual process is slightly more complex
|
|
|
- but the semantics are basically the same).
|
|
|
-
|
|
|
- Note:
|
|
|
- Using disconnect is not required to cleanup
|
|
|
- routing when an object is deleted, the framework
|
|
|
- will remove routes for deleted objects
|
|
|
- automatically. It's only necessary to disconnect
|
|
|
- if you want to stop routing to a live object.
|
|
|
-
|
|
|
- returns None, may raise DispatcherTypeError or
|
|
|
- DispatcherKeyError
|
|
|
- """
|
|
|
- if signal is None:
|
|
|
- raise errors.DispatcherTypeError(
|
|
|
- 'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
|
|
- )
|
|
|
- if weak: receiver = saferef.safeRef(receiver)
|
|
|
- senderkey = id(sender)
|
|
|
- try:
|
|
|
- signals = connections[senderkey]
|
|
|
- receivers = signals[signal]
|
|
|
- except KeyError:
|
|
|
- raise errors.DispatcherKeyError(
|
|
|
- """No receivers found for signal %r from sender %r""" %(
|
|
|
- signal,
|
|
|
- sender
|
|
|
- )
|
|
|
- )
|
|
|
- try:
|
|
|
- # also removes from receivers
|
|
|
- _removeOldBackRefs(senderkey, signal, receiver, receivers)
|
|
|
- except ValueError:
|
|
|
- raise errors.DispatcherKeyError(
|
|
|
- """No connection to receiver %s for signal %s from sender %s""" %(
|
|
|
- receiver,
|
|
|
- signal,
|
|
|
- sender
|
|
|
- )
|
|
|
- )
|
|
|
- _cleanupConnections(senderkey, signal)
|
|
|
-
|
|
|
-def getReceivers( sender = Any, signal = Any ):
|
|
|
- """Get list of receivers from global tables
|
|
|
-
|
|
|
- This utility function allows you to retrieve the
|
|
|
- raw list of receivers from the connections table
|
|
|
- for the given sender and signal pair.
|
|
|
-
|
|
|
- Note:
|
|
|
- there is no guarantee that this is the actual list
|
|
|
- stored in the connections table, so the value
|
|
|
- should be treated as a simple iterable/truth value
|
|
|
- rather than, for instance a list to which you
|
|
|
- might append new records.
|
|
|
-
|
|
|
- Normally you would use liveReceivers( getReceivers( ...))
|
|
|
- to retrieve the actual receiver objects as an iterable
|
|
|
- object.
|
|
|
- """
|
|
|
- try:
|
|
|
- return connections[id(sender)][signal]
|
|
|
- except KeyError:
|
|
|
- return []
|
|
|
+ """Disconnect receiver from sender for signal
|
|
|
+
|
|
|
+ receiver -- the registered receiver to disconnect
|
|
|
+ signal -- the registered signal to disconnect
|
|
|
+ sender -- the registered sender to disconnect
|
|
|
+ weak -- the weakref state to disconnect
|
|
|
+
|
|
|
+ disconnect reverses the process of connect,
|
|
|
+ the semantics for the individual elements are
|
|
|
+ logically equivalent to a tuple of
|
|
|
+ (receiver, signal, sender, weak) used as a key
|
|
|
+ to be deleted from the internal routing tables.
|
|
|
+ (The actual process is slightly more complex
|
|
|
+ but the semantics are basically the same).
|
|
|
+
|
|
|
+ Note:
|
|
|
+ Using disconnect is not required to cleanup
|
|
|
+ routing when an object is deleted, the framework
|
|
|
+ will remove routes for deleted objects
|
|
|
+ automatically. It's only necessary to disconnect
|
|
|
+ if you want to stop routing to a live object.
|
|
|
+
|
|
|
+ returns None, may raise DispatcherTypeError or
|
|
|
+ DispatcherKeyError
|
|
|
+ """
|
|
|
+ if signal is None:
|
|
|
+ raise errors.DispatcherTypeError(
|
|
|
+ 'Signal cannot be None (receiver=%r sender=%r)' % (receiver,
|
|
|
+ sender)
|
|
|
+ )
|
|
|
+ if weak: receiver = saferef.safeRef(receiver)
|
|
|
+ senderkey = id(sender)
|
|
|
+ try:
|
|
|
+ signals = connections[senderkey]
|
|
|
+ receivers = signals[signal]
|
|
|
+ except KeyError:
|
|
|
+ raise errors.DispatcherKeyError(
|
|
|
+ """No receivers found for signal %r from sender %r""" % (
|
|
|
+ signal,
|
|
|
+ sender
|
|
|
+ )
|
|
|
+ )
|
|
|
+ try:
|
|
|
+ # also removes from receivers
|
|
|
+ _removeOldBackRefs(senderkey, signal, receiver, receivers)
|
|
|
+ except ValueError:
|
|
|
+ raise errors.DispatcherKeyError(
|
|
|
+ """No connection to receiver %s for signal %s from sender %s""" % (
|
|
|
+ receiver,
|
|
|
+ signal,
|
|
|
+ sender
|
|
|
+ )
|
|
|
+ )
|
|
|
+ _cleanupConnections(senderkey, signal)
|
|
|
+
|
|
|
+
|
|
|
+def getReceivers(sender=Any, signal=Any):
|
|
|
+ """Get list of receivers from global tables
|
|
|
+
|
|
|
+ This utility function allows you to retrieve the
|
|
|
+ raw list of receivers from the connections table
|
|
|
+ for the given sender and signal pair.
|
|
|
+
|
|
|
+ Note:
|
|
|
+ there is no guarantee that this is the actual list
|
|
|
+ stored in the connections table, so the value
|
|
|
+ should be treated as a simple iterable/truth value
|
|
|
+ rather than, for instance a list to which you
|
|
|
+ might append new records.
|
|
|
+
|
|
|
+ Normally you would use liveReceivers(getReceivers(...))
|
|
|
+ to retrieve the actual receiver objects as an iterable
|
|
|
+ object.
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ return connections[id(sender)][signal]
|
|
|
+ except KeyError:
|
|
|
+ return []
|
|
|
+
|
|
|
|
|
|
def liveReceivers(receivers):
|
|
|
- """Filter sequence of receivers to get resolved, live receivers
|
|
|
-
|
|
|
- This is a generator which will iterate over
|
|
|
- the passed sequence, checking for weak references
|
|
|
- and resolving them, then returning all live
|
|
|
- receivers.
|
|
|
- """
|
|
|
- for receiver in receivers:
|
|
|
- if isinstance( receiver, WEAKREF_TYPES):
|
|
|
- # Dereference the weak reference.
|
|
|
- receiver = receiver()
|
|
|
- if receiver is not None:
|
|
|
- yield receiver
|
|
|
- else:
|
|
|
- yield receiver
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-def getAllReceivers( sender = Any, signal = Any ):
|
|
|
- """Get list of all receivers from global tables
|
|
|
-
|
|
|
- This gets all receivers which should receive
|
|
|
- the given signal from sender, each receiver should
|
|
|
- be produced only once by the resulting generator
|
|
|
- """
|
|
|
- receivers = {}
|
|
|
- for set in (
|
|
|
- # Get receivers that receive *this* signal from *this* sender.
|
|
|
- getReceivers( sender, signal ),
|
|
|
- # Add receivers that receive *any* signal from *this* sender.
|
|
|
- getReceivers( sender, Any ),
|
|
|
- # Add receivers that receive *this* signal from *any* sender.
|
|
|
- getReceivers( Any, signal ),
|
|
|
- # Add receivers that receive *any* signal from *any* sender.
|
|
|
- getReceivers( Any, Any ),
|
|
|
- ):
|
|
|
- for receiver in set:
|
|
|
- if receiver: # filter out dead instance-method weakrefs
|
|
|
- try:
|
|
|
- if receiver not in receivers:
|
|
|
- receivers[receiver] = 1
|
|
|
- yield receiver
|
|
|
- except TypeError:
|
|
|
- # dead weakrefs raise TypeError on hash...
|
|
|
- pass
|
|
|
+ """Filter sequence of receivers to get resolved, live receivers
|
|
|
+
|
|
|
+ This is a generator which will iterate over
|
|
|
+ the passed sequence, checking for weak references
|
|
|
+ and resolving them, then returning all live
|
|
|
+ receivers.
|
|
|
+ """
|
|
|
+ for receiver in receivers:
|
|
|
+ if isinstance(receiver, WEAKREF_TYPES):
|
|
|
+ # Dereference the weak reference.
|
|
|
+ receiver = receiver()
|
|
|
+ if receiver is not None:
|
|
|
+ yield receiver
|
|
|
+ else:
|
|
|
+ yield receiver
|
|
|
+
|
|
|
+
|
|
|
+def getAllReceivers(sender=Any, signal=Any):
|
|
|
+ """Get list of all receivers from global tables
|
|
|
+
|
|
|
+ This gets all receivers which should receive
|
|
|
+ the given signal from sender, each receiver should
|
|
|
+ be produced only once by the resulting generator
|
|
|
+ """
|
|
|
+ receivers = {}
|
|
|
+ for set in (
|
|
|
+ # Get receivers that receive *this* signal from *this* sender.
|
|
|
+ getReceivers(sender, signal),
|
|
|
+ # Add receivers that receive *any* signal from *this* sender.
|
|
|
+ getReceivers(sender, Any),
|
|
|
+ # Add receivers that receive *this* signal from *any* sender.
|
|
|
+ getReceivers(Any, signal),
|
|
|
+ # Add receivers that receive *any* signal from *any* sender.
|
|
|
+ getReceivers(Any, Any),
|
|
|
+ ):
|
|
|
+ for receiver in set:
|
|
|
+ if receiver: # filter out dead instance-method weakrefs
|
|
|
+ try:
|
|
|
+ if receiver not in receivers:
|
|
|
+ receivers[receiver] = 1
|
|
|
+ yield receiver
|
|
|
+ except TypeError:
|
|
|
+ # dead weakrefs raise TypeError on hash...
|
|
|
+ pass
|
|
|
+
|
|
|
|
|
|
def send(signal=Any, sender=Anonymous, *arguments, **named):
|
|
|
- """Send signal from sender to all connected receivers.
|
|
|
-
|
|
|
- signal -- (hashable) signal value, see connect for details
|
|
|
-
|
|
|
- sender -- the sender of the signal
|
|
|
-
|
|
|
- if Any, only receivers registered for Any will receive
|
|
|
- the message.
|
|
|
-
|
|
|
- if Anonymous, only receivers registered to receive
|
|
|
- messages from Anonymous or Any will receive the message
|
|
|
-
|
|
|
- Otherwise can be any python object (normally one
|
|
|
- registered with a connect if you actually want
|
|
|
- something to occur).
|
|
|
-
|
|
|
- arguments -- positional arguments which will be passed to
|
|
|
- *all* receivers. Note that this may raise TypeErrors
|
|
|
- if the receivers do not allow the particular arguments.
|
|
|
- Note also that arguments are applied before named
|
|
|
- arguments, so they should be used with care.
|
|
|
-
|
|
|
- named -- named arguments which will be filtered according
|
|
|
- to the parameters of the receivers to only provide those
|
|
|
- acceptable to the receiver.
|
|
|
-
|
|
|
- Return a list of tuple pairs [(receiver, response), ... ]
|
|
|
-
|
|
|
- if any receiver raises an error, the error propagates back
|
|
|
- through send, terminating the dispatch loop, so it is quite
|
|
|
- possible to not have all receivers called if a raises an
|
|
|
- error.
|
|
|
- """
|
|
|
- # Call each receiver with whatever arguments it can accept.
|
|
|
- # Return a list of tuple pairs [(receiver, response), ... ].
|
|
|
- responses = []
|
|
|
- for receiver in liveReceivers(getAllReceivers(sender, signal)):
|
|
|
- response = robustapply.robustApply(
|
|
|
- receiver,
|
|
|
- signal=signal,
|
|
|
- sender=sender,
|
|
|
- *arguments,
|
|
|
- **named
|
|
|
- )
|
|
|
- responses.append((receiver, response))
|
|
|
- return responses
|
|
|
-def sendExact( signal=Any, sender=Anonymous, *arguments, **named ):
|
|
|
- """Send signal only to those receivers registered for exact message
|
|
|
-
|
|
|
- sendExact allows for avoiding Any/Anonymous registered
|
|
|
- handlers, sending only to those receivers explicitly
|
|
|
- registered for a particular signal on a particular
|
|
|
- sender.
|
|
|
- """
|
|
|
- responses = []
|
|
|
- for receiver in liveReceivers(getReceivers(sender, signal)):
|
|
|
- response = robustapply.robustApply(
|
|
|
- receiver,
|
|
|
- signal=signal,
|
|
|
- sender=sender,
|
|
|
- *arguments,
|
|
|
- **named
|
|
|
- )
|
|
|
- responses.append((receiver, response))
|
|
|
- return responses
|
|
|
-
|
|
|
+ """Send signal from sender to all connected receivers.
|
|
|
+
|
|
|
+ signal -- (hashable) signal value, see connect for details
|
|
|
+
|
|
|
+ sender -- the sender of the signal
|
|
|
+
|
|
|
+ if Any, only receivers registered for Any will receive
|
|
|
+ the message.
|
|
|
+
|
|
|
+ if Anonymous, only receivers registered to receive
|
|
|
+ messages from Anonymous or Any will receive the message
|
|
|
+
|
|
|
+ Otherwise can be any python object (normally one
|
|
|
+ registered with a connect if you actually want
|
|
|
+ something to occur).
|
|
|
+
|
|
|
+ arguments -- positional arguments which will be passed to
|
|
|
+ *all* receivers. Note that this may raise TypeErrors
|
|
|
+ if the receivers do not allow the particular arguments.
|
|
|
+ Note also that arguments are applied before named
|
|
|
+ arguments, so they should be used with care.
|
|
|
+
|
|
|
+ named -- named arguments which will be filtered according
|
|
|
+ to the parameters of the receivers to only provide those
|
|
|
+ acceptable to the receiver.
|
|
|
+
|
|
|
+ Return a list of tuple pairs [(receiver, response), ... ]
|
|
|
+
|
|
|
+ if any receiver raises an error, the error propagates back
|
|
|
+ through send, terminating the dispatch loop, so it is quite
|
|
|
+ possible to not have all receivers called if a raises an
|
|
|
+ error.
|
|
|
+ """
|
|
|
+ # Call each receiver with whatever arguments it can accept.
|
|
|
+ # Return a list of tuple pairs [(receiver, response), ... ].
|
|
|
+ responses = []
|
|
|
+ for receiver in liveReceivers(getAllReceivers(sender, signal)):
|
|
|
+ response = robustapply.robustApply(
|
|
|
+ receiver,
|
|
|
+ signal=signal,
|
|
|
+ sender=sender,
|
|
|
+ *arguments,
|
|
|
+ **named
|
|
|
+ )
|
|
|
+ responses.append((receiver, response))
|
|
|
+ return responses
|
|
|
+
|
|
|
+
|
|
|
+def sendExact(signal=Any, sender=Anonymous, *arguments, **named):
|
|
|
+ """Send signal only to those receivers registered for exact message
|
|
|
+
|
|
|
+ sendExact allows for avoiding Any/Anonymous registered
|
|
|
+ handlers, sending only to those receivers explicitly
|
|
|
+ registered for a particular signal on a particular
|
|
|
+ sender.
|
|
|
+ """
|
|
|
+ responses = []
|
|
|
+ for receiver in liveReceivers(getReceivers(sender, signal)):
|
|
|
+ response = robustapply.robustApply(
|
|
|
+ receiver,
|
|
|
+ signal=signal,
|
|
|
+ sender=sender,
|
|
|
+ *arguments,
|
|
|
+ **named
|
|
|
+ )
|
|
|
+ responses.append((receiver, response))
|
|
|
+ return responses
|
|
|
+
|
|
|
|
|
|
def _removeReceiver(receiver):
|
|
|
- """Remove receiver from connections."""
|
|
|
- if not sendersBack or not connections:
|
|
|
- # During module cleanup the objects will be replaced with None
|
|
|
+ """Remove receiver from connections."""
|
|
|
+ if not sendersBack or not connections:
|
|
|
+ # During module cleanup the objects will be replaced with None
|
|
|
# The order of replacing many chnage, so both variables need
|
|
|
# to be checked.
|
|
|
- return False
|
|
|
- backKey = id(receiver)
|
|
|
- try:
|
|
|
- backSet = sendersBack.pop(backKey)
|
|
|
- except KeyError:
|
|
|
- return False
|
|
|
- else:
|
|
|
- for senderkey in backSet:
|
|
|
- try:
|
|
|
- signals = connections[senderkey].keys()
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
- else:
|
|
|
- for signal in signals:
|
|
|
- try:
|
|
|
- receivers = connections[senderkey][signal]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
- else:
|
|
|
- try:
|
|
|
- receivers.remove( receiver )
|
|
|
- except Exception:
|
|
|
- pass
|
|
|
- _cleanupConnections(senderkey, signal)
|
|
|
+ return False
|
|
|
+ backKey = id(receiver)
|
|
|
+ try:
|
|
|
+ backSet = sendersBack.pop(backKey)
|
|
|
+ except KeyError:
|
|
|
+ return False
|
|
|
+ else:
|
|
|
+ for senderkey in backSet:
|
|
|
+ try:
|
|
|
+ signals = connections[senderkey].keys()
|
|
|
+ except KeyError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ for signal in signals:
|
|
|
+ try:
|
|
|
+ receivers = connections[senderkey][signal]
|
|
|
+ except KeyError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ try:
|
|
|
+ receivers.remove(receiver)
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+ _cleanupConnections(senderkey, signal)
|
|
|
+
|
|
|
|
|
|
def _cleanupConnections(senderkey, signal):
|
|
|
- """Delete any empty signals for senderkey. Delete senderkey if empty."""
|
|
|
- try:
|
|
|
- receivers = connections[senderkey][signal]
|
|
|
- except:
|
|
|
- pass
|
|
|
- else:
|
|
|
- if not receivers:
|
|
|
- # No more connected receivers. Therefore, remove the signal.
|
|
|
- try:
|
|
|
- signals = connections[senderkey]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
- else:
|
|
|
- del signals[signal]
|
|
|
- if not signals:
|
|
|
- # No more signal connections. Therefore, remove the sender.
|
|
|
- _removeSender(senderkey)
|
|
|
+ """Delete any empty signals for senderkey. Delete senderkey if empty."""
|
|
|
+ try:
|
|
|
+ receivers = connections[senderkey][signal]
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ if not receivers:
|
|
|
+ # No more connected receivers. Therefore, remove the signal.
|
|
|
+ try:
|
|
|
+ signals = connections[senderkey]
|
|
|
+ except KeyError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ del signals[signal]
|
|
|
+ if not signals:
|
|
|
+ # No more signal connections. Therefore, remove the sender.
|
|
|
+ _removeSender(senderkey)
|
|
|
+
|
|
|
|
|
|
def _removeSender(senderkey):
|
|
|
- """Remove senderkey from connections."""
|
|
|
- _removeBackrefs(senderkey)
|
|
|
- try:
|
|
|
- del connections[senderkey]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
- # Senderkey will only be in senders dictionary if sender
|
|
|
- # could be weakly referenced.
|
|
|
- try:
|
|
|
- del senders[senderkey]
|
|
|
- except:
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
-def _removeBackrefs( senderkey):
|
|
|
- """Remove all back-references to this senderkey"""
|
|
|
- try:
|
|
|
- signals = connections[senderkey]
|
|
|
- except KeyError:
|
|
|
- signals = None
|
|
|
- else:
|
|
|
- items = signals.items()
|
|
|
- def allReceivers( ):
|
|
|
- for signal,set in items:
|
|
|
- for item in set:
|
|
|
- yield item
|
|
|
- for receiver in allReceivers():
|
|
|
- _killBackref( receiver, senderkey )
|
|
|
+ """Remove senderkey from connections."""
|
|
|
+ _removeBackrefs(senderkey)
|
|
|
+ try:
|
|
|
+ del connections[senderkey]
|
|
|
+ except KeyError:
|
|
|
+ pass
|
|
|
+ # Senderkey will only be in senders dictionary if sender
|
|
|
+ # could be weakly referenced.
|
|
|
+ try:
|
|
|
+ del senders[senderkey]
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+def _removeBackrefs(senderkey):
|
|
|
+ """Remove all back-references to this senderkey"""
|
|
|
+ try:
|
|
|
+ signals = connections[senderkey]
|
|
|
+ except KeyError:
|
|
|
+ signals = None
|
|
|
+ else:
|
|
|
+ items = signals.items()
|
|
|
+
|
|
|
+ def allReceivers():
|
|
|
+ for signal, set in items:
|
|
|
+ for item in set:
|
|
|
+ yield item
|
|
|
+ for receiver in allReceivers():
|
|
|
+ _killBackref(receiver, senderkey)
|
|
|
+
|
|
|
|
|
|
def _removeOldBackRefs(senderkey, signal, receiver, receivers):
|
|
|
- """Kill old sendersBack references from receiver
|
|
|
-
|
|
|
- This guards against multiple registration of the same
|
|
|
- receiver for a given signal and sender leaking memory
|
|
|
- as old back reference records build up.
|
|
|
-
|
|
|
- Also removes old receiver instance from receivers
|
|
|
- """
|
|
|
- try:
|
|
|
- index = receivers.index(receiver)
|
|
|
- # need to scan back references here and remove senderkey
|
|
|
- except ValueError:
|
|
|
- return False
|
|
|
- else:
|
|
|
- oldReceiver = receivers[index]
|
|
|
- del receivers[index]
|
|
|
- found = 0
|
|
|
- signals = connections.get(signal)
|
|
|
- if signals is not None:
|
|
|
- for sig,recs in connections.get(signal,{}).iteritems():
|
|
|
- if sig != signal:
|
|
|
- for rec in recs:
|
|
|
- if rec is oldReceiver:
|
|
|
- found = 1
|
|
|
- break
|
|
|
- if not found:
|
|
|
- _killBackref( oldReceiver, senderkey )
|
|
|
- return True
|
|
|
- return False
|
|
|
-
|
|
|
-
|
|
|
-def _killBackref( receiver, senderkey ):
|
|
|
- """Do the actual removal of back reference from receiver to senderkey"""
|
|
|
- receiverkey = id(receiver)
|
|
|
- set = sendersBack.get( receiverkey, () )
|
|
|
- while senderkey in set:
|
|
|
- try:
|
|
|
- set.remove( senderkey )
|
|
|
- except:
|
|
|
- break
|
|
|
- if not set:
|
|
|
- try:
|
|
|
- del sendersBack[ receiverkey ]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
- return True
|
|
|
+ """Kill old sendersBack references from receiver
|
|
|
+
|
|
|
+ This guards against multiple registration of the same
|
|
|
+ receiver for a given signal and sender leaking memory
|
|
|
+ as old back reference records build up.
|
|
|
+
|
|
|
+ Also removes old receiver instance from receivers
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ index = receivers.index(receiver)
|
|
|
+ # need to scan back references here and remove senderkey
|
|
|
+ except ValueError:
|
|
|
+ return False
|
|
|
+ else:
|
|
|
+ oldReceiver = receivers[index]
|
|
|
+ del receivers[index]
|
|
|
+ found = 0
|
|
|
+ signals = connections.get(signal)
|
|
|
+ if signals is not None:
|
|
|
+ for sig, recs in connections.get(signal, {}).iteritems():
|
|
|
+ if sig != signal:
|
|
|
+ for rec in recs:
|
|
|
+ if rec is oldReceiver:
|
|
|
+ found = 1
|
|
|
+ break
|
|
|
+ if not found:
|
|
|
+ _killBackref(oldReceiver, senderkey)
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+def _killBackref(receiver, senderkey):
|
|
|
+ """Do the actual removal of back reference from receiver to senderkey"""
|
|
|
+ receiverkey = id(receiver)
|
|
|
+ set = sendersBack.get(receiverkey, ())
|
|
|
+ while senderkey in set:
|
|
|
+ try:
|
|
|
+ set.remove(senderkey)
|
|
|
+ except:
|
|
|
+ break
|
|
|
+ if not set:
|
|
|
+ try:
|
|
|
+ del sendersBack[receiverkey]
|
|
|
+ except KeyError:
|
|
|
+ pass
|
|
|
+ return True
|