~ubuntu-branches/ubuntu/utopic/buildbot/utopic-proposed

« back to all changes in this revision

Viewing changes to buildbot/twcompat.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2006-04-15 21:20:08 UTC
  • Revision ID: james.westby@ubuntu.com-20060415212008-jfj53u29zl30jqi1
Tags: upstream-0.7.2
ImportĀ upstreamĀ versionĀ 0.7.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
if 0:
 
3
    print "hey python-mode, stop thinking I want 8-char indentation"
 
4
 
 
5
"""
 
6
utilities to be compatible with both Twisted-1.3 and 2.0
 
7
 
 
8
implements. Use this like the following.
 
9
 
 
10
from buildbot.tcompat import implements
 
11
class Foo:
 
12
    if implements:
 
13
        implements(IFoo)
 
14
    else:
 
15
        __implements__ = IFoo,
 
16
        
 
17
Interface:
 
18
    from buildbot.tcompat import Interface
 
19
    class IFoo(Interface)
 
20
 
 
21
providedBy:
 
22
    from buildbot.tcompat import providedBy
 
23
    assert providedBy(obj, IFoo)
 
24
"""
 
25
 
 
26
from twisted.copyright import version
 
27
from twisted.python import components
 
28
 
 
29
# does our Twisted use zope.interface?
 
30
if hasattr(components, "interface"):
 
31
    # yes
 
32
    from zope.interface import implements
 
33
    from zope.interface import Interface
 
34
    def providedBy(obj, iface):
 
35
        return iface.providedBy(obj)
 
36
else:
 
37
    # nope
 
38
    implements = None
 
39
    from twisted.python.components import Interface
 
40
    providedBy = components.implements
 
41
 
 
42
# are we using a version of Trial that allows setUp/testFoo/tearDown to
 
43
# return Deferreds?
 
44
oldtrial = version.startswith("1.3")
 
45
 
 
46
# use this at the end of setUp/testFoo/tearDown methods
 
47
def maybeWait(d, timeout="none"):
 
48
    from twisted.trial import unittest
 
49
    if oldtrial:
 
50
        # this is required for oldtrial (twisted-1.3.0) compatibility. When we
 
51
        # move to retrial (twisted-2.0.0), replace these with a simple 'return
 
52
        # d'.
 
53
        if timeout == "none":
 
54
            unittest.deferredResult(d)
 
55
        else:
 
56
            unittest.deferredResult(d, timeout)
 
57
        return None
 
58
    return d
 
59
 
 
60
# waitForDeferred and getProcessOutputAndValue are twisted-2.0 things. If
 
61
# we're running under 1.3, patch them into place. These versions are copied
 
62
# from twisted somewhat after 2.0.1 .
 
63
 
 
64
from twisted.internet import defer
 
65
if not hasattr(defer, 'waitForDeferred'):
 
66
    Deferred = defer.Deferred
 
67
    class waitForDeferred:
 
68
        """
 
69
        API Stability: semi-stable
 
70
 
 
71
        Maintainer: U{Christopher Armstrong<mailto:radix@twistedmatrix.com>}
 
72
 
 
73
        waitForDeferred and deferredGenerator help you write
 
74
        Deferred-using code that looks like it's blocking (but isn't
 
75
        really), with the help of generators.
 
76
 
 
77
        There are two important functions involved: waitForDeferred, and
 
78
        deferredGenerator.
 
79
 
 
80
            def thingummy():
 
81
                thing = waitForDeferred(makeSomeRequestResultingInDeferred())
 
82
                yield thing
 
83
                thing = thing.getResult()
 
84
                print thing #the result! hoorj!
 
85
            thingummy = deferredGenerator(thingummy)
 
86
 
 
87
        waitForDeferred returns something that you should immediately yield;
 
88
        when your generator is resumed, calling thing.getResult() will either
 
89
        give you the result of the Deferred if it was a success, or raise an
 
90
        exception if it was a failure.
 
91
 
 
92
        deferredGenerator takes one of these waitForDeferred-using
 
93
        generator functions and converts it into a function that returns a
 
94
        Deferred. The result of the Deferred will be the last
 
95
        value that your generator yielded (remember that 'return result' won't
 
96
        work; use 'yield result; return' in place of that).
 
97
 
 
98
        Note that not yielding anything from your generator will make the
 
99
        Deferred result in None. Yielding a Deferred from your generator
 
100
        is also an error condition; always yield waitForDeferred(d)
 
101
        instead.
 
102
 
 
103
        The Deferred returned from your deferred generator may also
 
104
        errback if your generator raised an exception.
 
105
 
 
106
            def thingummy():
 
107
                thing = waitForDeferred(makeSomeRequestResultingInDeferred())
 
108
                yield thing
 
109
                thing = thing.getResult()
 
110
                if thing == 'I love Twisted':
 
111
                    # will become the result of the Deferred
 
112
                    yield 'TWISTED IS GREAT!'
 
113
                    return
 
114
                else:
 
115
                    # will trigger an errback
 
116
                    raise Exception('DESTROY ALL LIFE')
 
117
            thingummy = deferredGenerator(thingummy)
 
118
 
 
119
        Put succinctly, these functions connect deferred-using code with this
 
120
        'fake blocking' style in both directions: waitForDeferred converts from
 
121
        a Deferred to the 'blocking' style, and deferredGenerator converts from
 
122
        the 'blocking' style to a Deferred.
 
123
        """
 
124
        def __init__(self, d):
 
125
            if not isinstance(d, Deferred):
 
126
                raise TypeError("You must give waitForDeferred a Deferred. You gave it %r." % (d,))
 
127
            self.d = d
 
128
 
 
129
        def getResult(self):
 
130
            if hasattr(self, 'failure'):
 
131
                self.failure.raiseException()
 
132
            return self.result
 
133
 
 
134
    def _deferGenerator(g, deferred=None, result=None):
 
135
        """
 
136
        See L{waitForDeferred}.
 
137
        """
 
138
        while 1:
 
139
            if deferred is None:
 
140
                deferred = defer.Deferred()
 
141
            try:
 
142
                result = g.next()
 
143
            except StopIteration:
 
144
                deferred.callback(result)
 
145
                return deferred
 
146
            except:
 
147
                deferred.errback()
 
148
                return deferred
 
149
 
 
150
            # Deferred.callback(Deferred) raises an error; we catch this case
 
151
            # early here and give a nicer error message to the user in case
 
152
            # they yield a Deferred. Perhaps eventually these semantics may
 
153
            # change.
 
154
            if isinstance(result, defer.Deferred):
 
155
                return fail(TypeError("Yield waitForDeferred(d), not d!"))
 
156
 
 
157
            if isinstance(result, waitForDeferred):
 
158
                waiting=[True, None]
 
159
                # Pass vars in so they don't get changed going around the loop
 
160
                def gotResult(r, waiting=waiting, result=result):
 
161
                    result.result = r
 
162
                    if waiting[0]:
 
163
                        waiting[0] = False
 
164
                        waiting[1] = r
 
165
                    else:
 
166
                        _deferGenerator(g, deferred, r)
 
167
                def gotError(f, waiting=waiting, result=result):
 
168
                    result.failure = f
 
169
                    if waiting[0]:
 
170
                        waiting[0] = False
 
171
                        waiting[1] = f
 
172
                    else:
 
173
                        _deferGenerator(g, deferred, f)
 
174
                result.d.addCallbacks(gotResult, gotError)
 
175
                if waiting[0]:
 
176
                    # Haven't called back yet, set flag so that we get reinvoked
 
177
                    # and return from the loop
 
178
                    waiting[0] = False
 
179
                    return deferred
 
180
                else:
 
181
                    result = waiting[1]
 
182
 
 
183
    def func_metamerge(f, g):
 
184
        """
 
185
        Merge function metadata from f -> g and return g
 
186
        """
 
187
        try:
 
188
            g.__doc__ = f.__doc__
 
189
            g.__dict__.update(f.__dict__)
 
190
            g.__name__ = f.__name__
 
191
        except (TypeError, AttributeError):
 
192
            pass
 
193
        return g
 
194
 
 
195
    def deferredGenerator(f):
 
196
        """
 
197
        See L{waitForDeferred}.
 
198
        """
 
199
        def unwindGenerator(*args, **kwargs):
 
200
            return _deferGenerator(f(*args, **kwargs))
 
201
        return func_metamerge(f, unwindGenerator)
 
202
 
 
203
    defer.waitForDeferred = waitForDeferred
 
204
    defer.deferredGenerator = deferredGenerator
 
205
 
 
206
from twisted.internet import utils
 
207
if not hasattr(utils, "getProcessOutputAndValue"):
 
208
    from twisted.internet import reactor, protocol
 
209
    _callProtocolWithDeferred = utils._callProtocolWithDeferred
 
210
    try:
 
211
        import cStringIO as StringIO
 
212
    except ImportError:
 
213
        import StringIO
 
214
 
 
215
    class _EverythingGetter(protocol.ProcessProtocol):
 
216
 
 
217
        def __init__(self, deferred):
 
218
            self.deferred = deferred
 
219
            self.outBuf = StringIO.StringIO()
 
220
            self.errBuf = StringIO.StringIO()
 
221
            self.outReceived = self.outBuf.write
 
222
            self.errReceived = self.errBuf.write
 
223
 
 
224
        def processEnded(self, reason):
 
225
            out = self.outBuf.getvalue()
 
226
            err = self.errBuf.getvalue()
 
227
            e = reason.value
 
228
            code = e.exitCode
 
229
            if e.signal:
 
230
                self.deferred.errback((out, err, e.signal))
 
231
            else:
 
232
                self.deferred.callback((out, err, code))
 
233
 
 
234
    def getProcessOutputAndValue(executable, args=(), env={}, path='.', 
 
235
                                 reactor=reactor):
 
236
        """Spawn a process and returns a Deferred that will be called back
 
237
        with its output (from stdout and stderr) and it's exit code as (out,
 
238
        err, code) If a signal is raised, the Deferred will errback with the
 
239
        stdout and stderr up to that point, along with the signal, as (out,
 
240
        err, signalNum)
 
241
        """
 
242
        return _callProtocolWithDeferred(_EverythingGetter,
 
243
                                         executable, args, env, path,
 
244
                                         reactor)
 
245
    utils.getProcessOutputAndValue = getProcessOutputAndValue