| 1 | === modified file 'twisted/internet/defer.py' |
|---|
| 2 | --- twisted/internet/defer.py 2010-02-21 23:19:24 +0000 |
|---|
| 3 | +++ twisted/internet/defer.py 2010-02-22 20:23:28 +0000 |
|---|
| 4 | @@ -180,8 +180,8 @@ |
|---|
| 5 | For more information about Deferreds, see doc/howto/defer.html or |
|---|
| 6 | U{http://twistedmatrix.com/projects/core/documentation/howto/defer.html} |
|---|
| 7 | |
|---|
| 8 | - When creating a Deferred, you should provide a canceller function, which |
|---|
| 9 | - will be called by d.cancel() to let you do any cleanup necessary if the |
|---|
| 10 | + When creating a Deferred, you may provide a canceller function, which |
|---|
| 11 | + will be called by d.cancel() to let you do any clean-up necessary if the |
|---|
| 12 | user decides not to wait for the deferred to complete. |
|---|
| 13 | """ |
|---|
| 14 | |
|---|
| 15 | @@ -204,20 +204,25 @@ |
|---|
| 16 | """ |
|---|
| 17 | Initialize a L{Deferred}. |
|---|
| 18 | |
|---|
| 19 | - @param canceller: an object to call in order to stop the pending |
|---|
| 20 | - operation scheduled by this L{Deferred} when L{Deferred.cancel} is |
|---|
| 21 | - invoked. If this callable does not invoke its argument's |
|---|
| 22 | - C{callback} or C{errback} method when it is called, |
|---|
| 23 | - L{Deferred.cancel} will invoke L{Deferred.errback} on its behalf. |
|---|
| 24 | - Note that if this is not supplied, C{callback} or C{errback} may |
|---|
| 25 | - still be invoked once, even if the default behavior of C{cancel} |
|---|
| 26 | - invokes C{errback} on your behalf. This is to allow the clients of |
|---|
| 27 | - code which returns a Deferred to to cancel it without the |
|---|
| 28 | - instantiator providing any specific implementation support for |
|---|
| 29 | - cancellation. New in 10.0. |
|---|
| 30 | - |
|---|
| 31 | - @type canceller: a 1-argument callable which takes a L{Deferred} and |
|---|
| 32 | - returns C{None} |
|---|
| 33 | + @param canceller: a callable used to stop the pending operation |
|---|
| 34 | + scheduled by this L{Deferred} when L{Deferred.cancel} is |
|---|
| 35 | + invoked. The canceller will be passed the deferred whose |
|---|
| 36 | + cancelation is requested (i.e., self). |
|---|
| 37 | + |
|---|
| 38 | + If a canceller is not given, or does not invoke its argument's |
|---|
| 39 | + C{callback} or C{errback} method, L{Deferred.cancel} will |
|---|
| 40 | + invoke L{Deferred.errback} with a L{CancelledError}. |
|---|
| 41 | + |
|---|
| 42 | + Note that if a canceller is not given, C{callback} or |
|---|
| 43 | + C{errback} may still be invoked exactly once, even though |
|---|
| 44 | + defer.py will have already invoked C{errback}, as described |
|---|
| 45 | + above. This allows clients of code which returns a L{Deferred} |
|---|
| 46 | + to cancel it without requiring the L{Deferred} instantiator to |
|---|
| 47 | + provide any specific implementation support for cancellation. |
|---|
| 48 | + New in 10.0. |
|---|
| 49 | + |
|---|
| 50 | + @type canceller: a 1-argument callable which takes a L{Deferred}. The |
|---|
| 51 | + return result is ignored. |
|---|
| 52 | """ |
|---|
| 53 | self.callbacks = [] |
|---|
| 54 | self._canceller = canceller |
|---|
| 55 | @@ -360,38 +365,29 @@ |
|---|
| 56 | """ |
|---|
| 57 | Cancel this L{Deferred}. |
|---|
| 58 | |
|---|
| 59 | - If this L{Deferred} is waiting on another L{Deferred}, forward the |
|---|
| 60 | - cancellation to the other L{Deferred}. |
|---|
| 61 | - |
|---|
| 62 | If the L{Deferred} has not yet had its C{errback} or C{callback} method |
|---|
| 63 | invoked, call the canceller function provided to the constructor. If |
|---|
| 64 | that function does not invoke C{callback} or C{errback}, or if no |
|---|
| 65 | canceller function was provided, errback with L{CancelledError}. |
|---|
| 66 | |
|---|
| 67 | - @raise AlreadyCalledError: if the L{Deferred} has previously had its |
|---|
| 68 | - C{callback} or C{errback} method invoked. |
|---|
| 69 | + If this L{Deferred} is waiting on another L{Deferred}, forward the |
|---|
| 70 | + cancellation to the other L{Deferred}. |
|---|
| 71 | """ |
|---|
| 72 | - canceller = self._canceller |
|---|
| 73 | if not self.called: |
|---|
| 74 | + canceller = self._canceller |
|---|
| 75 | if canceller: |
|---|
| 76 | canceller(self) |
|---|
| 77 | else: |
|---|
| 78 | - # Eat the callback that will eventually be fired |
|---|
| 79 | + # Arrange to eat the callback that will eventually be fired |
|---|
| 80 | # since there was no real canceller. |
|---|
| 81 | self._suppressAlreadyCalled = 1 |
|---|
| 82 | - |
|---|
| 83 | if not self.called: |
|---|
| 84 | - # The canceller didn't do an errback of its own |
|---|
| 85 | - try: |
|---|
| 86 | - raise CancelledError() |
|---|
| 87 | - except: |
|---|
| 88 | - self.errback(failure.Failure()) |
|---|
| 89 | + # There was no canceller, or the canceller didn't call |
|---|
| 90 | + # callback or errback. |
|---|
| 91 | + self.errback(failure.Failure(CancelledError())) |
|---|
| 92 | elif isinstance(self.result, Deferred): |
|---|
| 93 | - # Waiting for another deferred -- cancel it instead |
|---|
| 94 | + # Waiting for another deferred -- cancel it instead. |
|---|
| 95 | self.result.cancel() |
|---|
| 96 | - else: |
|---|
| 97 | - # Called and not waiting for another deferred |
|---|
| 98 | - raise AlreadyCalledError() |
|---|
| 99 | |
|---|
| 100 | |
|---|
| 101 | def _continue(self, result): |
|---|
| 102 | @@ -400,9 +396,6 @@ |
|---|
| 103 | |
|---|
| 104 | |
|---|
| 105 | def _startRunCallbacks(self, result): |
|---|
| 106 | - # Canceller is no longer relevant |
|---|
| 107 | - self.canceller = None |
|---|
| 108 | - |
|---|
| 109 | if self.called: |
|---|
| 110 | if self._suppressAlreadyCalled: |
|---|
| 111 | self._suppressAlreadyCalled = 0 |
|---|
| 112 | @@ -1078,6 +1071,17 @@ |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | def _cancelAcquire(self, d): |
|---|
| 116 | + """ |
|---|
| 117 | + Remove a deferred d from our waiting list, as the deferred has been |
|---|
| 118 | + canceled. |
|---|
| 119 | + |
|---|
| 120 | + Note: We do not need to wrap this in a try/except to catch d not |
|---|
| 121 | + being in self.waiting because this canceller will not be called if |
|---|
| 122 | + d has fired. release() pops a deferred out of self.waiting and |
|---|
| 123 | + calls it, so the canceller will no longer be called. |
|---|
| 124 | + |
|---|
| 125 | + @param d: The deferred that has been canceled. |
|---|
| 126 | + """ |
|---|
| 127 | self.waiting.remove(d) |
|---|
| 128 | |
|---|
| 129 | |
|---|
| 130 | @@ -1130,6 +1134,17 @@ |
|---|
| 131 | |
|---|
| 132 | |
|---|
| 133 | def _cancelAcquire(self, d): |
|---|
| 134 | + """ |
|---|
| 135 | + Remove a deferred d from our waiting list, as the deferred has been |
|---|
| 136 | + canceled. |
|---|
| 137 | + |
|---|
| 138 | + Note: We do not need to wrap this in a try/except to catch d not |
|---|
| 139 | + being in self.waiting because this canceller will not be called if |
|---|
| 140 | + d has fired. release() pops a deferred out of self.waiting and |
|---|
| 141 | + calls it, so the canceller will no longer be called. |
|---|
| 142 | + |
|---|
| 143 | + @param d: The deferred that has been canceled. |
|---|
| 144 | + """ |
|---|
| 145 | self.waiting.remove(d) |
|---|
| 146 | |
|---|
| 147 | |
|---|
| 148 | @@ -1202,6 +1217,17 @@ |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | def _cancelGet(self, d): |
|---|
| 152 | + """ |
|---|
| 153 | + Remove a deferred d from our waiting list, as the deferred has been |
|---|
| 154 | + canceled. |
|---|
| 155 | + |
|---|
| 156 | + Note: We do not need to wrap this in a try/except to catch d not |
|---|
| 157 | + being in self.waiting because this canceller will not be called if |
|---|
| 158 | + d has fired. put() pops a deferred out of self.waiting and calls |
|---|
| 159 | + it, so the canceller will no longer be called. |
|---|
| 160 | + |
|---|
| 161 | + @param d: The deferred that has been canceled. |
|---|
| 162 | + """ |
|---|
| 163 | self.waiting.remove(d) |
|---|
| 164 | |
|---|
| 165 | |
|---|
| 166 | |
|---|
| 167 | === modified file 'twisted/test/test_defer.py' |
|---|
| 168 | --- twisted/test/test_defer.py 2010-02-21 21:13:51 +0000 |
|---|
| 169 | +++ twisted/test/test_defer.py 2010-02-22 20:28:00 +0000 |
|---|
| 170 | @@ -28,40 +28,40 @@ |
|---|
| 171 | class DeferredTestCase(unittest.TestCase): |
|---|
| 172 | |
|---|
| 173 | def setUp(self): |
|---|
| 174 | - self.callback_results = None |
|---|
| 175 | - self.errback_results = None |
|---|
| 176 | - self.callback2_results = None |
|---|
| 177 | + self.callbackResults = None |
|---|
| 178 | + self.errbackResults = None |
|---|
| 179 | + self.callback2Results = None |
|---|
| 180 | |
|---|
| 181 | def _callback(self, *args, **kw): |
|---|
| 182 | - self.callback_results = args, kw |
|---|
| 183 | + self.callbackResults = args, kw |
|---|
| 184 | return args[0] |
|---|
| 185 | |
|---|
| 186 | def _callback2(self, *args, **kw): |
|---|
| 187 | - self.callback2_results = args, kw |
|---|
| 188 | + self.callback2Results = args, kw |
|---|
| 189 | |
|---|
| 190 | def _errback(self, *args, **kw): |
|---|
| 191 | - self.errback_results = args, kw |
|---|
| 192 | + self.errbackResults = args, kw |
|---|
| 193 | |
|---|
| 194 | def testCallbackWithoutArgs(self): |
|---|
| 195 | deferred = defer.Deferred() |
|---|
| 196 | deferred.addCallback(self._callback) |
|---|
| 197 | deferred.callback("hello") |
|---|
| 198 | - self.failUnlessEqual(self.errback_results, None) |
|---|
| 199 | - self.failUnlessEqual(self.callback_results, (('hello',), {})) |
|---|
| 200 | + self.failUnlessEqual(self.errbackResults, None) |
|---|
| 201 | + self.failUnlessEqual(self.callbackResults, (('hello',), {})) |
|---|
| 202 | |
|---|
| 203 | def testCallbackWithArgs(self): |
|---|
| 204 | deferred = defer.Deferred() |
|---|
| 205 | deferred.addCallback(self._callback, "world") |
|---|
| 206 | deferred.callback("hello") |
|---|
| 207 | - self.failUnlessEqual(self.errback_results, None) |
|---|
| 208 | - self.failUnlessEqual(self.callback_results, (('hello', 'world'), {})) |
|---|
| 209 | + self.failUnlessEqual(self.errbackResults, None) |
|---|
| 210 | + self.failUnlessEqual(self.callbackResults, (('hello', 'world'), {})) |
|---|
| 211 | |
|---|
| 212 | def testCallbackWithKwArgs(self): |
|---|
| 213 | deferred = defer.Deferred() |
|---|
| 214 | deferred.addCallback(self._callback, world="world") |
|---|
| 215 | deferred.callback("hello") |
|---|
| 216 | - self.failUnlessEqual(self.errback_results, None) |
|---|
| 217 | - self.failUnlessEqual(self.callback_results, |
|---|
| 218 | + self.failUnlessEqual(self.errbackResults, None) |
|---|
| 219 | + self.failUnlessEqual(self.callbackResults, |
|---|
| 220 | (('hello',), {'world': 'world'})) |
|---|
| 221 | |
|---|
| 222 | def testTwoCallbacks(self): |
|---|
| 223 | @@ -69,10 +69,10 @@ |
|---|
| 224 | deferred.addCallback(self._callback) |
|---|
| 225 | deferred.addCallback(self._callback2) |
|---|
| 226 | deferred.callback("hello") |
|---|
| 227 | - self.failUnlessEqual(self.errback_results, None) |
|---|
| 228 | - self.failUnlessEqual(self.callback_results, |
|---|
| 229 | + self.failUnlessEqual(self.errbackResults, None) |
|---|
| 230 | + self.failUnlessEqual(self.callbackResults, |
|---|
| 231 | (('hello',), {})) |
|---|
| 232 | - self.failUnlessEqual(self.callback2_results, |
|---|
| 233 | + self.failUnlessEqual(self.callback2Results, |
|---|
| 234 | (('hello',), {})) |
|---|
| 235 | |
|---|
| 236 | def testDeferredList(self): |
|---|
| 237 | @@ -295,11 +295,11 @@ |
|---|
| 238 | d.addCallback(lambda r, d2=d2: d2) |
|---|
| 239 | d.addCallback(self._callback) |
|---|
| 240 | d.callback(1) |
|---|
| 241 | - assert self.callback_results is None, "Should not have been called yet." |
|---|
| 242 | + assert self.callbackResults is None, "Should not have been called yet." |
|---|
| 243 | d2.callback(2) |
|---|
| 244 | - assert self.callback_results is None, "Still should not have been called yet." |
|---|
| 245 | + assert self.callbackResults is None, "Still should not have been called yet." |
|---|
| 246 | d2.unpause() |
|---|
| 247 | - assert self.callback_results[0][0] == 2, "Result should have been from second deferred:%s"% (self.callback_results,) |
|---|
| 248 | + assert self.callbackResults[0][0] == 2, "Result should have been from second deferred:%s"% (self.callbackResults,) |
|---|
| 249 | |
|---|
| 250 | def testGatherResults(self): |
|---|
| 251 | # test successful list of deferreds |
|---|
| 252 | @@ -640,98 +640,253 @@ |
|---|
| 253 | d.addBoth(lambda ign: None) |
|---|
| 254 | |
|---|
| 255 | |
|---|
| 256 | -class FooError(Exception): |
|---|
| 257 | - pass |
|---|
| 258 | |
|---|
| 259 | class DeferredCancellerTest(unittest.TestCase): |
|---|
| 260 | def setUp(self): |
|---|
| 261 | - self.callback_results = None |
|---|
| 262 | - self.errback_results = None |
|---|
| 263 | - self.callback2_results = None |
|---|
| 264 | - self.cancellerCalled = False |
|---|
| 265 | + self.callbackResults = None |
|---|
| 266 | + self.errbackResults = None |
|---|
| 267 | + self.callback2Results = None |
|---|
| 268 | + self.cancellerCallCount = 0 |
|---|
| 269 | + |
|---|
| 270 | + |
|---|
| 271 | + def tearDown(self): |
|---|
| 272 | + # Sanity check that the canceller was called at most once. |
|---|
| 273 | + self.assertTrue(self.cancellerCallCount in (0, 1)) |
|---|
| 274 | + |
|---|
| 275 | |
|---|
| 276 | def _callback(self, data): |
|---|
| 277 | - self.callback_results = data |
|---|
| 278 | + self.callbackResults = data |
|---|
| 279 | return data |
|---|
| 280 | |
|---|
| 281 | + |
|---|
| 282 | def _callback2(self, data): |
|---|
| 283 | - self.callback2_results = data |
|---|
| 284 | + self.callback2Results = data |
|---|
| 285 | + |
|---|
| 286 | |
|---|
| 287 | def _errback(self, data): |
|---|
| 288 | - self.errback_results = data |
|---|
| 289 | + self.errbackResults = data |
|---|
| 290 | |
|---|
| 291 | |
|---|
| 292 | def test_noCanceller(self): |
|---|
| 293 | """ |
|---|
| 294 | - Verify that a Deferred without a canceller errbacks with defer.CancelledError. |
|---|
| 295 | - """ |
|---|
| 296 | - d = defer.Deferred() |
|---|
| 297 | - d.addCallbacks(self._callback, self._errback) |
|---|
| 298 | - d.cancel() |
|---|
| 299 | - self.assertEquals(self.errback_results.type, defer.CancelledError) |
|---|
| 300 | - |
|---|
| 301 | - # Test that further callbacks *are* swallowed |
|---|
| 302 | - d.callback(None) |
|---|
| 303 | - |
|---|
| 304 | - # But that a second is not |
|---|
| 305 | - self.assertRaises(defer.AlreadyCalledError, d.callback, None) |
|---|
| 306 | - |
|---|
| 307 | - |
|---|
| 308 | - def test_canceller(self): |
|---|
| 309 | - """ |
|---|
| 310 | - Verify that a Deferred calls its specified canceller when it is |
|---|
| 311 | - cancelled. |
|---|
| 312 | - """ |
|---|
| 313 | - def cancel(d): |
|---|
| 314 | - self.cancellerCalled=True |
|---|
| 315 | - |
|---|
| 316 | - d = defer.Deferred(canceller=cancel) |
|---|
| 317 | - d.addCallbacks(self._callback, self._errback) |
|---|
| 318 | - d.cancel() |
|---|
| 319 | - self.assertEquals(self.cancellerCalled, True) |
|---|
| 320 | - self.assertEquals(self.errback_results.type, defer.CancelledError) |
|---|
| 321 | - |
|---|
| 322 | - # Test that further callbacks are *not* swallowed |
|---|
| 323 | - self.assertRaises(defer.AlreadyCalledError, d.callback, None) |
|---|
| 324 | - |
|---|
| 325 | - |
|---|
| 326 | - def test_cancellerWithCallback(self): |
|---|
| 327 | - """ |
|---|
| 328 | - Verify that a canceller which invokes a (non-errback) callback will not |
|---|
| 329 | - get errbacked with L{CancelledError} |
|---|
| 330 | - """ |
|---|
| 331 | - def cancel(d): |
|---|
| 332 | - self.cancellerCalled=True |
|---|
| 333 | - d.errback(FooError()) |
|---|
| 334 | - d = defer.Deferred(canceller=cancel) |
|---|
| 335 | - d.addCallbacks(self._callback, self._errback) |
|---|
| 336 | - d.cancel() |
|---|
| 337 | - self.assertEquals(self.cancellerCalled, True) |
|---|
| 338 | - self.assertEquals(self.errback_results.type, FooError) |
|---|
| 339 | - |
|---|
| 340 | - |
|---|
| 341 | - def test_cancelAlreadyCalled(self): |
|---|
| 342 | - """ |
|---|
| 343 | - Verify that cancelling a L{Deferred} twice will result in an |
|---|
| 344 | - L{AlreadyCalledError}. |
|---|
| 345 | - """ |
|---|
| 346 | - def cancel(d): |
|---|
| 347 | - self.cancellerCalled=True |
|---|
| 348 | - d = defer.Deferred(canceller=cancel) |
|---|
| 349 | - d.callback(None) |
|---|
| 350 | - self.assertRaises(defer.AlreadyCalledError, d.cancel) |
|---|
| 351 | - self.assertEquals(self.cancellerCalled, False) |
|---|
| 352 | + A L{defer.Deferred} without a canceller must errback with a |
|---|
| 353 | + L{defer.CancelledError} and not callback. |
|---|
| 354 | + """ |
|---|
| 355 | + d = defer.Deferred() |
|---|
| 356 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 357 | + d.cancel() |
|---|
| 358 | + self.assertEquals(self.errbackResults.type, defer.CancelledError) |
|---|
| 359 | + self.assertEquals(self.callbackResults, None) |
|---|
| 360 | + |
|---|
| 361 | + |
|---|
| 362 | + def test_raisesAfterCancelAndCallback(self): |
|---|
| 363 | + """ |
|---|
| 364 | + A L{defer.Deferred} without a canceller, when cancelled must allow |
|---|
| 365 | + a single extra call to callback, and raise |
|---|
| 366 | + L{defer.AlreadyCalledError} if callbacked or errbacked thereafter. |
|---|
| 367 | + """ |
|---|
| 368 | + d = defer.Deferred() |
|---|
| 369 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 370 | + d.cancel() |
|---|
| 371 | + |
|---|
| 372 | + # A single extra callback should be swallowed. |
|---|
| 373 | + d.callback(None) |
|---|
| 374 | + |
|---|
| 375 | + # But a second call to callback or errback is not. |
|---|
| 376 | + self.assertRaises(defer.AlreadyCalledError, d.callback, None) |
|---|
| 377 | + self.assertRaises(defer.AlreadyCalledError, d.errback, Exception()) |
|---|
| 378 | + |
|---|
| 379 | + |
|---|
| 380 | + def test_raisesAfterCancelAndErrback(self): |
|---|
| 381 | + """ |
|---|
| 382 | + A L{defer.Deferred} without a canceller, when cancelled must allow |
|---|
| 383 | + a single extra call to errback, and raise |
|---|
| 384 | + L{defer.AlreadyCalledError} if callbacked or errbacked thereafter. |
|---|
| 385 | + """ |
|---|
| 386 | + d = defer.Deferred() |
|---|
| 387 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 388 | + d.cancel() |
|---|
| 389 | + |
|---|
| 390 | + # A single extra errback should be swallowed. |
|---|
| 391 | + d.errback(Exception()) |
|---|
| 392 | + |
|---|
| 393 | + # But a second call to callback or errback is not. |
|---|
| 394 | + self.assertRaises(defer.AlreadyCalledError, d.callback, None) |
|---|
| 395 | + self.assertRaises(defer.AlreadyCalledError, d.errback, Exception()) |
|---|
| 396 | + |
|---|
| 397 | + |
|---|
| 398 | + def test_noCancellerMultipleCancelsAfterCancelAndCallback(self): |
|---|
| 399 | + """ |
|---|
| 400 | + A L{Deferred} without a canceller, when cancelled and then |
|---|
| 401 | + callbacked, ignores multiple cancels thereafter. |
|---|
| 402 | + """ |
|---|
| 403 | + d = defer.Deferred() |
|---|
| 404 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 405 | + d.cancel() |
|---|
| 406 | + currentFailure = self.errbackResults |
|---|
| 407 | + # One callback will be ignored |
|---|
| 408 | + d.callback(None) |
|---|
| 409 | + # Cancel should have no effect. |
|---|
| 410 | + d.cancel() |
|---|
| 411 | + self.assertIdentical(currentFailure, self.errbackResults) |
|---|
| 412 | + |
|---|
| 413 | + |
|---|
| 414 | + def test_noCancellerMultipleCancelsAfterCancelAndErrback(self): |
|---|
| 415 | + """ |
|---|
| 416 | + A L{defer.Deferred} without a canceller, when cancelled and then |
|---|
| 417 | + errbacked, ignores multiple cancels thereafter. |
|---|
| 418 | + """ |
|---|
| 419 | + d = defer.Deferred() |
|---|
| 420 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 421 | + d.cancel() |
|---|
| 422 | + self.assertEquals(self.errbackResults.type, defer.CancelledError) |
|---|
| 423 | + currentFailure = self.errbackResults |
|---|
| 424 | + # One errback will be ignored |
|---|
| 425 | + d.errback(GenericError()) |
|---|
| 426 | + # I.e., we should still have a CancelledError. |
|---|
| 427 | + self.assertEquals(self.errbackResults.type, defer.CancelledError) |
|---|
| 428 | + d.cancel() |
|---|
| 429 | + self.assertIdentical(currentFailure, self.errbackResults) |
|---|
| 430 | + |
|---|
| 431 | + |
|---|
| 432 | + def test_noCancellerMultipleCancel(self): |
|---|
| 433 | + """ |
|---|
| 434 | + Calling cancel multiple times on a deferred with no canceller |
|---|
| 435 | + results in a L{defer.CancelledError}. Subsequent calls to cancel |
|---|
| 436 | + do not cause an error. |
|---|
| 437 | + """ |
|---|
| 438 | + d = defer.Deferred() |
|---|
| 439 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 440 | + d.cancel() |
|---|
| 441 | + self.assertEquals(self.errbackResults.type, defer.CancelledError) |
|---|
| 442 | + currentFailure = self.errbackResults |
|---|
| 443 | + d.cancel() |
|---|
| 444 | + self.assertIdentical(currentFailure, self.errbackResults) |
|---|
| 445 | + |
|---|
| 446 | + |
|---|
| 447 | + def test_cancellerMultipleCancel(self): |
|---|
| 448 | + """ |
|---|
| 449 | + Verify that calling cancel multiple times on a deferred with a |
|---|
| 450 | + canceller that does not errback results in a |
|---|
| 451 | + L{defer.CancelledError} and that subsequent calls to cancel do not |
|---|
| 452 | + cause an error and that after all that, the canceller was only |
|---|
| 453 | + called once. |
|---|
| 454 | + """ |
|---|
| 455 | + def cancel(d): |
|---|
| 456 | + self.cancellerCallCount += 1 |
|---|
| 457 | + |
|---|
| 458 | + d = defer.Deferred(canceller=cancel) |
|---|
| 459 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 460 | + d.cancel() |
|---|
| 461 | + self.assertEquals(self.errbackResults.type, defer.CancelledError) |
|---|
| 462 | + currentFailure = self.errbackResults |
|---|
| 463 | + d.cancel() |
|---|
| 464 | + self.assertIdentical(currentFailure, self.errbackResults) |
|---|
| 465 | + self.assertEquals(self.cancellerCallCount, 1) |
|---|
| 466 | + |
|---|
| 467 | + |
|---|
| 468 | + def test_simpleCanceller(self): |
|---|
| 469 | + """ |
|---|
| 470 | + Verify that a L{defer.Deferred} calls its specified canceller when |
|---|
| 471 | + it is cancelled, and that further call/errbacks raise |
|---|
| 472 | + L{defer.AlreadyCalledError}. |
|---|
| 473 | + """ |
|---|
| 474 | + def cancel(d): |
|---|
| 475 | + self.cancellerCallCount += 1 |
|---|
| 476 | + |
|---|
| 477 | + d = defer.Deferred(canceller=cancel) |
|---|
| 478 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 479 | + d.cancel() |
|---|
| 480 | + self.assertEquals(self.cancellerCallCount, 1) |
|---|
| 481 | + self.assertEquals(self.errbackResults.type, defer.CancelledError) |
|---|
| 482 | + |
|---|
| 483 | + # Test that further call/errbacks are *not* swallowed |
|---|
| 484 | + self.assertRaises(defer.AlreadyCalledError, d.callback, None) |
|---|
| 485 | + self.assertRaises(defer.AlreadyCalledError, d.errback, Exception()) |
|---|
| 486 | + |
|---|
| 487 | + |
|---|
| 488 | + def test_cancellerArg(self): |
|---|
| 489 | + """ |
|---|
| 490 | + Verify that a canceller is given the correct deferred argument. |
|---|
| 491 | + """ |
|---|
| 492 | + def cancel(d1): |
|---|
| 493 | + self.assertIdentical(d1, d) |
|---|
| 494 | + d = defer.Deferred(canceller=cancel) |
|---|
| 495 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 496 | + d.cancel() |
|---|
| 497 | + |
|---|
| 498 | + |
|---|
| 499 | + def test_cancelAfterCallback(self): |
|---|
| 500 | + """ |
|---|
| 501 | + Test that cancelling a deferred after it has been callbacked does |
|---|
| 502 | + not cause an error. |
|---|
| 503 | + """ |
|---|
| 504 | + def cancel(d): |
|---|
| 505 | + self.cancellerCallCount += 1 |
|---|
| 506 | + d.errback(GenericError()) |
|---|
| 507 | + d = defer.Deferred(canceller=cancel) |
|---|
| 508 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 509 | + d.callback('biff!') |
|---|
| 510 | + d.cancel() |
|---|
| 511 | + self.assertEquals(self.cancellerCallCount, 0) |
|---|
| 512 | + self.assertEquals(self.errbackResults, None) |
|---|
| 513 | + self.assertEquals(self.callbackResults, 'biff!') |
|---|
| 514 | + |
|---|
| 515 | + |
|---|
| 516 | + def test_cancelAfterErrback(self): |
|---|
| 517 | + """ |
|---|
| 518 | + Test that cancelling a L{Deferred} after it has been errbacked does |
|---|
| 519 | + not result in a L{defer.CancelledError}. |
|---|
| 520 | + """ |
|---|
| 521 | + def cancel(d): |
|---|
| 522 | + self.cancellerCallCount += 1 |
|---|
| 523 | + d.errback(GenericError()) |
|---|
| 524 | + d = defer.Deferred(canceller=cancel) |
|---|
| 525 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 526 | + d.errback(GenericError()) |
|---|
| 527 | + d.cancel() |
|---|
| 528 | + self.assertEquals(self.cancellerCallCount, 0) |
|---|
| 529 | + self.assertEquals(self.errbackResults.type, GenericError) |
|---|
| 530 | + self.assertEquals(self.callbackResults, None) |
|---|
| 531 | + |
|---|
| 532 | + |
|---|
| 533 | + def test_cancellerThatErrbacks(self): |
|---|
| 534 | + """ |
|---|
| 535 | + Test a canceller which errbacks its deferred. |
|---|
| 536 | + """ |
|---|
| 537 | + def cancel(d): |
|---|
| 538 | + self.cancellerCallCount += 1 |
|---|
| 539 | + d.errback(GenericError()) |
|---|
| 540 | + d = defer.Deferred(canceller=cancel) |
|---|
| 541 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 542 | + d.cancel() |
|---|
| 543 | + self.assertEquals(self.cancellerCallCount, 1) |
|---|
| 544 | + self.assertEquals(self.errbackResults.type, GenericError) |
|---|
| 545 | + |
|---|
| 546 | + |
|---|
| 547 | + def test_cancellerThatCallbacks(self): |
|---|
| 548 | + """ |
|---|
| 549 | + Test a canceller which calls its deferred. |
|---|
| 550 | + """ |
|---|
| 551 | + def cancel(d): |
|---|
| 552 | + self.cancellerCallCount += 1 |
|---|
| 553 | + d.callback('hello!') |
|---|
| 554 | + d = defer.Deferred(canceller=cancel) |
|---|
| 555 | + d.addCallbacks(self._callback, self._errback) |
|---|
| 556 | + d.cancel() |
|---|
| 557 | + self.assertEquals(self.cancellerCallCount, 1) |
|---|
| 558 | + self.assertEquals(self.callbackResults, 'hello!') |
|---|
| 559 | + self.assertEquals(self.errbackResults, None) |
|---|
| 560 | |
|---|
| 561 | |
|---|
| 562 | def test_cancelNestedDeferred(self): |
|---|
| 563 | """ |
|---|
| 564 | Verify that a Deferred, A, which is waiting on another Deferred, B, |
|---|
| 565 | - returned from one of its callbacks, will propagate L{CancelledError} |
|---|
| 566 | - when B is cancelled. |
|---|
| 567 | + returned from one of its callbacks, will propagate |
|---|
| 568 | + L{defer.CancelledError} when A is cancelled. |
|---|
| 569 | """ |
|---|
| 570 | def innerCancel(d): |
|---|
| 571 | - self.assertIdentical(d, B) |
|---|
| 572 | - self.cancellerCalled = True |
|---|
| 573 | + self.cancellerCallCount += 1 |
|---|
| 574 | def cancel(d): |
|---|
| 575 | self.assert_(False) |
|---|
| 576 | |
|---|
| 577 | @@ -741,8 +896,11 @@ |
|---|
| 578 | A.addCallback(lambda data: B) |
|---|
| 579 | A.cancel() |
|---|
| 580 | A.addCallbacks(self._callback, self._errback) |
|---|
| 581 | - self.assertEquals(self.cancellerCalled, True) |
|---|
| 582 | - self.assertEquals(self.errback_results.type, defer.CancelledError) |
|---|
| 583 | + # The cancel count should be one (the cancellation done by B) |
|---|
| 584 | + self.assertEquals(self.cancellerCallCount, 1) |
|---|
| 585 | + # B's canceller didn't errback, so defer.py will have called errback |
|---|
| 586 | + # with a CancelledError. |
|---|
| 587 | + self.assertEquals(self.errbackResults.type, defer.CancelledError) |
|---|
| 588 | |
|---|
| 589 | |
|---|
| 590 | |
|---|
| 591 | @@ -775,9 +933,9 @@ |
|---|
| 592 | |
|---|
| 593 | def test_errorLog(self): |
|---|
| 594 | """ |
|---|
| 595 | - Verify that when a Deferred with no references to it is fired, and its |
|---|
| 596 | - final result (the one not handled by any callback) is an exception, |
|---|
| 597 | - that exception will be logged immediately. |
|---|
| 598 | + Verify that when a L{Deferred} with no references to it is fired, |
|---|
| 599 | + and its final result (the one not handled by any callback) is an |
|---|
| 600 | + exception, that exception will be logged immediately. |
|---|
| 601 | """ |
|---|
| 602 | defer.Deferred().addCallback(lambda x: 1/0).callback(1) |
|---|
| 603 | gc.collect() |
|---|
| 604 | @@ -884,6 +1042,33 @@ |
|---|
| 605 | lock.release() |
|---|
| 606 | self.failIf(lock.locked) |
|---|
| 607 | |
|---|
| 608 | + |
|---|
| 609 | + def test_cancelLockAfterAcquired(self): |
|---|
| 610 | + """ |
|---|
| 611 | + When canceling a L{Deferred] from a L{DeferredLock} that already |
|---|
| 612 | + has the lock, the cancel should have no effect. |
|---|
| 613 | + """ |
|---|
| 614 | + def _failOnErrback(_): |
|---|
| 615 | + self.fail("Unexpected errback call!") |
|---|
| 616 | + lock = defer.DeferredLock() |
|---|
| 617 | + d = lock.acquire() |
|---|
| 618 | + d.addErrback(_failOnErrback) |
|---|
| 619 | + d.cancel() |
|---|
| 620 | + |
|---|
| 621 | + |
|---|
| 622 | + def test_cancelLockBeforeAcquired(self): |
|---|
| 623 | + """ |
|---|
| 624 | + When canceling a L{Deferred] from a L{DeferredLock} that does not |
|---|
| 625 | + yet have the lock (i.e., the L{Deferred} has not fired), the cancel |
|---|
| 626 | + should cause a L{defer.CancelledError} failure. |
|---|
| 627 | + """ |
|---|
| 628 | + lock = defer.DeferredLock() |
|---|
| 629 | + _ign = lock.acquire() |
|---|
| 630 | + d = lock.acquire() |
|---|
| 631 | + self.failUnlessFailure(d, defer.CancelledError) |
|---|
| 632 | + d.cancel() |
|---|
| 633 | + |
|---|
| 634 | + |
|---|
| 635 | def testSemaphore(self): |
|---|
| 636 | N = 13 |
|---|
| 637 | sem = defer.DeferredSemaphore(N) |
|---|
| 638 | @@ -929,6 +1114,35 @@ |
|---|
| 639 | sem.release() |
|---|
| 640 | self.assertEquals(self.counter, N + 1) |
|---|
| 641 | |
|---|
| 642 | + |
|---|
| 643 | + def test_cancelSemaphoreAfterAcquired(self): |
|---|
| 644 | + """ |
|---|
| 645 | + When canceling a L{Deferred] from a L{DeferredSemaphore} that |
|---|
| 646 | + already has the semaphore, the cancel should have no effect. |
|---|
| 647 | + """ |
|---|
| 648 | + def _failOnErrback(_): |
|---|
| 649 | + self.fail("Unexpected errback call!") |
|---|
| 650 | + |
|---|
| 651 | + sem = defer.DeferredSemaphore(1) |
|---|
| 652 | + d = sem.acquire() |
|---|
| 653 | + d.addErrback(_failOnErrback) |
|---|
| 654 | + d.cancel() |
|---|
| 655 | + |
|---|
| 656 | + |
|---|
| 657 | + def test_cancelSemaphoreBeforeAcquired(self): |
|---|
| 658 | + """ |
|---|
| 659 | + When canceling a L{Deferred] from a L{DeferredSemaphore} that does |
|---|
| 660 | + not yet have the semaphore (i.e., the L{Deferred} has not fired), |
|---|
| 661 | + the cancel should cause a L{defer.CancelledError} failure. |
|---|
| 662 | + """ |
|---|
| 663 | + sem = defer.DeferredSemaphore(1) |
|---|
| 664 | + _ign = sem.acquire() |
|---|
| 665 | + d = sem.acquire() |
|---|
| 666 | + self.failUnlessFailure(d, defer.CancelledError) |
|---|
| 667 | + d.cancel() |
|---|
| 668 | + return d |
|---|
| 669 | + |
|---|
| 670 | + |
|---|
| 671 | def testQueue(self): |
|---|
| 672 | N, M = 2, 2 |
|---|
| 673 | queue = defer.DeferredQueue(N, M) |
|---|
| 674 | @@ -967,6 +1181,34 @@ |
|---|
| 675 | self.assertRaises(defer.QueueUnderflow, queue.get) |
|---|
| 676 | |
|---|
| 677 | |
|---|
| 678 | + def test_cancelQueueAfterSynchronousGet(self): |
|---|
| 679 | + """ |
|---|
| 680 | + When canceling a L{Deferred] from a L{DeferredQueue} that already has |
|---|
| 681 | + a result, the cancel should have no effect. |
|---|
| 682 | + """ |
|---|
| 683 | + def _failOnErrback(_): |
|---|
| 684 | + self.fail("Unexpected errback call!") |
|---|
| 685 | + |
|---|
| 686 | + queue = defer.DeferredQueue() |
|---|
| 687 | + d = queue.get() |
|---|
| 688 | + d.addErrback(_failOnErrback) |
|---|
| 689 | + queue.put(None) |
|---|
| 690 | + d.cancel() |
|---|
| 691 | + |
|---|
| 692 | + |
|---|
| 693 | + def test_cancelQueueAfterGet(self): |
|---|
| 694 | + """ |
|---|
| 695 | + When canceling a L{Deferred] from a L{DeferredQueue} that does not |
|---|
| 696 | + have a result (i.e., the L{Deferred} has not fired), the cancel |
|---|
| 697 | + should cause a L{defer.CancelledError} failure. |
|---|
| 698 | + """ |
|---|
| 699 | + queue = defer.DeferredQueue() |
|---|
| 700 | + d = queue.get() |
|---|
| 701 | + self.failUnlessFailure(d, defer.CancelledError) |
|---|
| 702 | + d.cancel() |
|---|
| 703 | + return d |
|---|
| 704 | + |
|---|
| 705 | + |
|---|
| 706 | |
|---|
| 707 | class DeferredFilesystemLockTestCase(unittest.TestCase): |
|---|
| 708 | """ |
|---|