3 print "hey python-mode, stop thinking I want 8-char indentation"
6 utilities to be compatible with both Twisted-1.3 and 2.0
8 implements. Use this like the following.
10 from buildbot.twcompat import implements
15 __implements__ = IFoo,
18 from buildbot.tcompat import Interface
22 from buildbot.tcompat import providedBy
23 assert providedBy(obj, IFoo)
28 from twisted
.copyright
import version
29 from twisted
.python
import components
31 # does our Twisted use zope.interface?
33 if hasattr(components
, "interface"):
35 from zope
.interface
import implements
36 from zope
.interface
import Interface
37 def providedBy(obj
, iface
):
38 return iface
.providedBy(obj
)
41 from twisted
.python
.components
import Interface
42 providedBy
= components
.implements
44 # waitForDeferred and getProcessOutputAndValue are twisted-2.0 things. If
45 # we're running under 1.3, patch them into place. These versions are copied
46 # from twisted somewhat after 2.0.1 .
48 from twisted
.internet
import defer
49 if not hasattr(defer
, 'waitForDeferred'):
50 Deferred
= defer
.Deferred
51 class waitForDeferred
:
53 API Stability: semi-stable
55 Maintainer: U{Christopher Armstrong<mailto:radix@twistedmatrix.com>}
57 waitForDeferred and deferredGenerator help you write
58 Deferred-using code that looks like it's blocking (but isn't
59 really), with the help of generators.
61 There are two important functions involved: waitForDeferred, and
65 thing = waitForDeferred(makeSomeRequestResultingInDeferred())
67 thing = thing.getResult()
68 print thing #the result! hoorj!
69 thingummy = deferredGenerator(thingummy)
71 waitForDeferred returns something that you should immediately yield;
72 when your generator is resumed, calling thing.getResult() will either
73 give you the result of the Deferred if it was a success, or raise an
74 exception if it was a failure.
76 deferredGenerator takes one of these waitForDeferred-using
77 generator functions and converts it into a function that returns a
78 Deferred. The result of the Deferred will be the last
79 value that your generator yielded (remember that 'return result' won't
80 work; use 'yield result; return' in place of that).
82 Note that not yielding anything from your generator will make the
83 Deferred result in None. Yielding a Deferred from your generator
84 is also an error condition; always yield waitForDeferred(d)
87 The Deferred returned from your deferred generator may also
88 errback if your generator raised an exception.
91 thing = waitForDeferred(makeSomeRequestResultingInDeferred())
93 thing = thing.getResult()
94 if thing == 'I love Twisted':
95 # will become the result of the Deferred
96 yield 'TWISTED IS GREAT!'
99 # will trigger an errback
100 raise Exception('DESTROY ALL LIFE')
101 thingummy = deferredGenerator(thingummy)
103 Put succinctly, these functions connect deferred-using code with this
104 'fake blocking' style in both directions: waitForDeferred converts from
105 a Deferred to the 'blocking' style, and deferredGenerator converts from
106 the 'blocking' style to a Deferred.
108 def __init__(self
, d
):
109 if not isinstance(d
, Deferred
):
110 raise TypeError("You must give waitForDeferred a Deferred. You gave it %r." % (d
,))
114 if hasattr(self
, 'failure'):
115 self
.failure
.raiseException()
118 def _deferGenerator(g
, deferred
=None, result
=None):
120 See L{waitForDeferred}.
124 deferred
= defer
.Deferred()
127 except StopIteration:
128 deferred
.callback(result
)
134 # Deferred.callback(Deferred) raises an error; we catch this case
135 # early here and give a nicer error message to the user in case
136 # they yield a Deferred. Perhaps eventually these semantics may
138 if isinstance(result
, defer
.Deferred
):
139 return defer
.fail(TypeError("Yield waitForDeferred(d), not d!"))
141 if isinstance(result
, waitForDeferred
):
143 # Pass vars in so they don't get changed going around the loop
144 def gotResult(r
, waiting
=waiting
, result
=result
):
150 _deferGenerator(g
, deferred
, r
)
151 def gotError(f
, waiting
=waiting
, result
=result
):
157 _deferGenerator(g
, deferred
, f
)
158 result
.d
.addCallbacks(gotResult
, gotError
)
160 # Haven't called back yet, set flag so that we get reinvoked
161 # and return from the loop
167 def func_metamerge(f
, g
):
169 Merge function metadata from f -> g and return g
172 g
.__doc
__ = f
.__doc
__
173 g
.__dict
__.update(f
.__dict
__)
174 g
.__name
__ = f
.__name
__
175 except (TypeError, AttributeError):
179 def deferredGenerator(f
):
181 See L{waitForDeferred}.
183 def unwindGenerator(*args
, **kwargs
):
184 return _deferGenerator(f(*args
, **kwargs
))
185 return func_metamerge(f
, unwindGenerator
)
187 defer
.waitForDeferred
= waitForDeferred
188 defer
.deferredGenerator
= deferredGenerator
190 from twisted
.internet
import utils
191 if not hasattr(utils
, "getProcessOutputAndValue"):
192 from twisted
.internet
import reactor
, protocol
193 _callProtocolWithDeferred
= utils
._callProtocolWithDeferred
200 class _EverythingGetter(protocol
.ProcessProtocol
):
202 def __init__(self
, deferred
):
203 self
.deferred
= deferred
204 self
.outBuf
= StringIO
.StringIO()
205 self
.errBuf
= StringIO
.StringIO()
206 self
.outReceived
= self
.outBuf
.write
207 self
.errReceived
= self
.errBuf
.write
209 def processEnded(self
, reason
):
210 out
= self
.outBuf
.getvalue()
211 err
= self
.errBuf
.getvalue()
215 self
.deferred
.errback((out
, err
, e
.signal
))
217 self
.deferred
.callback((out
, err
, code
))
219 def getProcessOutputAndValue(executable
, args
=(), env
={}, path
='.',
221 """Spawn a process and returns a Deferred that will be called back
222 with its output (from stdout and stderr) and it's exit code as (out,
223 err, code) If a signal is raised, the Deferred will errback with the
224 stdout and stderr up to that point, along with the signal, as (out,
227 return _callProtocolWithDeferred(_EverythingGetter
,
228 executable
, args
, env
, path
,
230 utils
.getProcessOutputAndValue
= getProcessOutputAndValue
233 # copied from Twisted circa 2.2.0
234 def _which(name
, flags
=os
.X_OK
):
235 """Search PATH for executable files with the given name.
238 @param name: The name for which to search.
241 @param flags: Arguments to L{os.access}.
244 @return: A list of the full paths to files found, in the
245 order in which they were found.
248 exts
= filter(None, os
.environ
.get('PATHEXT', '').split(os
.pathsep
))
249 for p
in os
.environ
['PATH'].split(os
.pathsep
):
250 p
= os
.path
.join(p
, name
)
251 if os
.access(p
, flags
):
255 if os
.access(pext
, flags
):
261 from twisted
.python
.procutils
import which