Ticket #990: updated-990.diff

File updated-990.diff, 12.6 KB (added by glyph, 11 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))