~0x44/nova/bug838466

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/internet/utils.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_iutils -*-
 
2
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
"""
 
6
Utility methods.
 
7
"""
 
8
 
 
9
import sys, warnings
 
10
 
 
11
from twisted.internet import protocol, defer
 
12
from twisted.python import failure, util as tputil
 
13
 
 
14
try:
 
15
    import cStringIO as StringIO
 
16
except ImportError:
 
17
    import StringIO
 
18
 
 
19
def _callProtocolWithDeferred(protocol, executable, args, env, path, reactor=None):
 
20
    if reactor is None:
 
21
        from twisted.internet import reactor
 
22
 
 
23
    d = defer.Deferred()
 
24
    p = protocol(d)
 
25
    reactor.spawnProcess(p, executable, (executable,)+tuple(args), env, path)
 
26
    return d
 
27
 
 
28
 
 
29
 
 
30
class _UnexpectedErrorOutput(IOError):
 
31
    """
 
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}.
 
35
 
 
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
 
38
        closed).
 
39
    """
 
40
    def __init__(self, text, processEnded):
 
41
        IOError.__init__(self, "got stderr: %r" % (text,))
 
42
        self.processEnded = processEnded
 
43
 
 
44
 
 
45
 
 
46
class _BackRelay(protocol.ProcessProtocol):
 
47
    """
 
48
    Trivial protocol for communicating with a process and turning its output
 
49
    into the result of a L{Deferred}.
 
50
 
 
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}.
 
56
 
 
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.
 
63
    """
 
64
 
 
65
    def __init__(self, deferred, errortoo=0):
 
66
        self.deferred = deferred
 
67
        self.s = StringIO.StringIO()
 
68
        if errortoo:
 
69
            self.errReceived = self.errReceivedIsGood
 
70
        else:
 
71
            self.errReceived = self.errReceivedIsBad
 
72
 
 
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))
 
78
            self.deferred = None
 
79
            self.transport.loseConnection()
 
80
 
 
81
    def errReceivedIsGood(self, text):
 
82
        self.s.write(text)
 
83
 
 
84
    def outReceived(self, text):
 
85
        self.s.write(text)
 
86
 
 
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)
 
92
 
 
93
 
 
94
 
 
95
def getProcessOutput(executable, args=(), env={}, path=None, reactor=None,
 
96
                     errortoo=0):
 
97
    """
 
98
    Spawn a process and return its output as a deferred returning a string.
 
99
 
 
100
    @param executable: The file name to run and get the output of - the
 
101
                       full path should be used.
 
102
 
 
103
    @param args: the command line arguments to pass to the process; a
 
104
                 sequence of strings. The first string should *NOT* be the
 
105
                 executable's name.
 
106
 
 
107
    @param env: the environment variables to pass to the processs; a
 
108
                dictionary of strings.
 
109
 
 
110
    @param path: the path to run the subprocess in - defaults to the
 
111
                 current directory.
 
112
 
 
113
    @param reactor: the reactor to use - defaults to the default reactor
 
114
 
 
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.
 
120
    """
 
121
    return _callProtocolWithDeferred(lambda d:
 
122
                                        _BackRelay(d, errortoo=errortoo),
 
123
                                     executable, args, env, path,
 
124
                                     reactor)
 
125
 
 
126
 
 
127
class _ValueGetter(protocol.ProcessProtocol):
 
128
 
 
129
    def __init__(self, deferred):
 
130
        self.deferred = deferred
 
131
 
 
132
    def processEnded(self, reason):
 
133
        self.deferred.callback(reason.value.exitCode)
 
134
 
 
135
 
 
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,
 
139
                                    reactor)
 
140
 
 
141
 
 
142
class _EverythingGetter(protocol.ProcessProtocol):
 
143
 
 
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
 
150
 
 
151
    def processEnded(self, reason):
 
152
        out = self.outBuf.getvalue()
 
153
        err = self.errBuf.getvalue()
 
154
        e = reason.value
 
155
        code = e.exitCode
 
156
        if e.signal:
 
157
            self.deferred.errback((out, err, e.signal))
 
158
        else:
 
159
            self.deferred.callback((out, err, code))
 
160
 
 
161
def getProcessOutputAndValue(executable, args=(), env={}, path=None,
 
162
                             reactor=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)
 
167
    """
 
168
    return _callProtocolWithDeferred(_EverythingGetter, executable, args, env, path,
 
169
                                    reactor)
 
170
 
 
171
def _resetWarningFilters(passthrough, addedFilters):
 
172
    for f in addedFilters:
 
173
        try:
 
174
            warnings.filters.remove(f)
 
175
        except ValueError:
 
176
            pass
 
177
    return passthrough
 
178
 
 
179
 
 
180
def runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw):
 
181
    """Run the function C{f}, but with some warnings suppressed.
 
182
 
 
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
 
186
    """
 
187
    for args, kwargs in suppressedWarnings:
 
188
        warnings.filterwarnings(*args, **kwargs)
 
189
    addedFilters = warnings.filters[:len(suppressedWarnings)]
 
190
    try:
 
191
        result = f(*a, **kw)
 
192
    except:
 
193
        exc_info = sys.exc_info()
 
194
        _resetWarningFilters(None, addedFilters)
 
195
        raise exc_info[0], exc_info[1], exc_info[2]
 
196
    else:
 
197
        if isinstance(result, defer.Deferred):
 
198
            result.addBoth(_resetWarningFilters, addedFilters)
 
199
        else:
 
200
            _resetWarningFilters(None, addedFilters)
 
201
        return result
 
202
 
 
203
 
 
204
def suppressWarnings(f, *suppressedWarnings):
 
205
    """
 
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.
 
209
    """
 
210
    def warningSuppressingWrapper(*a, **kw):
 
211
        return runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw)
 
212
    return tputil.mergeFunctionMetadata(f, warningSuppressingWrapper)
 
213
 
 
214
 
 
215
__all__ = [
 
216
    "runWithWarningsSuppressed", "suppressWarnings",
 
217
 
 
218
    "getProcessOutput", "getProcessValue", "getProcessOutputAndValue",
 
219
    ]