1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 from twisted.internet import defer, reactor
23 from twisted.python import reflect
24
25
26 from flumotion.common import errors
27
28
30 def wrapper(*args, **kwargs):
31 gen = proc(*args, **kwargs)
32 result = defer.Deferred()
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
59 def default_errback(failure, d):
60
61
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
89
90
91
92
93
94
95
96
97
98
99
100
101
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
127 return lambda self, *args, **kwargs: \
128 defer_generator(proc)(self, *args, **kwargs)
129
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
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 """
149 self.d = defer.Deferred()
150 self.fired = False
151
153 """
154 Clean up any resources related to the resolution.
155 Subclasses can implement me.
156 """
157 pass
158
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
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