Package flumotion :: Package twisted :: Module defer
[hide private]

Source Code for Module flumotion.twisted.defer

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_defer -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  from twisted.internet import defer, reactor 
 23  from twisted.python import reflect 
 24   
 25  # FIXME: this is for HandledException - maybe it should move here instead ? 
 26  from flumotion.common import errors 
 27   
 28  # See flumotion.test.test_defer for examples 
29 -def defer_generator(proc):
30 def wrapper(*args, **kwargs): 31 gen = proc(*args, **kwargs) 32 result = defer.Deferred() 33 34 # To support having the errback of last resort, we need to have 35 # an errback which runs after all the other errbacks, *at the 36 # point at which the deferred is fired*. So users of this code 37 # have from between the time the deferred is created and the 38 # time that the deferred is fired to attach their errbacks. 39 # 40 # Unfortunately we only control the time that the deferred is 41 # created. So we attach a first errback that then adds an 42 # errback to the end of the list. Unfortunately we can't add to 43 # the list while the deferred is firing. In a decision between 44 # having decent error reporting and being nice to a small part 45 # of twisted I chose the former. This code takes a reference to 46 # the callback list, so that we can add an errback to the list 47 # while the deferred is being fired. It temporarily sets the 48 # state of the deferred to not having been fired, so that adding 49 # the errbacks doesn't automatically call the newly added 50 # methods. 51 result.__callbacks = result.callbacks 52 def with_saved_callbacks(proc, *_args, **_kwargs): 53 saved_callbacks, saved_called = result.callbacks, result.called 54 result.callbacks, result.called = result.__callbacks, False 55 proc(*_args, **_kwargs) 56 result.callbacks, result.called = saved_callbacks, saved_called
57 58 # Add errback-of-last-resort 59 def default_errback(failure, d): 60 # an already handled exception just gets propagated up without 61 # doing a traceback 62 if failure.check(errors.HandledException): 63 return failure 64 65 def print_traceback(f): 66 import traceback 67 print 'flumotion.twisted.defer: ' + \ 68 'Unhandled error calling', proc.__name__, ':', f.type 69 traceback.print_exc() 70 with_saved_callbacks (lambda: d.addErrback(print_traceback)) 71 raise 72 result.addErrback(default_errback, result) 73 74 def generator_next(): 75 try: 76 x = gen.next() 77 if isinstance(x, defer.Deferred): 78 x.addCallback(callback, x).addErrback(errback, x) 79 else: 80 result.callback(x) 81 except StopIteration: 82 result.callback(None) 83 except Exception, e: 84 result.errback(e) 85 86 def errback(failure, d): 87 def raise_error(): 88 # failure.parents[-1] will be the exception class for local 89 # failures and the string name of the exception class 90 # for remote failures (which might not exist in our 91 # namespace) 92 # 93 # failure.value will be the tuple of arguments to the 94 # exception in the local case, or a string 95 # representation of that in the remote case (see 96 # pb.CopyableFailure.getStateToCopy()). 97 # 98 # we can only reproduce a remote exception if the 99 # exception class is in our namespace, and it only takes 100 # one string argument. if either condition is not true, 101 # we wrap the strings in a default Exception. 102 k, v = failure.parents[-1], failure.value 103 try: 104 if isinstance(k, str): 105 k = reflect.namedClass(k) 106 if isinstance(v, tuple): 107 e = k(*v) 108 else: 109 e = k(v) 110 except Exception: 111 e = Exception('%s: %r' % (failure.type, v)) 112 raise e 113 d.value = raise_error 114 generator_next() 115 116 def callback(result, d): 117 d.value = lambda: result 118 generator_next() 119 120 generator_next() 121 122 return result 123 124 return wrapper 125
126 -def defer_generator_method(proc):
127 return lambda self, *args, **kwargs: \ 128 defer_generator(proc)(self, *args, **kwargs)
129
130 -def defer_call_later(deferred):
131 """ 132 Return a deferred which will fire from a callLater after d fires 133 """ 134 def fire(result, d): 135 reactor.callLater(0, d.callback, result)
136 res = defer.Deferred() 137 deferred.addCallback(fire, res) 138 return res 139
140 -class Resolution:
141 """ 142 I am a helper class to make sure that the deferred is fired only once 143 with either a result or exception. 144 145 @ivar d: the deferred that gets fired as part of the resolution 146 @type d: L{twisted.internet.defer.Deferred} 147 """
148 - def __init__(self):
149 self.d = defer.Deferred() 150 self.fired = False
151
152 - def cleanup(self):
153 """ 154 Clean up any resources related to the resolution. 155 Subclasses can implement me. 156 """ 157 pass
158
159 - def callback(self, result):
160 """ 161 Make the result succeed, triggering the callbacks with the given result. 162 If a result was already reached, do nothing. 163 """ 164 if not self.fired: 165 self.fired = True 166 self.cleanup() 167 self.d.callback(result)
168
169 - def errback(self, exception):
170 """ 171 Make the result fail, triggering the errbacks with the given exception. 172 If a result was already reached, do nothing. 173 """ 174 if not self.fired: 175 self.fired = True 176 self.cleanup() 177 self.d.errback(exception)
178