1 # -*- test-case-name: twisted.test.test_iutils -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
11 from twisted.internet import protocol, defer
12 from twisted.python import failure, util as tputil
15 import cStringIO as StringIO
19 def _callProtocolWithDeferred(protocol, executable, args, env, path, reactor=None):
21 from twisted.internet import reactor
25 reactor.spawnProcess(p, executable, (executable,)+tuple(args), env, path)
30 class _UnexpectedErrorOutput(IOError):
32 Standard error data was received where it was not expected. This is a
33 subclass of L{IOError} to preserve backward compatibility with the previous
34 error behavior of L{getProcessOutput}.
36 @ivar processEnded: A L{Deferred} which will fire when the process which
37 produced the data on stderr has ended (exited and all file descriptors
40 def __init__(self, text, processEnded):
41 IOError.__init__(self, "got stderr: %r" % (text,))
42 self.processEnded = processEnded
46 class _BackRelay(protocol.ProcessProtocol):
48 Trivial protocol for communicating with a process and turning its output
49 into the result of a L{Deferred}.
51 @ivar deferred: A L{Deferred} which will be called back with all of stdout
52 and, if C{errortoo} is true, all of stderr as well (mixed together in
53 one string). If C{errortoo} is false and any bytes are received over
54 stderr, this will fire with an L{_UnexpectedErrorOutput} instance and
55 the attribute will be set to C{None}.
57 @ivar onProcessEnded: If C{errortoo} is false and bytes are received over
58 stderr, this attribute will refer to a L{Deferred} which will be called
59 back when the process ends. This C{Deferred} is also associated with
60 the L{_UnexpectedErrorOutput} which C{deferred} fires with earlier in
61 this case so that users can determine when the process has actually
62 ended, in addition to knowing when bytes have been received via stderr.
65 def __init__(self, deferred, errortoo=0):
66 self.deferred = deferred
67 self.s = StringIO.StringIO()
69 self.errReceived = self.errReceivedIsGood
71 self.errReceived = self.errReceivedIsBad
73 def errReceivedIsBad(self, text):
74 if self.deferred is not None:
75 self.onProcessEnded = defer.Deferred()
76 err = _UnexpectedErrorOutput(text, self.onProcessEnded)
77 self.deferred.errback(failure.Failure(err))
79 self.transport.loseConnection()
81 def errReceivedIsGood(self, text):
84 def outReceived(self, text):
87 def processEnded(self, reason):
88 if self.deferred is not None:
89 self.deferred.callback(self.s.getvalue())
90 elif self.onProcessEnded is not None:
91 self.onProcessEnded.errback(reason)
95 def getProcessOutput(executable, args=(), env={}, path=None, reactor=None,
98 Spawn a process and return its output as a deferred returning a string.
100 @param executable: The file name to run and get the output of - the
101 full path should be used.
103 @param args: the command line arguments to pass to the process; a
104 sequence of strings. The first string should *NOT* be the
107 @param env: the environment variables to pass to the processs; a
108 dictionary of strings.
110 @param path: the path to run the subprocess in - defaults to the
113 @param reactor: the reactor to use - defaults to the default reactor
115 @param errortoo: If true, include stderr in the result. If false, if
116 stderr is received the returned L{Deferred} will errback with an
117 L{IOError} instance with a C{processEnded} attribute. The
118 C{processEnded} attribute refers to a L{Deferred} which fires when the
119 executed process ends.
121 return _callProtocolWithDeferred(lambda d:
122 _BackRelay(d, errortoo=errortoo),
123 executable, args, env, path,
127 class _ValueGetter(protocol.ProcessProtocol):
129 def __init__(self, deferred):
130 self.deferred = deferred
132 def processEnded(self, reason):
133 self.deferred.callback(reason.value.exitCode)
136 def getProcessValue(executable, args=(), env={}, path=None, reactor=None):
137 """Spawn a process and return its exit code as a Deferred."""
138 return _callProtocolWithDeferred(_ValueGetter, executable, args, env, path,
142 class _EverythingGetter(protocol.ProcessProtocol):
144 def __init__(self, deferred):
145 self.deferred = deferred
146 self.outBuf = StringIO.StringIO()
147 self.errBuf = StringIO.StringIO()
148 self.outReceived = self.outBuf.write
149 self.errReceived = self.errBuf.write
151 def processEnded(self, reason):
152 out = self.outBuf.getvalue()
153 err = self.errBuf.getvalue()
157 self.deferred.errback((out, err, e.signal))
159 self.deferred.callback((out, err, code))
161 def getProcessOutputAndValue(executable, args=(), env={}, path=None,
163 """Spawn a process and returns a Deferred that will be called back with
164 its output (from stdout and stderr) and it's exit code as (out, err, code)
165 If a signal is raised, the Deferred will errback with the stdout and
166 stderr up to that point, along with the signal, as (out, err, signalNum)
168 return _callProtocolWithDeferred(_EverythingGetter, executable, args, env, path,
171 def _resetWarningFilters(passthrough, addedFilters):
172 for f in addedFilters:
174 warnings.filters.remove(f)
180 def runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw):
181 """Run the function C{f}, but with some warnings suppressed.
183 @param suppressedWarnings: A list of arguments to pass to filterwarnings.
184 Must be a sequence of 2-tuples (args, kwargs).
185 @param f: A callable, followed by its arguments and keyword arguments
187 for args, kwargs in suppressedWarnings:
188 warnings.filterwarnings(*args, **kwargs)
189 addedFilters = warnings.filters[:len(suppressedWarnings)]
193 exc_info = sys.exc_info()
194 _resetWarningFilters(None, addedFilters)
195 raise exc_info[0], exc_info[1], exc_info[2]
197 if isinstance(result, defer.Deferred):
198 result.addBoth(_resetWarningFilters, addedFilters)
200 _resetWarningFilters(None, addedFilters)
204 def suppressWarnings(f, *suppressedWarnings):
206 Wrap C{f} in a callable which suppresses the indicated warnings before
207 invoking C{f} and unsuppresses them afterwards. If f returns a Deferred,
208 warnings will remain suppressed until the Deferred fires.
210 def warningSuppressingWrapper(*a, **kw):
211 return runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw)
212 return tputil.mergeFunctionMetadata(f, warningSuppressingWrapper)
216 "runWithWarningsSuppressed", "suppressWarnings",
218 "getProcessOutput", "getProcessValue", "getProcessOutputAndValue",