Author: jacob
Date: 2009-05-20 11:19:24 -0500 (Wed, 20 May 2009)
New Revision: 10832

Added:
   django/branches/releases/1.0.X/tests/modeltests/signals/tests.py
Modified:
   django/branches/releases/1.0.X/django/dispatch/dispatcher.py
Log:
[1.0.X] Fixed #11134: signals recievers that disconnect during their processing 
no longer mess things up for other handlers. Thanks, Honza Kral. Backport of 
[10831] from trunk.

Modified: django/branches/releases/1.0.X/django/dispatch/dispatcher.py
===================================================================
--- django/branches/releases/1.0.X/django/dispatch/dispatcher.py        
2009-05-20 16:13:55 UTC (rev 10831)
+++ django/branches/releases/1.0.X/django/dispatch/dispatcher.py        
2009-05-20 16:19:24 UTC (rev 10832)
@@ -14,52 +14,61 @@
     return id(target)
 
 class Signal(object):
-    """Base class for all signals
+    """
+    Base class for all signals
     
     Internal attributes:
-        receivers -- { receriverkey (id) : weakref(receiver) }
+    
+        receivers
+            { receriverkey (id) : weakref(receiver) }
     """
     
     def __init__(self, providing_args=None):
-        """providing_args -- A list of the arguments this signal can pass 
along in
-                       a send() call.
         """
+        Create a new signal.
+        
+        providing_args
+            A list of the arguments this signal can pass along in a send() 
call.
+        """
         self.receivers = []
         if providing_args is None:
             providing_args = []
         self.providing_args = set(providing_args)
 
     def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
-        """Connect receiver to sender for signal
+        """
+        Connect receiver to sender for signal.
     
-        receiver -- a function or an instance method which is to
-            receive signals.  Receivers must be
-            hashable objects.
+        Arguments:
+        
+            receiver
+                A function or an instance method which is to receive signals.
+                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).
+                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 must be able to accept keyword arguments.
+                Receivers must be able to accept keyword arguments.
 
-            If receivers have a dispatch_uid attribute, the receiver will
-              not be added if another receiver already exists with that
-              dispatch_uid.
+                If receivers have a dispatch_uid attribute, the receiver will
+                not be added if another receiver already exists with that
+                dispatch_uid.
 
-        sender -- the sender to which the receiver should respond
-            Must either be of type Signal, or None to receive events
-            from any sender.
+            sender
+                The sender to which the receiver should respond Must either be
+                of type Signal, or None to receive events from any sender.
 
-        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.
+            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.
         
-        dispatch_uid -- an identifier used to uniquely identify a particular
-            instance of a receiver. This will usually be a string, though it
-            may be anything hashable.
-
-        returns None
+            dispatch_uid
+                An identifier used to uniquely identify a particular instance 
of
+                a receiver. This will usually be a string, though it may be
+                anything hashable.
         """
         from django.conf import settings
         
@@ -99,22 +108,27 @@
             self.receivers.append((lookup_key, receiver))
 
     def disconnect(self, receiver=None, sender=None, weak=True, 
dispatch_uid=None):
-        """Disconnect receiver from sender for signal
+        """
+        Disconnect receiver from sender for signal.
+
+        If weak references are used, disconnect need not be called. The 
receiver
+        will be remove from dispatch automatically.
     
-        receiver -- the registered receiver to disconnect. May be none if
-            dispatch_uid is specified.
-        sender -- the registered sender to disconnect
-        weak -- the weakref state to disconnect
-        dispatch_uid -- the unique identifier of the receiver to disconnect
-    
-        disconnect reverses the process of connect.
-
-        If weak references are used, disconnect need not be called.
-          The receiver will be remove from dispatch automatically.
-
-        returns None
+        Arguments:
+        
+            receiver
+                The registered receiver to disconnect. May be none if
+                dispatch_uid is specified.
+            
+            sender
+                The registered sender to disconnect
+            
+            weak
+                The weakref state to disconnect
+            
+            dispatch_uid
+                the unique identifier of the receiver to disconnect
         """
-
         if dispatch_uid:
             lookup_key = (dispatch_uid, _make_id(sender))
         else:
@@ -127,21 +141,23 @@
                 break
 
     def send(self, sender, **named):
-        """Send signal from sender to all connected receivers.
+        """
+        Send signal from sender to all connected receivers.
 
-        sender -- the sender of the signal
-            Either a specific object or None.
+        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.
+
+        Arguments:
+        
+            sender
+                The sender of the signal Either a specific object or None.
     
-        named -- named arguments which will be passed to receivers.
+            named
+                Named arguments which will be passed to receivers.
 
         Returns 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.
         """
-
         responses = []
         if not self.receivers:
             return responses
@@ -152,23 +168,28 @@
         return responses
 
     def send_robust(self, sender, **named):
-        """Send signal from sender to all connected receivers catching errors
+        """
+        Send signal from sender to all connected receivers catching errors.
 
-        sender -- the sender of the signal
-            Can be any python object (normally one registered with
-            a connect if you actually want something to occur).
+        Arguments:
+        
+            sender
+                The sender of the signal Can be any python object (normally one
+                registered with a connect if you actually want something to
+                occur).
 
-        named -- named arguments which will be passed to receivers.
-            These arguments must be a subset of the argument names
-            defined in providing_args.
+            named
+                Named arguments which will be passed to receivers. These
+                arguments must be a subset of the argument names defined in
+                providing_args.
 
-        Return a list of tuple pairs [(receiver, response), ... ],
-        may raise DispatcherKeyError
+        Return a list of tuple pairs [(receiver, response), ... ]. May raise
+        DispatcherKeyError.
 
-        if any receiver raises an error (specifically any subclass of 
Exception),
-        the error instance is returned as the result for that receiver.
+        if any receiver raises an error (specifically any subclass of
+        Exception), the error instance is returned as the result for that
+        receiver.
         """
-
         responses = []
         if not self.receivers:
             return responses
@@ -185,13 +206,14 @@
         return responses
 
     def _live_receivers(self, senderkey):
-        """Filter sequence of receivers to get resolved, live receivers
+        """
+        Filter sequence of receivers to get resolved, live receivers.
 
-        This checks for weak references
-        and resolves them, then returning only live
-        receivers.
+        This checks for weak references and resolves them, then returning only
+        live receivers.
         """
         none_senderkey = _make_id(None)
+        receivers = []
 
         for (receiverkey, r_senderkey), receiver in self.receivers:
             if r_senderkey == none_senderkey or r_senderkey == senderkey:
@@ -199,12 +221,15 @@
                     # Dereference the weak reference.
                     receiver = receiver()
                     if receiver is not None:
-                        yield receiver
+                        receivers.append(receiver)
                 else:
-                    yield receiver
+                    receivers.append(receiver)
+        return receivers
 
     def _remove_receiver(self, receiver):
-        """Remove dead receivers from connections."""
+        """
+        Remove dead receivers from connections.
+        """
 
         to_remove = []
         for key, connected_receiver in self.receivers:

Added: django/branches/releases/1.0.X/tests/modeltests/signals/tests.py
===================================================================
--- django/branches/releases/1.0.X/tests/modeltests/signals/tests.py            
                (rev 0)
+++ django/branches/releases/1.0.X/tests/modeltests/signals/tests.py    
2009-05-20 16:19:24 UTC (rev 10832)
@@ -0,0 +1,28 @@
+from django.db.models import signals
+from django.test import TestCase
+from modeltests.signals.models import Person
+
+class MyReceiver(object):
+    def __init__(self, param):
+        self.param = param
+        self._run = False
+
+    def __call__(self, signal, sender, **kwargs):
+        self._run = True
+        signal.disconnect(receiver=self, sender=sender)
+
+class SignalTests(TestCase):
+    def test_disconnect_in_dispatch(self):
+        """
+        Test that signals that disconnect when being called don't mess future
+        dispatching.
+        """
+        a, b = MyReceiver(1), MyReceiver(2)
+        signals.post_save.connect(sender=Person, receiver=a)
+        signals.post_save.connect(sender=Person, receiver=b)
+        p = Person.objects.create(first_name='John', last_name='Smith')
+        
+        self.failUnless(a._run)
+        self.failUnless(b._run)
+        self.assertEqual(signals.post_save.receivers, [])
+        


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to