Ticket #3439: dispatch.patch
File dispatch.patch, 17.8 KB (added by , 18 years ago) |
---|
-
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'
1 from django.dispatch.dispatcher import * 2 from django.dispatch import dispatcher, robust 3 4 import unittest, pprint, copy 5 6 def x(a): 7 return a 8 9 class Dummy( object ): 10 pass 11 12 13 class Callable(object): 14 15 def __call__( self, a ): 16 return a 17 18 def a( self, a ): 19 return a 20 21 22 class 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 143 def getSuite(): 144 return unittest.makeSuite(DispatcherTests,'test') 145 146 if __name__ == "__main__": 147 unittest.main () -
tests/dispatchtests/tests/test_robustapply.py
=== added file 'tests/dispatchtests/tests/test_robustapply.py'
1 from django.dispatch.robustapply import * 2 3 import unittest 4 def noArgument(): 5 pass 6 def oneArgument (blah): 7 pass 8 def twoArgument(blah, other): 9 pass 10 class 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 21 def getSuite(): 22 return unittest.makeSuite(TestCases,'test') 23 24 25 if __name__ == "__main__": 26 unittest.main() 27 -
tests/dispatchtests/tests/test_saferef.py
=== added file 'tests/dispatchtests/tests/test_saferef.py'
1 from django.dispatch.saferef import * 2 3 import unittest 4 class Test1( object): 5 def x( self ): 6 pass 7 def test2(obj): 8 pass 9 class Test2( object ): 10 def __call__( self, obj ): 11 pass 12 class 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 72 def getSuite(): 73 return unittest.makeSuite(Tester,'test') 74 75 if __name__ == "__main__": 76 unittest.main () -
tests/dispatchtests/tests/tests.py
=== added file 'tests/dispatchtests/tests/tests.py'
1 # fugly, but works. 2 from test_dispatcher import * 3 from test_robustapply import * 4 from test_saferef import * -
django/dispatch/dispatcher.py
=== modified file 'django/dispatch/dispatcher.py'
25 25 deletion, (considerably speeds up the cleanup process 26 26 vs. the original code.) 27 27 """ 28 from __future__ import generators29 28 import types, weakref 30 29 from django.dispatch import saferef, robustapply, errors 31 30 … … 33 32 __cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $" 34 33 __version__ = "$Revision: 1.9 $"[11:-2] 35 34 36 try:37 True38 except NameError:39 True = 1==140 False = 1==041 35 42 36 class _Parameter: 43 37 """Used to represent default parameter values.""" … … 140 134 if weak: 141 135 receiver = saferef.safeRef(receiver, onDelete=_removeReceiver) 142 136 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 147 140 # Keep track of senders for cleanup. 148 141 # Is Anonymous something we want to clean up? 149 142 if sender not in (None, Anonymous, Any): … … 251 244 to retrieve the actual receiver objects as an iterable 252 245 object. 253 246 """ 254 try:255 return connections[id(sender)][signal]256 except KeyError:257 247 existing = connections.get(id(sender)) 248 if existing is not None: 249 return existing.get(signal, []) 250 return [] 258 251 259 252 def liveReceivers(receivers): 260 253 """Filter sequence of receivers to get resolved, live receivers … … 278 271 def getAllReceivers( sender = Any, signal = Any ): 279 272 """Get list of all receivers from global tables 280 273 281 This gets all receivers which should receive274 This gets all dereferenced receivers which should receive 282 275 the given signal from sender, each receiver should 283 276 be produced only once by the resulting generator 284 277 """ 285 278 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 305 316 306 317 def send(signal=Any, sender=Anonymous, *arguments, **named): 307 318 """Send signal from sender to all connected receivers. … … 340 351 # Call each receiver with whatever arguments it can accept. 341 352 # Return a list of tuple pairs [(receiver, response), ... ]. 342 353 responses = [] 343 for receiver in liveReceivers(getAllReceivers(sender, signal)):354 for receiver in getAllReceivers(sender, signal): 344 355 response = robustapply.robustApply( 345 356 receiver, 346 357 signal=signal, … … 350 361 ) 351 362 responses.append((receiver, response)) 352 363 return responses 364 365 353 366 def sendExact( signal=Any, sender=Anonymous, *arguments, **named ): 354 367 """Send signal only to those receivers registered for exact message 355 368 … … 421 434 def _removeSender(senderkey): 422 435 """Remove senderkey from connections.""" 423 436 _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) 434 440 435 441 436 442 def _removeBackrefs( senderkey): 437 443 """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: 449 446 _killBackref( receiver, senderkey ) 450 447 448 451 449 def _removeOldBackRefs(senderkey, signal, receiver, receivers): 452 450 """Kill old sendersBack references from receiver 453 451 … … 483 481 def _killBackref( receiver, senderkey ): 484 482 """Do the actual removal of back reference from receiver to senderkey""" 485 483 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: 488 486 try: 489 set.remove( senderkey )487 receivers_list.remove( senderkey ) 490 488 except: 491 489 break 492 if not set:490 if not receivers_list: 493 491 try: 494 492 del sendersBack[ receiverkey ] 495 493 except KeyError: -
tests/runtests.py
=== modified file 'tests/runtests.py'
3 3 import os, sys, traceback 4 4 import unittest 5 5 6 MODEL_TESTS_DIR_NAME = 'modeltests'7 REGRESSION_TESTS_DIR_NAME = 'regressiontests'8 6 TEST_DATABASE_NAME = 'django_test_db' 9 7 TEST_TEMPLATE_DIR = 'templates' 10 8 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) 9 test_subdirs = ('modeltests', 'regressiontests', 'dispatchtests') 10 11 base_loc = os.path.dirname(__file__) 12 tests = dict([(x, os.path.join(base_loc, x)) for x in test_subdirs]) 13 13 14 14 ALWAYS_INSTALLED_APPS = [ 15 15 'django.contrib.contenttypes', … … 24 24 25 25 def get_test_models(): 26 26 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(): 28 28 for f in os.listdir(dirpath): 29 29 if f.startswith('__init__') or f.startswith('.') or f.startswith('sql') or f.startswith('invalid'): 30 30 continue … … 33 33 34 34 def get_invalid_models(): 35 35 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(): 37 37 for f in os.listdir(dirpath): 38 38 if f.startswith('__init__') or f.startswith('.') or f.startswith('sql'): 39 39 continue