[Twisted-Python] Raising exception from a Deferred canceller.
kylerzhang11 at gmail.com
Thu Aug 29 03:27:46 MDT 2013
As itamar mentioned in ticket #6676 <http://tm.tl/#6676>, If a cancellation
function for a Deferred throws an exception(the cancel() method of
throw exceptions, but the canceller may), behavior is undefined. If the
cancellation function throws an exception it is currently not caught, and
cancellation does not occur.
We can catch the exception and log it, and fallback to just firing Deferred
withCancelledError. This won’t break any old code. But an exception
raising from the cancellation function often means the cancellation is
Another option we have is taking this opportunity to make the cancellation
being able to fail. There is the motivation:
There are cases where a Deferred is uncancellable. For example, we can call
twisted.mail.imap4.IMAP4Client.delete to delete a mailbox. When the
operation is waiting in the queue, we can cancel it by removing it from the
queue. However, when the operation is already sent and is waiting for the
response, it becomes uncancellable.
If we allow the canceller(NOT the cancel() method of the Deferred) to raise
an exception, we can tell the user the cancellation is failed and the
Deferredwon’t be fired with a CancelledError.
Raising an exception from cancel() may break the old code. So we can catch
the exception raised by the canceller, then return a False without firing
theDeferred to tell the user that the cancellation is failed.
In order to avoid missing unexpected exceptions, we can create a
CancellationFailedError. When the canceller raises CancellationFailedError,
we catch it and return False. When the canceller raises others exceptions,
we catch it, log it then return False.
Something like this:
if not self.called:
canceller = self._canceller
log.err(None, "Unexpected exception from canceller.")
# Arrange to eat the callback that will eventually be fired
# since there was no real canceller.
self._suppressAlreadyCalled = True
if not self.called:
# There was no canceller, or the canceller didn't call
# callback or errback.
elif isinstance(self.result, Deferred):
# Waiting for another deferred -- cancel it instead.
This won’t break any code by raising an exception from cancel(), although
some code may rely on cancel() not returning any value.
So, what’s your opinion on raising an exception from the canceller?
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Twisted-Python