OLD | NEW |
1 # coding=utf8 | 1 # coding=utf8 |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 """Collection of subprocess wrapper functions. | 5 """Collection of subprocess wrapper functions. |
6 | 6 |
7 In theory you shouldn't need anything else in subprocess, or this module failed. | 7 In theory you shouldn't need anything else in subprocess, or this module failed. |
8 """ | 8 """ |
9 | 9 |
10 from __future__ import with_statement | 10 from __future__ import with_statement |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 | 132 |
133 | 133 |
134 class Popen(subprocess.Popen): | 134 class Popen(subprocess.Popen): |
135 """Wraps subprocess.Popen() with various workarounds. | 135 """Wraps subprocess.Popen() with various workarounds. |
136 | 136 |
137 - Forces English output since it's easier to parse the stdout if it is always | 137 - Forces English output since it's easier to parse the stdout if it is always |
138 in English. | 138 in English. |
139 - Sets shell=True on windows by default. You can override this by forcing | 139 - Sets shell=True on windows by default. You can override this by forcing |
140 shell parameter to a value. | 140 shell parameter to a value. |
141 - Adds support for VOID to not buffer when not needed. | 141 - Adds support for VOID to not buffer when not needed. |
| 142 - Adds self.start property. |
142 | 143 |
143 Note: Popen() can throw OSError when cwd or args[0] doesn't exist. Translate | 144 Note: Popen() can throw OSError when cwd or args[0] doesn't exist. Translate |
144 exceptions generated by cygwin when it fails trying to emulate fork(). | 145 exceptions generated by cygwin when it fails trying to emulate fork(). |
145 """ | 146 """ |
146 def __init__(self, args, **kwargs): | 147 def __init__(self, args, **kwargs): |
147 # Make sure we hack subprocess if necessary. | 148 # Make sure we hack subprocess if necessary. |
148 hack_subprocess() | 149 hack_subprocess() |
149 add_kill() | 150 add_kill() |
150 | 151 |
151 env = get_english_env(kwargs.get('env')) | 152 env = get_english_env(kwargs.get('env')) |
(...skipping 21 matching lines...) Expand all Loading... |
173 # Replaces VOID with handle to /dev/null. | 174 # Replaces VOID with handle to /dev/null. |
174 # Create a temporary file to workaround python's deadlock. | 175 # Create a temporary file to workaround python's deadlock. |
175 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait | 176 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait |
176 # When the pipe fills up, it will deadlock this process. Using a real | 177 # When the pipe fills up, it will deadlock this process. Using a real |
177 # file works around that issue. | 178 # file works around that issue. |
178 kwargs[stream] = open(os.devnull, 'w') | 179 kwargs[stream] = open(os.devnull, 'w') |
179 | 180 |
180 fix('stdout') | 181 fix('stdout') |
181 fix('stderr') | 182 fix('stderr') |
182 | 183 |
| 184 self.start = time.time() |
| 185 |
183 try: | 186 try: |
184 super(Popen, self).__init__(args, **kwargs) | 187 super(Popen, self).__init__(args, **kwargs) |
185 except OSError, e: | 188 except OSError, e: |
186 if e.errno == errno.EAGAIN and sys.platform == 'cygwin': | 189 if e.errno == errno.EAGAIN and sys.platform == 'cygwin': |
187 # Convert fork() emulation failure into a CygwinRebaseError(). | 190 # Convert fork() emulation failure into a CygwinRebaseError(). |
188 raise CygwinRebaseError( | 191 raise CygwinRebaseError( |
189 e.errno, | 192 e.errno, |
190 args, | 193 args, |
191 kwargs.get('cwd'), | 194 kwargs.get('cwd'), |
192 None, | 195 None, |
193 'Visit ' | 196 'Visit ' |
194 'http://code.google.com/p/chromium/wiki/CygwinDllRemappingFailure ' | 197 'http://code.google.com/p/chromium/wiki/CygwinDllRemappingFailure ' |
195 'to learn how to fix this error; you need to rebase your cygwin ' | 198 'to learn how to fix this error; you need to rebase your cygwin ' |
196 'dlls') | 199 'dlls') |
197 # Popen() can throw OSError when cwd or args[0] doesn't exist. Let it go | 200 # Popen() can throw OSError when cwd or args[0] doesn't exist. Let it go |
198 # through | 201 # through |
199 raise | 202 raise |
200 | 203 |
201 | 204 |
202 def communicate(args, timeout=None, **kwargs): | 205 def communicate(args, timeout=None, **kwargs): |
203 """Wraps subprocess.Popen().communicate(). | 206 """Wraps subprocess.Popen().communicate() and add timeout support. |
204 | 207 |
205 Returns ((stdout, stderr), returncode). | 208 Returns ((stdout, stderr), returncode). |
206 | 209 |
207 - The process will be killed after |timeout| seconds and returncode set to | 210 - The process will be killed after |timeout| seconds and returncode set to |
208 TIMED_OUT. | 211 TIMED_OUT. |
209 - Automatically passes stdin content as input so do not specify stdin=PIPE. | 212 - Automatically passes stdin content as input so do not specify stdin=PIPE. |
210 """ | 213 """ |
211 stdin = kwargs.pop('stdin', None) | 214 stdin = kwargs.pop('stdin', None) |
212 if stdin is not None: | 215 if stdin is not None: |
213 if stdin is VOID: | 216 if stdin is VOID: |
(...skipping 11 matching lines...) Expand all Loading... |
225 if stdin is not None: | 228 if stdin is not None: |
226 return proc.communicate(stdin), proc.returncode | 229 return proc.communicate(stdin), proc.returncode |
227 else: | 230 else: |
228 return proc.communicate(), proc.returncode | 231 return proc.communicate(), proc.returncode |
229 | 232 |
230 # Create a temporary file to workaround python's deadlock. | 233 # Create a temporary file to workaround python's deadlock. |
231 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait | 234 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait |
232 # When the pipe fills up, it will deadlock this process. Using a real file | 235 # When the pipe fills up, it will deadlock this process. Using a real file |
233 # works around that issue. | 236 # works around that issue. |
234 with tempfile.TemporaryFile() as buff: | 237 with tempfile.TemporaryFile() as buff: |
235 start = time.time() | |
236 kwargs['stdout'] = buff | 238 kwargs['stdout'] = buff |
237 proc = Popen(args, **kwargs) | 239 proc = Popen(args, **kwargs) |
238 if stdin is not None: | 240 if stdin is not None: |
239 proc.stdin.write(stdin) | 241 proc.stdin.write(stdin) |
240 while proc.returncode is None: | 242 while proc.returncode is None: |
241 proc.poll() | 243 proc.poll() |
242 if timeout and (time.time() - start) > timeout: | 244 if timeout and (time.time() - proc.start) > timeout: |
243 proc.kill() | 245 proc.kill() |
244 proc.wait() | 246 proc.wait() |
245 # It's -9 on linux and 1 on Windows. Standardize to TIMED_OUT. | 247 # It's -9 on linux and 1 on Windows. Standardize to TIMED_OUT. |
246 proc.returncode = TIMED_OUT | 248 proc.returncode = TIMED_OUT |
247 time.sleep(0.001) | 249 time.sleep(0.001) |
248 # Now that the process died, reset the cursor and read the file. | 250 # Now that the process died, reset the cursor and read the file. |
249 buff.seek(0) | 251 buff.seek(0) |
250 out = (buff.read(), None) | 252 out = (buff.read(), None) |
251 return out, proc.returncode | 253 return out, proc.returncode |
252 | 254 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
302 | 304 |
303 Captures stdout of a process call and returns stdout only. | 305 Captures stdout of a process call and returns stdout only. |
304 | 306 |
305 - Throws if return code is not 0. | 307 - Throws if return code is not 0. |
306 - Works even prior to python 2.7. | 308 - Works even prior to python 2.7. |
307 - Blocks stdin by default if not specified since no output will be visible. | 309 - Blocks stdin by default if not specified since no output will be visible. |
308 - As per doc, "The stdout argument is not allowed as it is used internally." | 310 - As per doc, "The stdout argument is not allowed as it is used internally." |
309 """ | 311 """ |
310 kwargs.setdefault('stdin', VOID) | 312 kwargs.setdefault('stdin', VOID) |
311 return check_call_out(args, stdout=PIPE, **kwargs)[0] | 313 return check_call_out(args, stdout=PIPE, **kwargs)[0] |
OLD | NEW |