1
# -*- test-case-name: twisted.test.test_iutils -*-
2
# Copyright (c) 2001-2008 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",