1
# -*- test-case-name: twisted.test.test_defer -*-
3
# Twisted, the Framework of Your Internet
4
# Copyright (C) 2001 Matthew W. Lefkowitz
6
# This library is free software; you can redistribute it and/or
7
# modify it under the terms of version 2.1 of the GNU Lesser General Public
8
# License as published by the Free Software Foundation.
10
# This library is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
# Lesser General Public License for more details.
15
# You should have received a copy of the GNU Lesser General Public
16
# License along with this library; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
"""Support for results that aren't immediately available.
23
Maintainer: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>}
26
from __future__ import nested_scopes
30
from twisted.python import log, failure
33
class AlreadyCalledError(Exception):
36
class AlreadyArmedError(Exception):
39
class TimeoutError(Exception):
48
Return a Deferred that has already had '.callback(result)' called.
50
This is useful when you're writing synchronous code to an
51
asynchronous interface: i.e., some code is calling you expecting a
52
Deferred result, but you don't actually need to do anything
53
asynchronous. Just return defer.succeed(theResult).
55
See L{fail} for a version of this function that uses a failing
56
Deferred rather than a successful one.
58
@param result: The result to give to the Deferred's 'callback'
69
def fail(result=_nothing):
71
Return a Deferred that has already had '.errback(result)' called.
73
See L{succeed}'s docstring for rationale.
75
@param result: The same argument that L{Deferred.errback<twisted.internet.defer.Deferred.errback>} takes.
79
if result is _nothing:
80
result = failure.Failure()
85
def execute(callable, *args, **kw):
86
"""Create a deferred from a callable and arguments.
88
Call the given function with the given arguments. Return a deferred which
89
has been fired with its callback as the result of that invocation or its
90
errback with a Failure for the exception thrown.
93
result = callable(*args, **kw)
97
return succeed(result)
99
def maybeDeferred(f, *args, **kw):
100
"""Invoke a function that may or may not return a deferred.
102
Call the given function with the given arguments. If the returned
103
object is a C{Deferred}, return it. If the returned object is a C{Failure},
104
wrap it with C{fail} and return it. Otherwise, wrap it in C{succeed} and
105
return it. If an exception is raised, convert it to a C{Failure}, wrap it
106
in C{fail}, and then return it.
108
@type f: Any callable
109
@param f: The callable to invoke
111
@param args: The arguments to pass to C{f}
112
@param kw: The keyword arguments to pass to C{f}
115
@return: The result of the function call, wrapped in a C{Deferred} if
118
API Stability: Unstable
121
if isinstance(f, Deferred) or f is None:
123
warnings.warn("First argument to maybeDeferred() should no longer be a Deferred or None. Just pass the function and the arguments.", DeprecationWarning, stacklevel=2)
124
deferred = f or Deferred()
129
result = f(*args, **kw)
132
return fail(failure.Failure())
134
deferred.errback(failure.Failure())
136
if isinstance(result, Deferred):
140
result.chainDeferred(deferred)
141
elif isinstance(result, failure.Failure):
145
deferred.errback(result)
148
return succeed(result)
150
deferred.callback(result)
153
def timeout(deferred):
154
deferred.errback(failure.Failure(TimeoutError("Callback timed out")))
160
"""This is a callback which will be put off until later.
162
Why do we want this? Well, in cases where a function in a threaded
163
program would block until it gets a result, for Twisted it should
164
not block. Instead, it should return a Deferred.
166
This can be implemented for protocols that run over the network by
167
writing an asynchronous protocol for twisted.internet. For methods
168
that come from outside packages that are not under our control, we use
169
threads (see for example L{twisted.enterprise.adbapi}).
171
For more information about Deferreds, see doc/howto/defer.html or
172
U{http://www.twistedmatrix.com/documents/howto/defer}
179
# enable .debug to record creation/first-invoker call stacks, and they
180
# will be added to any AlreadyCalledErrors we raise
186
self.creator = traceback.format_stack()[:-1]
188
def addCallbacks(self, callback, errback=None,
189
callbackArgs=None, callbackKeywords=None,
190
errbackArgs=None, errbackKeywords=None, asDefaults=0):
191
"""Add a pair of callbacks (success and error) to this Deferred.
193
These will be executed when the 'master' callback is run.
195
assert callable(callback)
196
assert errback == None or callable(errback)
197
cbs = ((callback, callbackArgs, callbackKeywords),
198
(errback or (passthru), errbackArgs, errbackKeywords))
200
self.callbacks[-1] = cbs
202
self.callbacks.append(cbs)
203
self.default = asDefaults
208
def addCallback(self, callback, *args, **kw):
209
"""Convenience method for adding just a callback.
213
return self.addCallbacks(callback, callbackArgs=args,
216
def addErrback(self, errback, *args, **kw):
217
"""Convenience method for adding just an errback.
221
return self.addCallbacks(passthru, errback,
225
def addBoth(self, callback, *args, **kw):
226
"""Convenience method for adding a single callable as both a callback
231
return self.addCallbacks(callback, callback,
232
callbackArgs=args, errbackArgs=args,
233
callbackKeywords=kw, errbackKeywords=kw)
235
def chainDeferred(self, d):
236
"""Chain another Deferred to this Deferred.
238
This method adds callbacks to this Deferred to call d's callback or
239
errback, as appropriate."""
240
return self.addCallbacks(d.callback, d.errback)
242
def callback(self, result):
243
"""Run all success callbacks that have been added to this Deferred.
245
Each callback will have its result passed as the first
246
argument to the next; this way, the callbacks act as a
247
'processing chain'. Also, if the success-callback returns a Failure
248
or raises an Exception, processing will continue on the *error*-
251
assert not isinstance(result, Deferred)
252
self._startRunCallbacks(result)
255
def errback(self, fail=None):
256
"""Run all error callbacks that have been added to this Deferred.
258
Each callback will have its result passed as the first
259
argument to the next; this way, the callbacks act as a
260
'processing chain'. Also, if the error-callback returns a non-Failure
261
or doesn't raise an Exception, processing will continue on the
262
*success*-callback chain.
264
If the argument that's passed to me is not a failure.Failure instance,
265
it will be embedded in one. If no argument is passed, a failure.Failure
266
instance will be created based on the current traceback stack.
268
Passing a string as `fail' is deprecated, and will be punished with
271
if not isinstance(fail, failure.Failure):
272
fail = failure.Failure(fail)
274
self._startRunCallbacks(fail)
278
"""Stop processing on a Deferred until L{unpause}() is called.
280
self.paused = self.paused + 1
284
"""Process all callbacks made since L{pause}() was called.
286
self.paused = self.paused - 1
292
def _continue(self, result):
296
def _startRunCallbacks(self, result):
299
raise AlreadyCalledError
300
extra = "\n" + self._debugInfo()
301
raise AlreadyCalledError(extra)
303
self.invoker = traceback.format_stack()[:-2]
308
self.timeoutCall.cancel()
311
# Avoid reference cycles, because this object defines __del__
315
def _debugInfo(self):
317
if hasattr(self, "creator"):
318
info += " C: Deferred was created:\n C:"
319
info += "".join(self.creator).rstrip().replace("\n","\n C:")
321
if hasattr(self, "invoker"):
322
info += " I: First Invoker was:\n I:"
323
info += "".join(self.invoker).rstrip().replace("\n","\n I:")
327
def _runCallbacks(self):
333
callback, args, kw = item[
334
isinstance(self.result, failure.Failure)]
338
self.result = callback(self.result, *args, **kw)
339
if isinstance(self.result, Deferred):
342
# note: this will cause _runCallbacks to be called
343
# "recursively" sometimes... this shouldn't cause any
344
# problems, since all the state has been set back to
345
# the way it's supposed to be, but it is useful to know
346
# in case something goes wrong. deferreds really ought
347
# not to return themselves from their callbacks.
349
self.result.addBoth(self._continue)
352
self.result = failure.Failure()
353
if isinstance(self.result, failure.Failure):
354
self.result.cleanFailure()
358
"""This method is deprecated.
362
def setTimeout(self, seconds, timeoutFunc=timeout, *args, **kw):
363
"""Set a timeout function to be triggered if I am not called.
365
@param seconds: How long to wait (from now) before firing the
368
@param timeoutFunc: will receive the Deferred and *args, **kw as its
369
arguments. The default timeoutFunc will call the errback with a
372
DON'T USE THIS! It's a bad idea! Use a function called by reactor.callLater instead
373
to accomplish the same thing!
375
YOU HAVE BEEN WARNED!
380
assert not self.timeoutCall, "Don't call setTimeout twice on the same Deferred."
382
from twisted.internet import reactor
383
self.timeoutCall = reactor.callLater(
385
lambda: self.called or timeoutFunc(self, *args, **kw))
386
return self.timeoutCall
388
armAndErrback = errback
389
armAndCallback = callback
390
armAndChain = chainDeferred
394
cname = self.__class__.__name__
395
if hasattr(self, 'result'):
396
return "<%s at %s current result: %r>" % (cname, hex(id(self)),
398
return "<%s at %s>" % (cname, hex(id(self)))
403
"""Print tracebacks and die.
405
If the *last* (and I do mean *last*) callback leaves me in an error
406
state, print a traceback (if said errback is a Failure).
409
isinstance(self.result, failure.Failure)):
410
log.msg("Unhandled error in Deferred:", isError=True)
412
log.msg("(debug: " + self._debugInfo() + ")", isError=True)
416
class DeferredList(Deferred):
417
"""I combine a group of deferreds into one callback.
419
I track a list of L{Deferred}s for their callbacks, and make a single
420
callback when they have all completed, a list of (success, result)
421
tuples, 'success' being a boolean.
423
Note that you can still use a L{Deferred} after putting it in a
424
DeferredList. For example, you can suppress 'Unhandled error in Deferred'
425
messages by adding errbacks to the Deferreds *after* putting them in the
426
DeferredList, as a DeferredList won't swallow the errors. (Although a more
427
convenient way to do this is simply to set the consumeErrors flag)
430
fireOnOneCallback = 0
433
def __init__(self, deferredList, fireOnOneCallback=0, fireOnOneErrback=0,
435
"""Initialize a DeferredList.
437
@type deferredList: C{list} of L{Deferred}s
438
@param deferredList: The list of deferreds to track.
439
@param fireOnOneCallback: (keyword param) a flag indicating that
440
only one callback needs to be fired for me to call
442
@param fireOnOneErrback: (keyword param) a flag indicating that
443
only one errback needs to be fired for me to call
445
@param consumeErrors: (keyword param) a flag indicating that any errors
446
raised in the original deferreds should be
447
consumed by this DeferredList. This is useful to
448
prevent spurious warnings being logged.
450
self.resultList = [None] * len(deferredList)
451
Deferred.__init__(self)
452
if len(deferredList) == 0 and not fireOnOneCallback:
453
self.callback(self.resultList)
455
# These flags need to be set *before* attaching callbacks to the
456
# deferreds, because the callbacks use these flags, and will run
457
# synchronously if any of the deferreds are already fired.
458
self.fireOnOneCallback = fireOnOneCallback
459
self.fireOnOneErrback = fireOnOneErrback
460
self.consumeErrors = consumeErrors
461
self.finishedCount = 0
464
for deferred in deferredList:
465
deferred.addCallbacks(self._cbDeferred, self._cbDeferred,
466
callbackArgs=(index,SUCCESS),
467
errbackArgs=(index,FAILURE))
470
def addDeferred(self, deferred):
473
warnings.warn('DeferredList.addDeferred is deprecated.',
474
DeprecationWarning, stacklevel=2)
475
self.resultList.append(None)
476
index = len(self.resultList) - 1
477
deferred.addCallbacks(self._cbDeferred, self._cbDeferred,
478
callbackArgs=(index,SUCCESS),
479
errbackArgs=(index,FAILURE))
481
def _cbDeferred(self, result, index, succeeded):
482
"""(internal) Callback for when one of my deferreds fires.
484
self.resultList[index] = (succeeded, result)
486
self.finishedCount += 1
488
if succeeded == SUCCESS and self.fireOnOneCallback:
489
self.callback((result, index))
490
elif succeeded == FAILURE and self.fireOnOneErrback:
491
self.errback(failure.Failure((result, index)))
492
elif self.finishedCount == len(self.resultList):
493
self.callback(self.resultList)
495
if succeeded == FAILURE and self.consumeErrors:
501
def _parseDListResult(l, fireOnOneErrback=0):
503
for success, value in l:
505
return [x[1] for x in l]
507
def gatherResults(deferredList, fireOnOneErrback=0):
508
"""Returns list with result of given Deferreds.
510
This builds on C{DeferredList} but is useful since you don't
511
need to parse the result for success/failure.
513
@type deferredList: C{list} of L{Deferred}s
516
raise "This function was previously totally, totally broken. Please fix your code to behave as documented."
517
d = DeferredList(deferredList, fireOnOneErrback=1)
518
d.addCallback(_parseDListResult)
521
# Constants for use with DeferredList
526
__all__ = ["Deferred", "DeferredList", "succeed", "fail", "FAILURE", "SUCCESS",
527
"AlreadyCalledError", "TimeoutError", "gatherResults",