Ticket #3439: dispatch.patch

File dispatch.patch, 17.8 KB (added by (removed), 9 years ago)

import of upstream tests, fix deregistration bug

  • tests/dispatchtests/__init__.py

    === added directory 'tests/dispatchtests'
    === added file 'tests/dispatchtests/__init__.py'
     
     1"""Unit-tests for the dispatch project
     2"""
  • tests/dispatchtests/tests/__init__.py

    === added directory 'tests/dispatchtests/tests'
    === added file 'tests/dispatchtests/tests/__init__.py'
     
     1"""Unit-tests for the dispatch project
     2"""
  • tests/dispatchtests/tests/test_dispatcher.py

    === added file 'tests/dispatchtests/tests/models.py'
    === added file 'tests/dispatchtests/tests/test_dispatcher.py'
     
     1from django.dispatch.dispatcher import *
     2from django.dispatch import dispatcher, robust
     3
     4import unittest, pprint, copy
     5
     6def x(a):
     7    return a
     8
     9class Dummy( object ):
     10    pass
     11
     12
     13class Callable(object):
     14
     15    def __call__( self, a ):
     16        return a
     17
     18    def a( self, a ):
     19        return a
     20
     21
     22class DispatcherTests(unittest.TestCase):
     23    """Test suite for dispatcher (barely started)"""
     24
     25    def setUp(self):
     26        # track the initial state, since it's possible that others have bleed receivers in
     27        self.sendersBack = copy.copy(dispatcher.sendersBack)
     28        self.connections = copy.copy(dispatcher.connections)
     29        self.senders = copy.copy(dispatcher.senders)
     30       
     31    def _isclean( self ):
     32        """Assert that everything has been cleaned up automatically"""
     33        assert dispatcher.sendersBack == self.sendersBack, dispatcher.sendersBack
     34        assert dispatcher.connections == self.connections, dispatcher.connections
     35        assert dispatcher.senders == self.senders, dispatcher.senders
     36   
     37    def testExact (self):
     38        a = Dummy()
     39        signal = 'this'
     40        connect( x, signal, a )
     41        expected = [(x,a)]
     42        result = send('this',a, a=a)
     43        assert result == expected,"""Send didn't return expected result:\n\texpected:%s\n\tgot:%s"""% (expected, result)
     44        disconnect( x, signal, a )
     45        assert len(list(getAllReceivers(a,signal))) == 0
     46        self._isclean()
     47
     48    def testAnonymousSend(self):
     49        a = Dummy()
     50        signal = 'this'
     51        connect( x, signal )
     52        expected = [(x,a)]
     53        result = send(signal,None, a=a)
     54        assert result == expected,"""Send didn't return expected result:\n\texpected:%s\n\tgot:%s"""% (expected, result)
     55        disconnect( x, signal )
     56        assert len(list(getAllReceivers(None,signal))) == 0
     57        self._isclean()
     58
     59    def testAnyRegistration(self):
     60        a = Dummy()
     61        signal = 'this'
     62        connect( x, signal, Any )
     63        expected = [(x,a)]
     64        result = send('this',object(), a=a)
     65        assert result == expected,"""Send didn't return expected result:\n\texpected:%s\n\tgot:%s"""% (expected, result)
     66        disconnect( x, signal, Any )
     67        expected = []
     68        result = send('this',object(), a=a)
     69        assert result == expected,"""Send didn't return expected result:\n\texpected:%s\n\tgot:%s"""% (expected, result)
     70        assert len(list(getAllReceivers(Any,signal))) == 0
     71
     72        self._isclean()
     73       
     74    def testAnyRegistration2(self):
     75        a = Dummy()
     76        signal = 'this'
     77        connect( x, Any, a )
     78        expected = [(x,a)]
     79        result = send('this',a, a=a)
     80        assert result == expected,"""Send didn't return expected result:\n\texpected:%s\n\tgot:%s"""% (expected, result)
     81        disconnect( x, Any, a )
     82        assert len(list(getAllReceivers(a,Any))) == 0
     83        self._isclean()
     84
     85    def testGarbageCollected(self):
     86        a = Callable()
     87        b = Dummy()
     88        signal = 'this'
     89        connect( a.a, signal, b )
     90        expected = []
     91        del a
     92        result = send('this',b, a=b)
     93        assert result == expected,"""Send didn't return expected result:\n\texpected:%s\n\tgot:%s"""% (expected, result)
     94        assert len(list(getAllReceivers(b,signal))) == 0, """Remaining handlers: %s"""%(getAllReceivers(b,signal),)
     95        self._isclean()
     96
     97    def testGarbageCollectedObj(self):
     98        class x:
     99            def __call__( self, a ):
     100                return a
     101        a = Callable()
     102        b = Dummy()
     103        signal = 'this'
     104        connect( a, signal, b )
     105        expected = []
     106        del a
     107        result = send('this',b, a=b)
     108        assert result == expected,"""Send didn't return expected result:\n\texpected:%s\n\tgot:%s"""% (expected, result)
     109        assert len(list(getAllReceivers(b,signal))) == 0, """Remaining handlers: %s"""%(getAllReceivers(b,signal),)
     110        self._isclean()
     111
     112
     113    def testMultipleRegistration(self):
     114        a = Callable()
     115        b = Dummy()
     116        signal = 'this'
     117        connect( a, signal, b )
     118        connect( a, signal, b )
     119        connect( a, signal, b )
     120        connect( a, signal, b )
     121        connect( a, signal, b )
     122        connect( a, signal, b )
     123        result = send('this',b, a=b)
     124        assert len( result ) == 1, result
     125        assert len(list(getAllReceivers(b,signal))) == 1, """Remaining handlers: %s"""%(getAllReceivers(b,signal),)
     126        del a
     127        del b
     128        del result
     129        self._isclean()
     130
     131    def testRobust( self ):
     132        """Test the sendRobust function"""
     133        def fails( ):
     134            raise ValueError( 'this' )
     135        a = object()
     136        signal = 'this'
     137        connect( fails, Any, a )
     138        result = robust.sendRobust('this',a, a=a)
     139        err = result[0][1]
     140        assert isinstance( err, ValueError )
     141        assert err.args == ('this',)
     142
     143def getSuite():
     144    return unittest.makeSuite(DispatcherTests,'test')
     145       
     146if __name__ == "__main__":
     147    unittest.main ()
  • tests/dispatchtests/tests/test_robustapply.py

    === added file 'tests/dispatchtests/tests/test_robustapply.py'
     
     1from django.dispatch.robustapply import *
     2
     3import unittest
     4def noArgument():
     5    pass
     6def oneArgument (blah):
     7    pass
     8def twoArgument(blah, other):
     9    pass
     10class TestCases( unittest.TestCase ):
     11    def test01( self ):
     12        robustApply(noArgument )
     13    def test02( self ):
     14        self.assertRaises( TypeError, robustApply, noArgument, "this" )
     15    def test03( self ):
     16        self.assertRaises( TypeError, robustApply, oneArgument )
     17    def test04( self ):
     18        """Raise error on duplication of a particular argument"""
     19        self.assertRaises( TypeError, robustApply, oneArgument, "this", blah = "that" )
     20
     21def getSuite():
     22    return unittest.makeSuite(TestCases,'test')
     23
     24
     25if __name__ == "__main__":
     26    unittest.main()
     27   
  • tests/dispatchtests/tests/test_saferef.py

    === added file 'tests/dispatchtests/tests/test_saferef.py'
     
     1from django.dispatch.saferef import *
     2
     3import unittest
     4class Test1( object):
     5    def x( self ):
     6        pass
     7def test2(obj):
     8    pass
     9class Test2( object ):
     10    def __call__( self, obj ):
     11        pass
     12class Tester (unittest.TestCase):
     13    def setUp (self):
     14        ts = []
     15        ss = []
     16        for x in xrange( 5000 ):
     17            t = Test1()
     18            ts.append( t)
     19            s = safeRef(t.x, self._closure )
     20            ss.append( s)
     21        ts.append( test2 )
     22        ss.append( safeRef(test2, self._closure) )
     23        for x in xrange( 30 ):
     24            t = Test2()
     25            ts.append( t)
     26            s = safeRef(t, self._closure )
     27            ss.append( s)
     28        self.ts = ts
     29        self.ss = ss
     30        self.closureCount = 0
     31    def tearDown( self ):
     32        del self.ts
     33        del self.ss
     34    def testIn(self):
     35        """Test the "in" operator for safe references (cmp)"""
     36        for t in self.ts[:50]:
     37            assert safeRef(t.x) in self.ss
     38    def testValid(self):
     39        """Test that the references are valid (return instance methods)"""
     40        for s in self.ss:
     41            assert s()
     42    def testShortCircuit (self):
     43        """Test that creation short-circuits to reuse existing references"""
     44        sd = {}
     45        for s in self.ss:
     46            sd[s] = 1
     47        for t in self.ts:
     48            if hasattr( t, 'x'):
     49                assert sd.has_key( safeRef(t.x))
     50            else:
     51                assert sd.has_key( safeRef(t))
     52    def testRepresentation (self):
     53        """Test that the reference object's representation works
     54
     55        XXX Doesn't currently check the results, just that no error
     56            is raised
     57        """
     58        repr( self.ss[-1] )
     59       
     60    def test(self):
     61        self.closureCount = 0
     62        wholeI = len(self.ts)
     63        for i in xrange( len(self.ts)-1, -1, -1):
     64            del self.ts[i]
     65            if wholeI-i != self.closureCount:
     66                """Unexpected number of items closed, expected %s, got %s closed"""%( wholeI-i,self.closureCount)
     67       
     68    def _closure(self, ref):
     69        """Dumb utility mechanism to increment deletion counter"""
     70        self.closureCount +=1
     71
     72def getSuite():
     73    return unittest.makeSuite(Tester,'test')
     74
     75if __name__ == "__main__":
     76    unittest.main ()
  • tests/dispatchtests/tests/tests.py

    === added file 'tests/dispatchtests/tests/tests.py'
     
     1# fugly, but works.
     2from test_dispatcher import *
     3from test_robustapply import *
     4from test_saferef import *
  • django/dispatch/dispatcher.py

    === modified file 'django/dispatch/dispatcher.py'
     
    2525        deletion, (considerably speeds up the cleanup process
    2626        vs. the original code.)
    2727"""
    28 from __future__ import generators
    2928import types, weakref
    3029from django.dispatch import saferef, robustapply, errors
    3130
     
    3332__cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $"
    3433__version__ = "$Revision: 1.9 $"[11:-2]
    3534
    36 try:
    37     True
    38 except NameError:
    39     True = 1==1
    40     False = 1==0
    4135
    4236class _Parameter:
    4337    """Used to represent default parameter values."""
     
    140134    if weak:
    141135        receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
    142136    senderkey = id(sender)
    143     if connections.has_key(senderkey):
    144         signals = connections[senderkey]
    145     else:
    146         connections[senderkey] = signals = {}
     137
     138    signals = connections.setdefault(senderkey, {})
     139
    147140    # Keep track of senders for cleanup.
    148141    # Is Anonymous something we want to clean up?
    149142    if sender not in (None, Anonymous, Any):
     
    251244    to retrieve the actual receiver objects as an iterable
    252245    object.
    253246    """
    254     try:
    255         return connections[id(sender)][signal]
    256     except KeyError:
    257         return []
     247    existing = connections.get(id(sender))
     248    if existing is not None:
     249        return existing.get(signal, [])
     250    return []
    258251
    259252def liveReceivers(receivers):
    260253    """Filter sequence of receivers to get resolved, live receivers
     
    278271def getAllReceivers( sender = Any, signal = Any ):
    279272    """Get list of all receivers from global tables
    280273
    281     This gets all receivers which should receive
     274    This gets all dereferenced receivers which should receive
    282275    the given signal from sender, each receiver should
    283276    be produced only once by the resulting generator
    284277    """
    285278    receivers = {}
    286     for set in (
    287         # Get receivers that receive *this* signal from *this* sender.
    288         getReceivers( sender, signal ),
    289         # Add receivers that receive *any* signal from *this* sender.
    290         getReceivers( sender, Any ),
    291         # Add receivers that receive *this* signal from *any* sender.
    292         getReceivers( Any, signal ),
    293         # Add receivers that receive *any* signal from *any* sender.
    294         getReceivers( Any, Any ),
    295     ):
    296         for receiver in set:
    297             if receiver: # filter out dead instance-method weakrefs
    298                 try:
    299                     if not receivers.has_key( receiver ):
    300                         receivers[receiver] = 1
    301                         yield receiver
    302                 except TypeError:
    303                     # dead weakrefs raise TypeError on hash...
    304                     pass
     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:
     303        try:
     304            if not receiver in receivers:
     305                if isinstance(receiver, WEAKREF_TYPES):
     306                    receiver = receiver()
     307                    # this should only (rough guess) be possible if somehow, deref'ing
     308                    # triggered a wipe.
     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
    305316
    306317def send(signal=Any, sender=Anonymous, *arguments, **named):
    307318    """Send signal from sender to all connected receivers.
     
    340351    # Call each receiver with whatever arguments it can accept.
    341352    # Return a list of tuple pairs [(receiver, response), ... ].
    342353    responses = []
    343     for receiver in liveReceivers(getAllReceivers(sender, signal)):
     354    for receiver in getAllReceivers(sender, signal):
    344355        response = robustapply.robustApply(
    345356            receiver,
    346357            signal=signal,
     
    350361        )
    351362        responses.append((receiver, response))
    352363    return responses
     364
     365
    353366def sendExact( signal=Any, sender=Anonymous, *arguments, **named ):
    354367    """Send signal only to those receivers registered for exact message
    355368
     
    421434def _removeSender(senderkey):
    422435    """Remove senderkey from connections."""
    423436    _removeBackrefs(senderkey)
    424     try:
    425         del connections[senderkey]
    426     except KeyError:
    427         pass
    428     # Senderkey will only be in senders dictionary if sender
    429     # could be weakly referenced.
    430     try:
    431         del senders[senderkey]
    432     except:
    433         pass
     437
     438    connections.pop(senderkey, None)
     439    senders.pop(senderkey, None)
    434440
    435441
    436442def _removeBackrefs( senderkey):
    437443    """Remove all back-references to this senderkey"""
    438     try:
    439         signals = connections[senderkey]
    440     except KeyError:
    441         signals = None
    442     else:
    443         items = signals.items()
    444         def allReceivers( ):
    445             for signal,set in items:
    446                 for item in set:
    447                     yield item
    448         for receiver in allReceivers():
     444    for receiver_list in connections.pop(senderkey, {}).values():
     445        for receiver in receiver_list:
    449446            _killBackref( receiver, senderkey )
    450447
     448
    451449def _removeOldBackRefs(senderkey, signal, receiver, receivers):
    452450    """Kill old sendersBack references from receiver
    453451
     
    483481def _killBackref( receiver, senderkey ):
    484482    """Do the actual removal of back reference from receiver to senderkey"""
    485483    receiverkey = id(receiver)
    486     set = sendersBack.get( receiverkey, () )
    487     while senderkey in set:
     484    receivers_list = sendersBack.get( receiverkey, () )
     485    while senderkey in receivers_list:
    488486        try:
    489             set.remove( senderkey )
     487            receivers_list.remove( senderkey )
    490488        except:
    491489            break
    492     if not set:
     490    if not receivers_list:
    493491        try:
    494492            del sendersBack[ receiverkey ]
    495493        except KeyError:
  • tests/runtests.py

    === modified file 'tests/runtests.py'
     
    33import os, sys, traceback
    44import unittest
    55
    6 MODEL_TESTS_DIR_NAME = 'modeltests'
    7 REGRESSION_TESTS_DIR_NAME = 'regressiontests'
    86TEST_DATABASE_NAME = 'django_test_db'
    97TEST_TEMPLATE_DIR = 'templates'
    108
    11 MODEL_TEST_DIR = os.path.join(os.path.dirname(__file__), MODEL_TESTS_DIR_NAME)
    12 REGRESSION_TEST_DIR = os.path.join(os.path.dirname(__file__), REGRESSION_TESTS_DIR_NAME)
     9test_subdirs = ('modeltests', 'regressiontests', 'dispatchtests')
     10
     11base_loc = os.path.dirname(__file__)
     12tests = dict([(x, os.path.join(base_loc, x)) for x in test_subdirs])
    1313
    1414ALWAYS_INSTALLED_APPS = [
    1515    'django.contrib.contenttypes',
     
    2424
    2525def get_test_models():
    2626    models = []
    27     for loc, dirpath in (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR), (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR):
     27    for loc, dirpath in tests.iteritems():
    2828        for f in os.listdir(dirpath):
    2929            if f.startswith('__init__') or f.startswith('.') or f.startswith('sql') or f.startswith('invalid'):
    3030                continue
     
    3333
    3434def get_invalid_models():
    3535    models = []
    36     for loc, dirpath in (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR), (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR):
     36    for loc, dirpath in tests.iteritems():
    3737        for f in os.listdir(dirpath):
    3838            if f.startswith('__init__') or f.startswith('.') or f.startswith('sql'):
    3939                continue
Back to Top