Code

Ticket #3439: dispatch.patch

File dispatch.patch, 17.8 KB (added by (removed), 7 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