OLD | NEW |
1 # Copyright (C) 2010 Google Inc. All rights reserved. | 1 # Copyright (C) 2010 Google Inc. All rights reserved. |
2 # | 2 # |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 def quote_data(data): | 65 def quote_data(data): |
66 txt = repr(data).replace('\\n', '\\n\n')[1:-1] | 66 txt = repr(data).replace('\\n', '\\n\n')[1:-1] |
67 lines = [] | 67 lines = [] |
68 for l in txt.splitlines(): | 68 for l in txt.splitlines(): |
69 m = _trailing_spaces_re.match(l) | 69 m = _trailing_spaces_re.match(l) |
70 if m: | 70 if m: |
71 l = m.group(1) + m.group(2).replace(' ', '\x20') | 71 l = m.group(1) + m.group(2).replace(' ', '\x20') |
72 lines.append(l) | 72 lines.append(l) |
73 return lines | 73 return lines |
74 | 74 |
| 75 |
75 class ServerProcess(object): | 76 class ServerProcess(object): |
| 77 |
76 """This class provides a wrapper around a subprocess that | 78 """This class provides a wrapper around a subprocess that |
77 implements a simple request/response usage model. The primary benefit | 79 implements a simple request/response usage model. The primary benefit |
78 is that reading responses takes a deadline, so that we don't ever block | 80 is that reading responses takes a deadline, so that we don't ever block |
79 indefinitely. The class also handles transparently restarting processes | 81 indefinitely. The class also handles transparently restarting processes |
80 as necessary to keep issuing commands.""" | 82 as necessary to keep issuing commands.""" |
81 | 83 |
82 def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False,
treat_no_data_as_crash=False, | 84 def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False,
treat_no_data_as_crash=False, |
83 logging=False): | 85 logging=False): |
84 self._port = port_obj | 86 self._port = port_obj |
85 self._name = name # Should be the command name (e.g. content_shell, ima
ge_diff) | 87 self._name = name # Should be the command name (e.g. content_shell, ima
ge_diff) |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
120 self._output = str() # bytesarray() once we require Python 2.6 | 122 self._output = str() # bytesarray() once we require Python 2.6 |
121 self._error = str() # bytesarray() once we require Python 2.6 | 123 self._error = str() # bytesarray() once we require Python 2.6 |
122 self._crashed = False | 124 self._crashed = False |
123 self.timed_out = False | 125 self.timed_out = False |
124 | 126 |
125 def process_name(self): | 127 def process_name(self): |
126 return self._name | 128 return self._name |
127 | 129 |
128 def _start(self): | 130 def _start(self): |
129 if self._proc: | 131 if self._proc: |
130 raise ValueError("%s already running" % self._name) | 132 raise ValueError('%s already running' % self._name) |
131 self._reset() | 133 self._reset() |
132 # close_fds is a workaround for http://bugs.python.org/issue2320 | 134 # close_fds is a workaround for http://bugs.python.org/issue2320 |
133 close_fds = not self._host.platform.is_win() | 135 close_fds = not self._host.platform.is_win() |
134 if self._logging: | 136 if self._logging: |
135 env_str = '' | 137 env_str = '' |
136 if self._env: | 138 if self._env: |
137 env_str += '\n'.join("%s=%s" % (k, v) for k, v in self._env.item
s()) + '\n' | 139 env_str += '\n'.join('%s=%s' % (k, v) for k, v in self._env.item
s()) + '\n' |
138 _log.info('CMD: \n%s%s\n', env_str, _quote_cmd(self._cmd)) | 140 _log.info('CMD: \n%s%s\n', env_str, _quote_cmd(self._cmd)) |
139 self._proc = self._host.executive.popen(self._cmd, stdin=self._host.exec
utive.PIPE, | 141 self._proc = self._host.executive.popen(self._cmd, stdin=self._host.exec
utive.PIPE, |
140 stdout=self._host.executive.PIPE, | 142 stdout=self._host.executive.PIPE
, |
141 stderr=self._host.executive.PIPE, | 143 stderr=self._host.executive.PIPE
, |
142 close_fds=close_fds, | 144 close_fds=close_fds, |
143 env=self._env, | 145 env=self._env, |
144 universal_newlines=self._universal_newlines) | 146 universal_newlines=self._univers
al_newlines) |
145 self._pid = self._proc.pid | 147 self._pid = self._proc.pid |
146 fd = self._proc.stdout.fileno() | 148 fd = self._proc.stdout.fileno() |
147 if not self._use_win32_apis: | 149 if not self._use_win32_apis: |
148 fl = fcntl.fcntl(fd, fcntl.F_GETFL) | 150 fl = fcntl.fcntl(fd, fcntl.F_GETFL) |
149 fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) | 151 fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) |
150 fd = self._proc.stderr.fileno() | 152 fd = self._proc.stderr.fileno() |
151 fl = fcntl.fcntl(fd, fcntl.F_GETFL) | 153 fl = fcntl.fcntl(fd, fcntl.F_GETFL) |
152 fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) | 154 fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) |
153 | 155 |
154 def _handle_possible_interrupt(self): | 156 def _handle_possible_interrupt(self): |
(...skipping 16 matching lines...) Expand all Loading... |
171 return None | 173 return None |
172 | 174 |
173 def write(self, bytes): | 175 def write(self, bytes): |
174 """Write a request to the subprocess. The subprocess is (re-)start()'ed | 176 """Write a request to the subprocess. The subprocess is (re-)start()'ed |
175 if is not already running.""" | 177 if is not already running.""" |
176 if not self._proc: | 178 if not self._proc: |
177 self._start() | 179 self._start() |
178 try: | 180 try: |
179 self._log_data(' IN', bytes) | 181 self._log_data(' IN', bytes) |
180 self._proc.stdin.write(bytes) | 182 self._proc.stdin.write(bytes) |
181 except IOError, e: | 183 except IOError as e: |
182 self.stop(0.0) | 184 self.stop(0.0) |
183 # stop() calls _reset(), so we have to set crashed to True after cal
ling stop(). | 185 # stop() calls _reset(), so we have to set crashed to True after cal
ling stop(). |
184 self._crashed = True | 186 self._crashed = True |
185 | 187 |
186 def _pop_stdout_line_if_ready(self): | 188 def _pop_stdout_line_if_ready(self): |
187 index_after_newline = self._output.find('\n') + 1 | 189 index_after_newline = self._output.find('\n') + 1 |
188 if index_after_newline > 0: | 190 if index_after_newline > 0: |
189 return self._pop_output_bytes(index_after_newline) | 191 return self._pop_output_bytes(index_after_newline) |
190 return None | 192 return None |
191 | 193 |
(...skipping 16 matching lines...) Expand all Loading... |
208 def retrieve_bytes_from_buffers(): | 210 def retrieve_bytes_from_buffers(): |
209 stdout_line = self._pop_stdout_line_if_ready() | 211 stdout_line = self._pop_stdout_line_if_ready() |
210 if stdout_line: | 212 if stdout_line: |
211 return stdout_line, None | 213 return stdout_line, None |
212 stderr_line = self._pop_stderr_line_if_ready() | 214 stderr_line = self._pop_stderr_line_if_ready() |
213 if stderr_line: | 215 if stderr_line: |
214 return None, stderr_line | 216 return None, stderr_line |
215 return None # Instructs the caller to keep waiting. | 217 return None # Instructs the caller to keep waiting. |
216 | 218 |
217 return_value = self._read(deadline, retrieve_bytes_from_buffers) | 219 return_value = self._read(deadline, retrieve_bytes_from_buffers) |
218 # FIXME: This is a bit of a hack around the fact that _read normally onl
y returns one value, but this caller wants it to return two. | 220 # FIXME: This is a bit of a hack around the fact that _read normally onl
y |
| 221 # returns one value, but this caller wants it to return two. |
219 if return_value is None: | 222 if return_value is None: |
220 return None, None | 223 return None, None |
221 return return_value | 224 return return_value |
222 | 225 |
223 def read_stdout(self, deadline, size): | 226 def read_stdout(self, deadline, size): |
224 if size <= 0: | 227 if size <= 0: |
225 raise ValueError('ServerProcess.read() called with a non-positive si
ze: %d ' % size) | 228 raise ValueError('ServerProcess.read() called with a non-positive si
ze: %d ' % size) |
226 | 229 |
227 def retrieve_bytes_from_stdout_buffer(): | 230 def retrieve_bytes_from_stdout_buffer(): |
228 if len(self._output) >= size: | 231 if len(self._output) >= size: |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 if self._proc.stdout.closed or self._proc.stderr.closed: | 264 if self._proc.stdout.closed or self._proc.stderr.closed: |
262 # If the process crashed and is using FIFOs, like Chromium Android,
the | 265 # If the process crashed and is using FIFOs, like Chromium Android,
the |
263 # stdout and stderr pipes will be closed. | 266 # stdout and stderr pipes will be closed. |
264 return | 267 return |
265 | 268 |
266 out_fd = self._proc.stdout.fileno() | 269 out_fd = self._proc.stdout.fileno() |
267 err_fd = self._proc.stderr.fileno() | 270 err_fd = self._proc.stderr.fileno() |
268 select_fds = (out_fd, err_fd) | 271 select_fds = (out_fd, err_fd) |
269 try: | 272 try: |
270 read_fds, _, _ = select.select(select_fds, [], select_fds, max(deadl
ine - time.time(), 0)) | 273 read_fds, _, _ = select.select(select_fds, [], select_fds, max(deadl
ine - time.time(), 0)) |
271 except select.error, e: | 274 except select.error as e: |
272 # We can ignore EINVAL since it's likely the process just crashed an
d we'll | 275 # We can ignore EINVAL since it's likely the process just crashed an
d we'll |
273 # figure that out the next time through the loop in _read(). | 276 # figure that out the next time through the loop in _read(). |
274 if e.args[0] == errno.EINVAL: | 277 if e.args[0] == errno.EINVAL: |
275 return | 278 return |
276 raise | 279 raise |
277 | 280 |
278 try: | 281 try: |
279 # Note that we may get no data during read() even though | 282 # Note that we may get no data during read() even though |
280 # select says we got something; see the select() man page | 283 # select says we got something; see the select() man page |
281 # on linux. I don't know if this happens on Mac OS and | 284 # on linux. I don't know if this happens on Mac OS and |
282 # other Unixen as well, but we don't bother special-casing | 285 # other Unixen as well, but we don't bother special-casing |
283 # Linux because it's relatively harmless either way. | 286 # Linux because it's relatively harmless either way. |
284 if out_fd in read_fds: | 287 if out_fd in read_fds: |
285 data = self._proc.stdout.read() | 288 data = self._proc.stdout.read() |
286 if not data and not stopping and (self._treat_no_data_as_crash o
r self._proc.poll()): | 289 if not data and not stopping and (self._treat_no_data_as_crash o
r self._proc.poll()): |
287 self._crashed = True | 290 self._crashed = True |
288 self._log_data('OUT', data) | 291 self._log_data('OUT', data) |
289 self._output += data | 292 self._output += data |
290 | 293 |
291 if err_fd in read_fds: | 294 if err_fd in read_fds: |
292 data = self._proc.stderr.read() | 295 data = self._proc.stderr.read() |
293 if not data and not stopping and (self._treat_no_data_as_crash o
r self._proc.poll()): | 296 if not data and not stopping and (self._treat_no_data_as_crash o
r self._proc.poll()): |
294 self._crashed = True | 297 self._crashed = True |
295 self._log_data('ERR', data) | 298 self._log_data('ERR', data) |
296 self._error += data | 299 self._error += data |
297 except IOError, e: | 300 except IOError as e: |
298 # We can ignore the IOErrors because we will detect if the subporces
s crashed | 301 # We can ignore the IOErrors because we will detect if the subporces
s crashed |
299 # the next time through the loop in _read() | 302 # the next time through the loop in _read() |
300 pass | 303 pass |
301 | 304 |
302 def _wait_for_data_and_update_buffers_using_win32_apis(self, deadline): | 305 def _wait_for_data_and_update_buffers_using_win32_apis(self, deadline): |
303 # See http://code.activestate.com/recipes/440554-module-to-allow-asynchr
onous-subprocess-use-on-win/ | 306 # See http://code.activestate.com/recipes/440554-module-to-allow-asynchr
onous-subprocess-use-on-win/ |
304 # and http://docs.activestate.com/activepython/2.6/pywin32/modules.html | 307 # and http://docs.activestate.com/activepython/2.6/pywin32/modules.html |
305 # for documentation on all of these win32-specific modules. | 308 # for documentation on all of these win32-specific modules. |
306 now = time.time() | 309 now = time.time() |
307 out_fh = msvcrt.get_osfhandle(self._proc.stdout.fileno()) | 310 out_fh = msvcrt.get_osfhandle(self._proc.stdout.fileno()) |
(...skipping 12 matching lines...) Expand all Loading... |
320 time.sleep(0.01) | 323 time.sleep(0.01) |
321 now = time.time() | 324 now = time.time() |
322 return | 325 return |
323 | 326 |
324 def _non_blocking_read_win32(self, handle): | 327 def _non_blocking_read_win32(self, handle): |
325 try: | 328 try: |
326 _, avail, _ = win32pipe.PeekNamedPipe(handle, 0) | 329 _, avail, _ = win32pipe.PeekNamedPipe(handle, 0) |
327 if avail > 0: | 330 if avail > 0: |
328 _, buf = win32file.ReadFile(handle, avail, None) | 331 _, buf = win32file.ReadFile(handle, avail, None) |
329 return buf | 332 return buf |
330 except Exception, e: | 333 except Exception as e: |
331 if e[0] not in (109, errno.ESHUTDOWN): # 109 == win32 ERROR_BROKEN_
PIPE | 334 if e[0] not in (109, errno.ESHUTDOWN): # 109 == win32 ERROR_BROKEN_
PIPE |
332 raise | 335 raise |
333 return None | 336 return None |
334 | 337 |
335 def has_crashed(self): | 338 def has_crashed(self): |
336 if not self._crashed and self.poll(): | 339 if not self._crashed and self.poll(): |
337 self._crashed = True | 340 self._crashed = True |
338 self._handle_possible_interrupt() | 341 self._handle_possible_interrupt() |
339 return self._crashed | 342 return self._crashed |
340 | 343 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 self._proc.wait() | 408 self._proc.wait() |
406 | 409 |
407 def replace_outputs(self, stdout, stderr): | 410 def replace_outputs(self, stdout, stderr): |
408 assert self._proc | 411 assert self._proc |
409 if stdout: | 412 if stdout: |
410 self._proc.stdout.close() | 413 self._proc.stdout.close() |
411 self._proc.stdout = stdout | 414 self._proc.stdout = stdout |
412 if stderr: | 415 if stderr: |
413 self._proc.stderr.close() | 416 self._proc.stderr.close() |
414 self._proc.stderr = stderr | 417 self._proc.stderr = stderr |
OLD | NEW |