Ticket #9318: 0001-Propagate-message-to-parent-s-handler-sender-is-chil.patch

File 0001-Propagate-message-to-parent-s-handler-sender-is-chil.patch, 4.9 KB (added by Alexander Artemenko, 16 years ago)

"Virtual" behaviour for signal dispatcher.

  • django/dispatch/dispatcher.py

    From 9084cbe36f51fd8fb3f049ef45a36ea806385393 Mon Sep 17 00:00:00 2001
    From: Alexander Artemenko <svetlyak.40wt@gmail.com>
    Date: Sun, 5 Oct 2008 20:58:20 +0400
    Subject: [PATCH] Propagate message to parent's handler sender is child model.
    
    ---
     django/dispatch/dispatcher.py                      |   26 +++++++++++++++----
     .../dispatch/tests/test_dispatcher.py              |   24 ++++++++++++++++--
     2 files changed, 41 insertions(+), 9 deletions(-)
    
    diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
    index 74fdd20..b894b82 100644
    a b  
     1import inspect
    12import weakref
    23try:
    34    set
    def _make_id(target):  
    1314        return (id(target.im_self), id(target.im_func))
    1415    return id(target)
    1516
     17def _make_ids(target):
     18    '''This function tries to collect parents for
     19       classes, inherited from Model.
     20       Without it, sending signals with inherited
     21       model does as 'sender' does not call appropriate
     22       receiver.
     23    '''
     24    ids = [_make_id(target)]
     25
     26    if inspect.isclass(target) and getattr(target, '_meta', None) is not None:
     27        for parent, _ in target._meta.parents.iteritems():
     28            ids.append(_make_id(parent))
     29    return ids
     30
    1631class Signal(object):
    1732    """Base class for all signals
    1833   
    class Signal(object):  
    6580       
    6681        # If DEBUG is on, check that we got a good receiver
    6782        if settings.DEBUG:
    68             import inspect
    6983            assert callable(receiver), "Signal receivers must be callable."
    7084           
    7185            # Check for **kwargs
    class Signal(object):  
    144158        if not self.receivers:
    145159            return responses
    146160
    147         for receiver in self._live_receivers(_make_id(sender)):
     161        for receiver in self._live_receivers(_make_ids(sender)):
    148162            response = receiver(signal=self, sender=sender, **named)
    149163            responses.append((receiver, response))
    150164        return responses
    class Signal(object):  
    173187
    174188        # Call each receiver with whatever arguments it can accept.
    175189        # Return a list of tuple pairs [(receiver, response), ... ].
    176         for receiver in self._live_receivers(_make_id(sender)):
     190        for receiver in self._live_receivers(_make_ids(sender)):
    177191            try:
    178192                response = receiver(signal=self, sender=sender, **named)
    179193            except Exception, err:
    class Signal(object):  
    182196                responses.append((receiver, response))
    183197        return responses
    184198
    185     def _live_receivers(self, senderkey):
     199    def _live_receivers(self, senderkeys):
    186200        """Filter sequence of receivers to get resolved, live receivers
    187201
    188202        This checks for weak references
    189203        and resolves them, then returning only live
    190204        receivers.
    191205        """
    192         none_senderkey = _make_id(None)
     206        senderkeys.append(_make_id(None))
    193207
    194208        for (receiverkey, r_senderkey), receiver in self.receivers:
    195             if r_senderkey == none_senderkey or r_senderkey == senderkey:
     209            if r_senderkey in senderkeys:
    196210                if isinstance(receiver, WEAKREF_TYPES):
    197211                    # Dereference the weak reference.
    198212                    receiver = receiver()
  • tests/regressiontests/dispatch/tests/test_dispatcher.py

    diff --git a/tests/regressiontests/dispatch/tests/test_dispatcher.py b/tests/regressiontests/dispatch/tests/test_dispatcher.py
    index baaae9c..8c29731 100644
    a b  
     1from django.db import models
    12from django.dispatch import Signal
    23import unittest
    34import copy
    class Callable(object):  
    2627
    2728a_signal = Signal(providing_args=["val"])
    2829
     30class Parent(models.Model): pass
     31class Child(Parent): pass
     32
     33
    2934class DispatcherTests(unittest.TestCase):
    3035    """Test suite for dispatcher (barely started)"""
    3136
     37    def tearDown(self):
     38        a_signal.receivers = []
     39
    3240    def _testIsClean(self, signal):
    3341        """Assert that everything has been cleaned up automatically"""
    3442        self.assertEqual(signal.receivers, [])
    35 
    36         # force cleanup just in case
    37         signal.receivers = []
    3843   
    3944    def testExact(self):
    4045        a_signal.connect(receiver_1_arg, sender=self)
    class DispatcherTests(unittest.TestCase):  
    103108        a_signal.disconnect(fails)
    104109        self._testIsClean(a_signal)
    105110
     111    def testPropagateToParent(self):
     112        a_signal.connect(receiver_1_arg, Parent)
     113
     114        responses = dict(a_signal.send(sender=Child, val='test'))
     115        self.assertEqual(1, len(responses))
     116        self.assertEqual('test', responses[receiver_1_arg])
     117
     118    def testNotPropagateToChild(self):
     119        a_signal.connect(receiver_1_arg, Child)
     120
     121        responses = dict(a_signal.send(sender=Parent, val='test'))
     122        self.assertEqual(0, len(responses))
     123
    106124def getSuite():
    107125    return unittest.makeSuite(DispatcherTests,'test')
    108126
Back to Top