app/django/dispatch/dispatcher.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
     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