[Buildbot-commits] buildbot/buildbot twcompat.py,1.1,1.2

Brian Warner warner at users.sourceforge.net
Tue Jul 19 01:55:23 UTC 2005


Update of /cvsroot/buildbot/buildbot/buildbot
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10106/buildbot

Modified Files:
	twcompat.py 
Log Message:
Revision: arch at buildbot.sf.net--2004/buildbot--dev--0--patch-234
Creator:  Brian Warner <warner at monolith.lothar.com>

overhaul ShellCommand timeout/interrupt/cleanup, add tests

	* buildbot/slave/commands.py (ShellCommand): overhaul
	error-handling code, to try and make timeout/interrupt work
	properly, and make win32 happier
	* buildbot/test/test_slavecommand.py: clean up, stop using
	reactor.iterate, add tests for timeout and interrupt
	* buildbot/test/sleep.py: utility for a new timeout test

	* buildbot/twcompat.py: copy over twisted 1.3/2.0 compatibility
	code from the local-usebranches branch


Index: twcompat.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/twcompat.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- twcompat.py	17 May 2005 10:14:10 -0000	1.1
+++ twcompat.py	19 Jul 2005 01:55:21 -0000	1.2
@@ -1,16 +1,18 @@
 
-from twisted.python import components
+if 0:
+    print "hey python-mode, stop thinking I want 8-char indentation"
 
 """
 utilities to be compatible with both Twisted-1.3 and 2.0
 
-implements. Use this like:
-    from buildbot.tcompat import implements
-    class Foo:
-        if implements:
-            implements(IFoo)
-        else:
-            __implements__ = IFoo,
+implements. Use this like the following.
+
+from buildbot.tcompat import implements
+class Foo:
+    if implements:
+        implements(IFoo)
+    else:
+        __implements__ = IFoo,
         
 Interface:
     from buildbot.tcompat import Interface
@@ -21,6 +23,9 @@
     assert providedBy(obj, IFoo)
 """
 
+from twisted.copyright import version
+from twisted.python import components
+
 # does our Twisted use zope.interface?
 if hasattr(components, "interface"):
     # yes
@@ -33,3 +38,208 @@
     implements = None
     from twisted.python.components import Interface
     providedBy = components.implements
+
+# are we using a version of Trial that allows setUp/testFoo/tearDown to
+# return Deferreds?
+oldtrial = version.startswith("1.3")
+
+# use this at the end of setUp/testFoo/tearDown methods
+def maybeWait(d, timeout="none"):
+    from twisted.trial import unittest
+    if oldtrial:
+        # this is required for oldtrial (twisted-1.3.0) compatibility. When we
+        # move to retrial (twisted-2.0.0), replace these with a simple 'return
+        # d'.
+        if timeout == "none":
+            unittest.deferredResult(d)
+        else:
+            unittest.deferredResult(d, timeout)
+        return None
+    return d
+
+# waitForDeferred and getProcessOutputAndValue are twisted-2.0 things. If
+# we're running under 1.3, patch them into place. These versions are copied
+# from twisted somewhat after 2.0.1 .
+
+from twisted.internet import defer
+if not hasattr(defer, 'waitForDeferred'):
+    Deferred = defer.Deferred
+    class waitForDeferred:
+        """
+        API Stability: semi-stable
+
+        Maintainer: U{Christopher Armstrong<mailto:radix at twistedmatrix.com>}
+
+        waitForDeferred and deferredGenerator help you write
+        Deferred-using code that looks like it's blocking (but isn't
+        really), with the help of generators.
+
+        There are two important functions involved: waitForDeferred, and
+        deferredGenerator.
+
+            def thingummy():
+                thing = waitForDeferred(makeSomeRequestResultingInDeferred())
+                yield thing
+                thing = thing.getResult()
+                print thing #the result! hoorj!
+            thingummy = deferredGenerator(thingummy)
+
+        waitForDeferred returns something that you should immediately yield;
+        when your generator is resumed, calling thing.getResult() will either
+        give you the result of the Deferred if it was a success, or raise an
+        exception if it was a failure.
+
+        deferredGenerator takes one of these waitForDeferred-using
+        generator functions and converts it into a function that returns a
+        Deferred. The result of the Deferred will be the last
+        value that your generator yielded (remember that 'return result' won't
+        work; use 'yield result; return' in place of that).
+
+        Note that not yielding anything from your generator will make the
+        Deferred result in None. Yielding a Deferred from your generator
+        is also an error condition; always yield waitForDeferred(d)
+        instead.
+
+        The Deferred returned from your deferred generator may also
+        errback if your generator raised an exception.
+
+            def thingummy():
+                thing = waitForDeferred(makeSomeRequestResultingInDeferred())
+                yield thing
+                thing = thing.getResult()
+                if thing == 'I love Twisted':
+                    # will become the result of the Deferred
+                    yield 'TWISTED IS GREAT!'
+                    return
+                else:
+                    # will trigger an errback
+                    raise Exception('DESTROY ALL LIFE')
+            thingummy = deferredGenerator(thingummy)
+
+        Put succinctly, these functions connect deferred-using code with this
+        'fake blocking' style in both directions: waitForDeferred converts from
+        a Deferred to the 'blocking' style, and deferredGenerator converts from
+        the 'blocking' style to a Deferred.
+        """
+        def __init__(self, d):
+            if not isinstance(d, Deferred):
+                raise TypeError("You must give waitForDeferred a Deferred. You gave it %r." % (d,))
+            self.d = d
+
+        def getResult(self):
+            if hasattr(self, 'failure'):
+                self.failure.raiseException()
+            return self.result
+
+    def _deferGenerator(g, deferred=None, result=None):
+        """
+        See L{waitForDeferred}.
+        """
+        while 1:
+            if deferred is None:
+                deferred = defer.Deferred()
+            try:
+                result = g.next()
+            except StopIteration:
+                deferred.callback(result)
+                return deferred
+            except:
+                deferred.errback()
+                return deferred
+
+            # Deferred.callback(Deferred) raises an error; we catch this case
+            # early here and give a nicer error message to the user in case
+            # they yield a Deferred. Perhaps eventually these semantics may
+            # change.
+            if isinstance(result, defer.Deferred):
+                return fail(TypeError("Yield waitForDeferred(d), not d!"))
+
+            if isinstance(result, waitForDeferred):
+                waiting=[True, None]
+                # Pass vars in so they don't get changed going around the loop
+                def gotResult(r, waiting=waiting, result=result):
+                    result.result = r
+                    if waiting[0]:
+                        waiting[0] = False
+                        waiting[1] = r
+                    else:
+                        _deferGenerator(g, deferred, r)
+                def gotError(f, waiting=waiting, result=result):
+                    result.failure = f
+                    if waiting[0]:
+                        waiting[0] = False
+                        waiting[1] = f
+                    else:
+                        _deferGenerator(g, deferred, f)
+                result.d.addCallbacks(gotResult, gotError)
+                if waiting[0]:
+                    # Haven't called back yet, set flag so that we get reinvoked
+                    # and return from the loop
+                    waiting[0] = False
+                    return deferred
+                else:
+                    result = waiting[1]
+
+    def func_metamerge(f, g):
+        """
+        Merge function metadata from f -> g and return g
+        """
+        try:
+            g.__doc__ = f.__doc__
+            g.__dict__.update(f.__dict__)
+            g.__name__ = f.__name__
+        except (TypeError, AttributeError):
+            pass
+        return g
+
+    def deferredGenerator(f):
+        """
+        See L{waitForDeferred}.
+        """
+        def unwindGenerator(*args, **kwargs):
+            return _deferGenerator(f(*args, **kwargs))
+        return func_metamerge(f, unwindGenerator)
+
+    defer.waitForDeferred = waitForDeferred
+    defer.deferredGenerator = deferredGenerator
+
+from twisted.internet import utils
+if not hasattr(utils, "getProcessOutputAndValue"):
+    from twisted.internet import reactor, protocol
+    _callProtocolWithDeferred = utils._callProtocolWithDeferred
+    try:
+        import cStringIO as StringIO
+    except ImportError:
+        import StringIO
+
+    class _EverythingGetter(protocol.ProcessProtocol):
+
+        def __init__(self, deferred):
+            self.deferred = deferred
+            self.outBuf = StringIO.StringIO()
+            self.errBuf = StringIO.StringIO()
+            self.outReceived = self.outBuf.write
+            self.errReceived = self.errBuf.write
+
+        def processEnded(self, reason):
+            out = self.outBuf.getvalue()
+            err = self.errBuf.getvalue()
+            e = reason.value
+            code = e.exitCode
+            if e.signal:
+                self.deferred.errback((out, err, e.signal))
+            else:
+                self.deferred.callback((out, err, code))
+
+    def getProcessOutputAndValue(executable, args=(), env={}, path='.', 
+                                 reactor=reactor):
+        """Spawn a process and returns a Deferred that will be called back
+        with its output (from stdout and stderr) and it's exit code as (out,
+        err, code) If a signal is raised, the Deferred will errback with the
+        stdout and stderr up to that point, along with the signal, as (out,
+        err, signalNum)
+        """
+        return _callProtocolWithDeferred(_EverythingGetter,
+                                         executable, args, env, path,
+                                         reactor)
+    utils.getProcessOutputAndValue = getProcessOutputAndValue





More information about the Commits mailing list