Ticket #990: updated-990.diff

File updated-990.diff, 12.6 KB (added by glyph, 9 years ago)
  • twisted/test/test_defer.py

     
    110110        dl = defer.DeferredList([], fireOnOneCallback=1) 
    111111        dl.addCallbacks(cb) 
    112112        self.failUnlessEqual(result, []) 
    113          
     113 
    114114    def testDeferredListFireOnOneError(self): 
    115115        defr1 = defer.Deferred() 
    116116        defr2 = defer.Deferred() 
     
    137137        failure = result[0] 
    138138 
    139139        # the type of the failure is a FirstError 
    140         self.failUnless(issubclass(failure.type, defer.FirstError),  
     140        self.failUnless(issubclass(failure.type, defer.FirstError), 
    141141            'issubclass(failure.type, defer.FirstError) failed: ' 
    142142            'failure.type is %r' % (failure.type,) 
    143143        ) 
     
    153153        self.failUnlessEqual(firstError.subFailure.value.args, ("from def2",)) 
    154154        self.failUnlessEqual(firstError.index, 1) 
    155155 
    156          
    157156 
     157 
    158158    def testDeferredListDontConsumeErrors(self): 
    159159        d1 = defer.Deferred() 
    160160        dl = defer.DeferredList([d1]) 
     
    473473        defer.setDebugging(True) 
    474474        d.addBoth(lambda ign: None) 
    475475 
     476class FooError(Exception): 
     477    pass 
    476478 
     479class DeferredCancellerTest(unittest.TestCase): 
     480    def setUp(self): 
     481        self.callback_results = None 
     482        self.errback_results = None 
     483        self.callback2_results = None 
     484        self.cancellerCalled = False 
     485 
     486    def _callback(self, data): 
     487        self.callback_results = data 
     488        return args[0] 
     489 
     490    def _callback2(self, data): 
     491        self.callback2_results = data 
     492 
     493    def _errback(self, data): 
     494        self.errback_results = data 
     495 
     496 
     497    def testNoCanceller(self): 
     498        # Deferred without a canceller errbacks defer.CancelledError 
     499        d=defer.Deferred() 
     500        d.addCallbacks(self._callback, self._errback) 
     501        d.cancel() 
     502        self.assertEquals(self.errback_results.type, defer.CancelledError) 
     503 
     504        # Test that further callbacks *are* swallowed 
     505        d.callback(None) 
     506 
     507        # But that a second is not 
     508        self.assertRaises(defer.AlreadyCalledError, d.callback, None) 
     509 
     510    def testCanceller(self): 
     511        def cancel(d): 
     512            self.cancellerCalled=True 
     513 
     514        d=defer.Deferred(canceller=cancel) 
     515        d.addCallbacks(self._callback, self._errback) 
     516        d.cancel() 
     517        self.assertEquals(self.cancellerCalled, True) 
     518        self.assertEquals(self.errback_results.type, defer.CancelledError) 
     519 
     520        # Test that further callbacks are *not* swallowed 
     521        self.assertRaises(defer.AlreadyCalledError, d.callback, None) 
     522 
     523    def testCancellerWithCallback(self): 
     524        # If we explicitly callback from the canceller, don't callback CancelledError 
     525        def cancel(d): 
     526            self.cancellerCalled=True 
     527            d.errback(FooError()) 
     528        d=defer.Deferred(canceller=cancel) 
     529        d.addCallbacks(self._callback, self._errback) 
     530        d.cancel() 
     531        self.assertEquals(self.cancellerCalled, True) 
     532        self.assertEquals(self.errback_results.type, FooError) 
     533 
     534    def testCancelAlreadyCalled(self): 
     535        def cancel(d): 
     536            self.cancellerCalled=True 
     537        d=defer.Deferred(canceller=cancel) 
     538        d.callback(None) 
     539        self.assertRaises(defer.AlreadyCalledError, d.cancel) 
     540        self.assertEquals(self.cancellerCalled, False) 
     541 
     542    def testCancelNestedDeferred(self): 
     543        def innerCancel(d): 
     544            self.assertIdentical(d, innerDeferred) 
     545            self.cancellerCalled=True 
     546        def cancel(d): 
     547            self.assert_(False) 
     548 
     549        innerDeferred=defer.Deferred(canceller=innerCancel) 
     550        d=defer.Deferred(canceller=cancel) 
     551        d.callback(None) 
     552        d.addCallback(lambda data: innerDeferred) 
     553        d.cancel() 
     554        d.addCallbacks(self._callback, self._errback) 
     555        self.assertEquals(self.cancellerCalled, True) 
     556        self.assertEquals(self.errback_results.type, defer.CancelledError) 
     557 
    477558class LogTestCase(unittest.TestCase): 
    478559 
    479560    def setUp(self): 
     
    563644        self.failUnless(lock.locked) 
    564645        self.assertEquals(self.counter, 3) 
    565646 
     647        d = lock.acquire().addBoth(lambda x: setattr(self, 'result', x)) 
     648        d.cancel() 
     649        self.assertEquals(self.result.type, defer.CancelledError) 
     650 
    566651        lock.release() 
    567652        self.failIf(lock.locked) 
    568653 
     
    591676            sem.acquire().addCallback(self._incr) 
    592677            self.assertEquals(self.counter, i) 
    593678 
     679 
     680        success = [] 
     681        def fail(r): 
     682            success.append(False) 
     683        def succeed(r): 
     684            success.append(True) 
     685        d = sem.acquire().addCallbacks(fail, succeed) 
     686        d.cancel() 
     687        self.assertEquals(success, [True]) 
     688 
    594689        sem.acquire().addCallback(self._incr) 
    595690        self.assertEquals(self.counter, N) 
    596691 
     
    638733        queue = defer.DeferredQueue(backlog=0) 
    639734        self.assertRaises(defer.QueueUnderflow, queue.get) 
    640735 
     736        queue = defer.DeferredQueue(size=0) 
     737 
     738        success = [] 
     739        def fail(r): 
     740            success.append(False) 
     741        def succeed(r): 
     742            success.append(True) 
     743        d = queue.get().addCallbacks(fail, succeed) 
     744        d.cancel() 
     745        self.assertEquals(success, [True]) 
     746        self.assertRaises(defer.QueueOverflow, queue.put, None) 
  • twisted/internet/defer.py

     
    2222class AlreadyCalledError(Exception): 
    2323    pass 
    2424 
    25 class TimeoutError(Exception): 
     25class CancelledError(Exception): 
    2626    pass 
     27# Backwards compatibility 
     28TimeoutError = CancelledError 
    2729 
    2830def logError(err): 
    2931    log.err(err) 
     
    117119    return deferred 
    118120 
    119121def timeout(deferred): 
    120     deferred.errback(failure.Failure(TimeoutError("Callback timed out"))) 
     122    deferred.cancel() 
    121123 
    122124def passthru(arg): 
    123125    return arg 
     
    149151 
    150152    For more information about Deferreds, see doc/howto/defer.html or 
    151153    U{http://twistedmatrix.com/projects/core/documentation/howto/defer.html} 
     154 
     155    When creating a Deferred, you should provide a canceller function, which 
     156    will be called by d.cancel() to let you do any cleanup necessary if the 
     157    user decides not to wait for the deferred to complete. 
    152158    """ 
     159 
    153160    called = 0 
    154161    paused = 0 
    155162    timeoutCall = None 
    156163    _debugInfo = None 
     164    _suppressAlreadyCalled = 0 
    157165 
    158166    # Keep this class attribute for now, for compatibility with code that 
    159167    # sets it directly. 
    160168    debug = False 
    161169 
    162     def __init__(self): 
     170    def __init__(self, canceller=None): 
    163171        self.callbacks = [] 
     172        self.canceller = canceller 
    164173        if self.debug: 
    165174            self._debugInfo = DebugInfo() 
    166175            self._debugInfo.creator = traceback.format_stack()[:-1] 
     
    177186        cbs = ((callback, callbackArgs, callbackKeywords), 
    178187               (errback or (passthru), errbackArgs, errbackKeywords)) 
    179188        self.callbacks.append(cbs) 
    180              
     189 
    181190        if self.called: 
    182191            self._runCallbacks() 
    183192        return self 
     
    253262 
    254263    def pause(self): 
    255264        """Stop processing on a Deferred until L{unpause}() is called. 
     265        You probably don't ever have a reason to call this function. 
    256266        """ 
    257267        self.paused = self.paused + 1 
    258268 
    259269 
    260270    def unpause(self): 
    261271        """Process all callbacks made since L{pause}() was called. 
     272        You probably don't ever have a reason to call this function. 
    262273        """ 
    263274        self.paused = self.paused - 1 
    264275        if self.paused: 
     
    266277        if self.called: 
    267278            self._runCallbacks() 
    268279 
     280    def cancel(self): 
     281        """Cancel this deferred. 
     282 
     283        If the deferred is waiting on another deferred, forward the 
     284        cancellation to the other deferred. 
     285 
     286        If the deferred has not yet been errback'd/callback'd, call 
     287        the canceller function provided to the constructor. If that 
     288        function does not do a callback/errback, or if no canceller 
     289        function was provided, errback with CancelledError. 
     290 
     291        Otherwise, raise AlreadyCalledError. 
     292        """ 
     293        canceller = self.canceller 
     294        if not self.called: 
     295            if canceller: 
     296                canceller(self) 
     297            else: 
     298                # Eat the callback that will eventually be fired 
     299                # since there was no real canceller. 
     300                self._suppressAlreadyCalled = 1 
     301 
     302            if not self.called: 
     303                # The canceller didn't do an errback of its own 
     304                try: 
     305                    raise CancelledError 
     306                except: 
     307                    self.errback(failure.Failure()) 
     308        elif isinstance(self.result, Deferred): 
     309            # Waiting for another deferred -- cancel it instead 
     310            self.result.cancel() 
     311        else: 
     312            # Called and not waiting for another deferred 
     313            raise AlreadyCalledError 
     314 
    269315    def _continue(self, result): 
    270316        self.result = result 
    271317        self.unpause() 
    272318 
    273319    def _startRunCallbacks(self, result): 
     320        # Canceller is no longer relevant 
     321        self.canceller = None 
     322 
    274323        if self.called: 
     324            if self._suppressAlreadyCalled: 
     325                self._suppressAlreadyCalled = 0 
     326                return 
    275327            if self.debug: 
    276328                if self._debugInfo is None: 
    277329                    self._debugInfo = DebugInfo() 
     
    337389 
    338390        @param timeoutFunc: will receive the Deferred and *args, **kw as its 
    339391        arguments.  The default timeoutFunc will call the errback with a 
    340         L{TimeoutError}. 
     392        L{CancelledError}. 
     393 
    341394        """ 
    342395        warnings.warn( 
    343396            "Deferred.setTimeout is deprecated.  Look for timeout " 
     
    393446 
    394447class FirstError(Exception): 
    395448    """First error to occur in a DeferredList if fireOnOneErrback is set. 
    396      
     449 
    397450    @ivar subFailure: the L{Failure} that occurred. 
    398451    @ivar index: the index of the Deferred in the DeferredList where it 
    399452    happened. 
     
    424477        if isinstance(other, tuple): 
    425478            return tuple(self) == other 
    426479        elif isinstance(other, FirstError): 
    427             return (self.subFailure == other.subFailure and  
     480            return (self.subFailure == other.subFailure and 
    428481                    self.index == other.index) 
    429482        return False 
    430483 
     
    705758 
    706759    locked = 0 
    707760 
     761    def _cancelAcquire(self, d): 
     762        self.waiting.remove(d) 
     763 
    708764    def acquire(self): 
    709765        """Attempt to acquire the lock. 
    710766 
    711767        @return: a Deferred which fires on lock acquisition. 
    712768        """ 
    713         d = Deferred() 
     769        d = Deferred(canceller=self._cancelAcquire) 
    714770        if self.locked: 
    715771            self.waiting.append(d) 
    716772        else: 
     
    743799        self.tokens = tokens 
    744800        self.limit = tokens 
    745801 
     802    def _cancelAcquire(self, d): 
     803        self.waiting.remove(d) 
     804 
    746805    def acquire(self): 
    747806        """Attempt to acquire the token. 
    748807 
    749808        @return: a Deferred which fires on token acquisition. 
    750809        """ 
    751810        assert self.tokens >= 0, "Internal inconsistency??  tokens should never be negative" 
    752         d = Deferred() 
     811        d = Deferred(canceller=self._cancelAcquire) 
    753812        if not self.tokens: 
    754813            self.waiting.append(d) 
    755814        else: 
     
    803862        self.size = size 
    804863        self.backlog = backlog 
    805864 
     865    def _cancelGet(self, d): 
     866        self.waiting.remove(d) 
     867 
    806868    def put(self, obj): 
    807869        """Add an object to this queue. 
    808870 
     
    826888        if self.pending: 
    827889            return succeed(self.pending.pop(0)) 
    828890        elif self.backlog is None or len(self.waiting) < self.backlog: 
    829             d = Deferred() 
     891            d = Deferred(canceller=self._cancelGet) 
    830892            self.waiting.append(d) 
    831893            return d 
    832894        else: 
     
    834896 
    835897 
    836898__all__ = ["Deferred", "DeferredList", "succeed", "fail", "FAILURE", "SUCCESS", 
    837            "AlreadyCalledError", "TimeoutError", "gatherResults", 
     899           "AlreadyCalledError", "TimeoutError", "CancelledError", "gatherResults", 
    838900           "maybeDeferred", "waitForDeferred", "deferredGenerator", 
    839901           "DeferredLock", "DeferredSemaphore", "DeferredQueue", 
    840902          ] 
  • twisted/trial/test/test_deferred.py

     
    219219        self.failIf(result.wasSuccessful()) 
    220220        self.failUnlessEqual(len(result.errors), 1) 
    221221        self._wasTimeout(result.errors[0][1]) 
     222        self.failUnless(result.errors[0][1].check(defer.CancelledError))