Hi again Glyph, Andrew
Below is a simpler and more flexible ControllableDeferred class.
The idea is simple: you want a deferred that you can call/errback yourself, and which you can also deactivate so that it never fires. This is done by manipulating two normal deferreds (self, and a deferred received from maybeDeferred). This ControllableDeferred class sits in the middle and will (a) pass on the normal result (via chainDeferred) if it arrives first, or (b) if called by the user, fire with the user-supplied value, or (c) if deactivated by the user, never fire. In cases b and c it will ignore the result (if any ever arrives) from the function originally called.
In summary, this provides two things you can't ordinarily do when you get a deferred (d) back from calling a function:
- Call d yourself, thereby firing it immediately. - Deactivate d, so that it never fires.
The code below could be a bit more sophisticated. E.g., it could tell you if you try to call a deferred you've deactivated. It should probably use an attribute name other than _called. I've kept it sparse for now though so the general idea is clearer. I changed its name to reflect that the deferred is more controllable.
from twisted.internet import defer
class ControllableDeferred(defer.Deferred): def __init__(self, f, *args, **kw): defer.Deferred.__init__(self) self._called = False self._calld = defer.maybeDeferred(f, *args, **kw).addBoth(self._fire)
def _fire(self, ign): if not self._called: self._called = True self._calld.chainDeferred(self)
def callback(self, result): if not self._called: self._called = True defer.Deferred.callback(self, result)
def errback(self, fail=None): if not self._called: self._called = True defer.Deferred.callback(self, fail)
def deactivate(self): self._called = True