<div dir="ltr">By the way, I don't need any of this to make its way into Twisted. I can still write my own class that does what I want (well, wanted).  Below is a 2013 version of the  CancelableDeferred. It's untested. The basic idea is that if you get a regular deferred from somewhere, you can use the class below to make a new deferred that you can callback, errback, or cancel at will. You can give a value to 'cancel' and it will be in args[0] of the CancelledError that your errback will receive.<div>
<br></div><div>I still find this approach attractive because it maintains the power/elegance of coding with Twisted deferreds but also gives the caller of deferred-producing code more flexibility. That's got to be a good thing, right?</div>
<div><br></div><div style>I hope the code makes the intention more clear, not less.</div><div><br></div><div>Terry</div><div><br><div><div><br></div><div><div>from twisted.internet.defer import CancelledError, Deferred</div>
<div>from twisted.python.failure import Failure</div><div><br></div><div>class ControllableDeferred2013(object):</div><div><br></div><div>    '''A Deferred-like class that takes a regular Twisted Deferred and</div>
<div>    provides a deferred that can be fired at will. If you have a regular</div><div>    Twisted Deferred, you can produce a deferred you have more control over</div><div>    by using your Deferred instance to make an instance of this class.</div>
<div><br></div><div>    Any time you need to fire a ControllableDeferred2013 instance for any</div><div>    reason, call its callback, errback or cancel method. It will fire</div><div>    immediately with the value you provide and the original Deferred will</div>
<div>    be cancelled.'''</div><div><br></div><div>    def __init__(self, originalDeferred):</div><div>        self._fired = False</div><div>        self._originalDeferred = originalDeferred</div><div>        self._newDeferred = Deferred()</div>
<div>        for method in ('addBoth', 'addCallback', 'addCallbacks', 'addErrback',</div><div>                             'chainDeferred'):</div><div>            setattr(self, method, getattr(self._newDeferred, method))</div>
<div>        originalDeferred.addBoth(self._originalFired)</div><div><br></div><div>    def _originalFired(self, result):</div><div>        if not self._fired:</div><div>            self._fired = True</div><div>            self._originalDeferred.chainDeferred(self._newDeferred)</div>
<div><br></div><div>    def cancel(self, value=None):</div><div>        if not self._fired:</div><div>            self._fired = True</div><div>            self._newDeferred.errback(Failure(CancelledError(value)))</div><div>
            self._originalDeferred.cancel()</div><div><br></div><div>    def callback(self, result=None):</div><div>        if not self._fired:</div><div>            self._fired = True</div><div>            self._newDeferred.callback(result)</div>
<div>            self._originalDeferred.cancel()</div><div><br></div><div>    def errback(self, fail=None):</div><div>        if not self._fired:</div><div>            self._fired = True</div><div>            self._newDeferred.errback(fail)</div>
<div>            self._originalDeferred.cancel()</div><div><br></div><div>    def pause(self):</div><div>        self._newDeferred.pause()</div><div>        self._originalDeferred.pause()</div><div><br></div><div>    def unpause(self):</div>
<div>        self._newDeferred.unpause()</div><div>        self._originalDeferred.unpause()</div></div><div><br></div><div><br></div><div># BTW, I posted the above code to <a href="http://blogs.fluidinfo.com/terry/2013/06/20/yet-another-cancelable-twisted-deferred-class/">http://blogs.fluidinfo.com/terry/2013/06/20/yet-another-cancelable-twisted-deferred-class/</a> as well.</div>
<div style><br></div></div></div></div>