2
Enhanced subprocess.Popen subclass, supporting:
3
* .communicate() with timeout
4
* kill/terminate/send_signal (like in Py 2.6) for Py 2.4 / 2.5
7
out, err = Popen(...).communicate(input, timeout=300)
15
if subprocess.mswindows:
17
# Python >= 2.6 should have this:
18
from _subprocess import TerminateProcess
20
# otherwise you need win32 extensions:
21
from win32process import TerminateProcess
26
class Popen(subprocess.Popen):
27
# send_signal, terminate, kill copied from Python 2.6
28
# (we want to support Python >= 2.4)
29
if subprocess.mswindows:
30
def send_signal(self, sig):
31
"""Send a signal to the process
33
if sig == signal.SIGTERM:
36
raise ValueError("Only SIGTERM is supported on Windows")
39
"""Terminates the process
41
TerminateProcess(self._handle, 1)
46
def send_signal(self, sig):
47
"""Send a signal to the process
49
os.kill(self.pid, sig)
52
"""Terminate the process with SIGTERM
54
self.send_signal(signal.SIGTERM)
57
"""Kill the process with SIGKILL
59
self.send_signal(signal.SIGKILL)
61
def communicate(self, input=None, timeout=None):
62
"""Interact with process: Send data to stdin. Read data from
63
stdout and stderr, until end-of-file is reached. Wait for
64
process to terminate. The optional input argument should be a
65
string to be sent to the child process, or None, if no data
66
should be sent to the child.
68
communicate() returns a tuple (stdout, stderr)."""
70
self.timeout = timeout
72
# Optimization: If we are only using one pipe, or no pipe at
73
# all, using select() or threads is unnecessary.
74
if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
79
self._fo_write_no_intr(self.stdin, input)
82
stdout = self._fo_read_no_intr(self.stdout)
85
stderr = self._fo_read_no_intr(self.stderr)
88
return (stdout, stderr)
90
return self._communicate(input)
92
if subprocess.mswindows:
93
def _communicate(self, input):
94
stdout = None # Return
95
stderr = None # Return
99
stdout_thread = threading.Thread(target=self._readerthread,
100
args=(self.stdout, stdout))
101
stdout_thread.setDaemon(True)
102
stdout_thread.start()
105
stderr_thread = threading.Thread(target=self._readerthread,
106
args=(self.stderr, stderr))
107
stderr_thread.setDaemon(True)
108
stderr_thread.start()
111
if input is not None:
112
self.stdin.write(input)
116
stdout_thread.join(self.timeout)
118
stderr_thread.join(self.timeout)
120
# if the threads are still alive, that means the thread join timed out
121
timed_out = (self.stdout and stdout_thread.isAlive() or
122
self.stderr and stderr_thread.isAlive())
128
# All data exchanged. Translate lists into strings.
129
if stdout is not None:
131
if stderr is not None:
134
# Translate newlines, if requested. We cannot let the file
135
# object do the translation: It is based on stdio, which is
136
# impossible to combine with select (unless forcing no
138
if self.universal_newlines and hasattr(file, 'newlines'):
140
stdout = self._translate_newlines(stdout)
142
stderr = self._translate_newlines(stderr)
144
return (stdout, stderr)
147
def _communicate(self, input):
151
stdout = None # Return
152
stderr = None # Return
155
# Flush stdio buffer. This might block, if the user has
156
# been writing to .stdin in an uncontrolled fashion.
159
write_set.append(self.stdin)
163
read_set.append(self.stdout)
166
read_set.append(self.stderr)
170
while read_set or write_set:
172
rlist, wlist, xlist = select.select(read_set, write_set, [], self.timeout)
173
except select.error, e:
174
if e.args[0] == errno.EINTR:
178
timed_out = not (rlist or wlist or xlist)
182
if self.stdin in wlist:
183
# When select has indicated that the file is writable,
184
# we can write up to PIPE_BUF bytes without risk
185
# blocking. POSIX defines PIPE_BUF >= 512
186
chunk = input[input_offset:input_offset + 512]
187
bytes_written = os.write(self.stdin.fileno(), chunk)
188
input_offset += bytes_written
189
if input_offset >= len(input):
191
write_set.remove(self.stdin)
193
if self.stdout in rlist:
194
data = os.read(self.stdout.fileno(), 1024)
197
read_set.remove(self.stdout)
200
if self.stderr in rlist:
201
data = os.read(self.stderr.fileno(), 1024)
204
read_set.remove(self.stderr)
207
# All data exchanged. Translate lists into strings.
208
if stdout is not None:
209
stdout = ''.join(stdout)
210
if stderr is not None:
211
stderr = ''.join(stderr)
213
# Translate newlines, if requested. We cannot let the file
214
# object do the translation: It is based on stdio, which is
215
# impossible to combine with select (unless forcing no
217
if self.universal_newlines and hasattr(file, 'newlines'):
219
stdout = self._translate_newlines(stdout)
221
stderr = self._translate_newlines(stderr)
227
return (stdout, stderr)
230
def exec_cmd(cmd, input=None, timeout=None):
231
p = Popen(cmd, shell=True,
232
close_fds=not subprocess.mswindows,
234
stdin=subprocess.PIPE,
235
stdout=subprocess.PIPE,
236
stderr=subprocess.PIPE)
237
data, errors = p.communicate(input, timeout=timeout)
238
return data, errors, p.returncode
241
if __name__ == '__main__':
242
print exec_cmd("python", "import time ; time.sleep(20) ; print 'never!' ;", timeout=10)
243
print exec_cmd("python", "import time ; time.sleep(20) ; print '20s gone' ;")