| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 # | 6 # |
| 7 # Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se> | 7 # Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se> |
| 8 # Licensed to PSF under a Contributor Agreement. | 8 # Licensed to PSF under a Contributor Agreement. |
| 9 # See http://www.python.org/2.4/license for licensing details. | 9 # See http://www.python.org/2.4/license for licensing details. |
| 10 | 10 |
| 11 """Subprocress execution | 11 """Subprocress execution |
| 12 | 12 |
| 13 This module holds a subclass of subprocess.Popen with our own required | 13 This module holds a subclass of subprocess.Popen with our own required |
| 14 features. | 14 features. |
| 15 """ | 15 """ |
| 16 | 16 |
| 17 import errno | 17 import errno |
| 18 import os | 18 import os |
| 19 import pty | 19 import pty |
| 20 import select | 20 import select |
| 21 import subprocess | 21 import subprocess |
| 22 import sys | 22 import sys |
| 23 import unittest | 23 import unittest |
| 24 | 24 |
| 25 | 25 |
| 26 # Import these here so the caller does not need to import subprocess also. | |
| 27 PIPE = subprocess.PIPE | |
| 28 STDOUT = subprocess.STDOUT | |
| 29 PIPE_PTY = -3 # Pipe output through a pty | |
| 30 | |
| 31 | 26 |
| 32 class Popen(subprocess.Popen): | 27 class Popen(subprocess.Popen): |
| 33 """Like subprocess.Popen with ptys and incremental output | 28 """Like subprocess.Popen with ptys and incremental output |
| 34 | 29 |
| 35 This class deals with running a child process and filtering its output on | 30 This class deals with running a child process and filtering its output on |
| 36 both stdout and stderr while it is running. We do this so we can monitor | 31 both stdout and stderr while it is running. We do this so we can monitor |
| 37 progress, and possibly relay the output to the user if requested. | 32 progress, and possibly relay the output to the user if requested. |
| 38 | 33 |
| 39 The class is similar to subprocess.Popen, the equivalent is something like: | 34 The class is similar to subprocess.Popen, the equivalent is something like: |
| 40 | 35 |
| 41 Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 36 Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 42 | 37 |
| 43 But this class has many fewer features, and two enhancement: | 38 But this class has many fewer features, and two enhancement: |
| 44 | 39 |
| 45 1. Rather than getting the output data only at the end, this class sends it | 40 1. Rather than getting the output data only at the end, this class sends it |
| 46 to a provided operation as it arrives. | 41 to a provided operation as it arrives. |
| 47 2. We use pseudo terminals so that the child will hopefully flush its output | 42 2. We use pseudo terminals so that the child will hopefully flush its output |
| 48 to us as soon as it is produced, rather than waiting for the end of a | 43 to us as soon as it is produced, rather than waiting for the end of a |
| 49 line. | 44 line. |
| 50 | 45 |
| 51 Use CommunicateFilter() to handle output from the subprocess. | 46 Use CommunicateFilter() to handle output from the subprocess. |
| 52 | 47 |
| 53 """ | 48 """ |
| 49 PIPE_PTY = -3 |
| 54 | 50 |
| 55 def __init__(self, args, stdin=None, stdout=PIPE_PTY, stderr=PIPE_PTY, | 51 def __init__(self, args, stdin=None, stdout=PIPE_PTY, stderr=PIPE_PTY, |
| 56 shell=False, cwd=None, env=None, **kwargs): | 52 shell=False, cwd=None, env=None, **kwargs): |
| 57 """Cut-down constructor | 53 """Cut-down constructor |
| 58 | 54 |
| 59 Args: | 55 Args: |
| 60 args: Program and arguments for subprocess to execute. | 56 args: Program and arguments for subprocess to execute. |
| 61 stdin: See subprocess.Popen() | 57 stdin: See subprocess.Popen() |
| 62 stdout: See subprocess.Popen(), except that we support the sentinel | 58 stdout: See subprocess.Popen(), except that we support the sentinel |
| 63 value of cros_subprocess.PIPE_PTY. | 59 value of cros_subprocess.Popen.PIPE_PTY. |
| 64 stderr: See subprocess.Popen(), except that we support the sentinel | 60 stderr: See subprocess.Popen(), except that we support the sentinel |
| 65 value of cros_subprocess.PIPE_PTY. | 61 value of cros_subprocess.Popen.PIPE_PTY. |
| 66 shell: See subprocess.Popen() | 62 shell: See subprocess.Popen() |
| 67 cwd: Working directory to change to for subprocess, or None if none. | 63 cwd: Working directory to change to for subprocess, or None if none. |
| 68 env: Environment to use for this subprocess, or None to inherit parent. | 64 env: Environment to use for this subprocess, or None to inherit parent. |
| 69 kwargs: No other arguments are supported at the moment. Passing other | 65 kwargs: No other arguments are supported at the moment. Passing other |
| 70 arguments will cause a ValueError to be raised. | 66 arguments will cause a ValueError to be raised. |
| 71 """ | 67 """ |
| 72 stdout_pty = None | 68 stdout_pty = None |
| 73 stderr_pty = None | 69 stderr_pty = None |
| 74 | 70 |
| 75 if stdout == PIPE_PTY: | 71 if stdout == Popen.PIPE_PTY: |
| 76 stdout_pty = pty.openpty() | 72 stdout_pty = pty.openpty() |
| 77 stdout = os.fdopen(stdout_pty[1]) | 73 stdout = os.fdopen(stdout_pty[1]) |
| 78 if stderr == PIPE_PTY: | 74 if stderr == Popen.PIPE_PTY: |
| 79 stderr_pty = pty.openpty() | 75 stderr_pty = pty.openpty() |
| 80 stderr = os.fdopen(stderr_pty[1]) | 76 stderr = os.fdopen(stderr_pty[1]) |
| 81 | 77 |
| 82 super(Popen, self).__init__(args, stdin=stdin, | 78 super(Popen, self).__init__(args, stdin=stdin, |
| 83 stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, env=env, | 79 stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, env=env, |
| 84 **kwargs) | 80 **kwargs) |
| 85 | 81 |
| 86 # If we're on a PTY, we passed the slave half of the PTY to the subprocess. | 82 # If we're on a PTY, we passed the slave half of the PTY to the subprocess. |
| 87 # We want to use the master half on our end from now on. Setting this here | 83 # We want to use the master half on our end from now on. Setting this here |
| 88 # does make some assumptions about the implementation of subprocess, but | 84 # does make some assumptions about the implementation of subprocess, but |
| 89 # those assumptions are pretty minor. | 85 # those assumptions are pretty minor. |
| 90 | |
| 91 # Note that if stderr is STDOUT, then self.stderr will be set to None by | |
| 92 # this constructor. | |
| 93 if stdout_pty is not None: | 86 if stdout_pty is not None: |
| 94 self.stdout = os.fdopen(stdout_pty[0]) | 87 self.stdout = os.fdopen(stdout_pty[0]) |
| 95 if stderr_pty is not None: | 88 if stderr_pty is not None: |
| 96 self.stderr = os.fdopen(stderr_pty[0]) | 89 self.stderr = os.fdopen(stderr_pty[0]) |
| 97 | 90 |
| 98 # Insist that unit tests exist for other arguments we don't support. | 91 # Insist that unit tests exist for other arguments we don't support. |
| 99 if kwargs: | 92 if kwargs: |
| 100 raise ValueError("Unit tests do not test extra args - please add tests") | 93 raise ValueError("Unit tests do not test extra args - please add tests") |
| 101 | 94 |
| 102 def CommunicateFilter(self, output): | 95 def CommunicateFilter(self, operation): |
| 103 """Interact with process: Read data from stdout and stderr. | 96 """Interact with process: Read data from stdout and stderr. |
| 104 | 97 |
| 105 This method runs until end-of-file is reached, then waits for the | 98 This method runs until end-of-file is reached, then waits for the |
| 106 subprocess to terminate. | 99 subprocess to terminate. |
| 107 | 100 |
| 108 The output function is sent all output from the subprocess and must be | 101 The operation object is send all output from the subprocess through its |
| 109 defined like this: | 102 Output() method which must be defined like this: |
| 110 | 103 |
| 111 def Output([self,] stream, data) | 104 def Output(self, stream, data) |
| 112 Args: | 105 Args: |
| 113 stream: the stream the output was received on, which will be | 106 stream: the stream the output was received on, which will be |
| 114 sys.stdout or sys.stderr. | 107 sys.stdout or sys.stderr. |
| 115 data: a string containing the data | 108 data: a string containing the data |
| 116 | 109 |
| 117 Note: The data read is buffered in memory, so do not use this | 110 Note: The data read is buffered in memory, so do not use this |
| 118 method if the data size is large or unlimited. | 111 method if the data size is large or unlimited. |
| 119 | 112 |
| 120 Args: | 113 Args: |
| 121 output: Function to call with each fragment of output. | 114 operation: Operation to use for this subprocess. |
| 122 | 115 |
| 123 Returns: | 116 Returns: |
| 124 A tuple (stdout, stderr, combined) which is the data received on | 117 A tuple (stdout, stderr, combined) which is the data received on |
| 125 stdout, stderr and the combined data (interleaved stdout and stderr). | 118 stdout, stderr and the combined data (interleaved stdout and stderr). |
| 126 | |
| 127 Note that the interleaved output will only be sensible if you have | |
| 128 set both stdout and stderr to PIPE or PIPE_PTY. Even then it depends on | |
| 129 the timing of the output in the subprocess. If a subprocess flips | |
| 130 between stdout and stderr quickly in succession, by the time we come to | |
| 131 read the output from each we may see several lines in each, and will read | |
| 132 all the stdout lines, then all the stderr lines. So the interleaving | |
| 133 may not be correct. In this case you might want to pass | |
| 134 stderr=cros_subprocess.STDOUT to the constructor. | |
| 135 | |
| 136 This feature is still useful for subprocesses where stderr is | |
| 137 rarely used and indicates an error. | |
| 138 | |
| 139 Note also that if you set stderr to STDOUT, then stderr will be empty | |
| 140 and the combined output will just be the same as stdout. | |
| 141 """ | 119 """ |
| 142 | 120 |
| 143 read_set = [] | 121 read_set = [] |
| 144 write_set = [] | 122 write_set = [] |
| 145 stdout = None # Return | 123 stdout = None # Return |
| 146 stderr = None # Return | 124 stderr = None # Return |
| 147 | 125 |
| 148 if self.stdin: | 126 if self.stdin: |
| 149 # Flush stdio buffer. This might block, if the user has | 127 # Flush stdio buffer. This might block, if the user has |
| 150 # been writing to .stdin in an uncontrolled fashion. | 128 # been writing to .stdin in an uncontrolled fashion. |
| 151 self.stdin.flush() | 129 self.stdin.flush() |
| 152 if input: | 130 if input: |
| 153 write_set.append(self.stdin) | 131 write_set.append(self.stdin) |
| 154 else: | 132 else: |
| 155 self.stdin.close() | 133 self.stdin.close() |
| 156 if self.stdout: | 134 if self.stdout: |
| 157 read_set.append(self.stdout) | 135 read_set.append(self.stdout) |
| 158 stdout = [] | 136 stdout = [] |
| 159 if self.stderr and self.stderr != self.stdout: | 137 if self.stderr: |
| 160 read_set.append(self.stderr) | 138 read_set.append(self.stderr) |
| 161 stderr = [] | 139 stderr = [] |
| 162 combined = [] | 140 combined = [] |
| 163 | 141 |
| 164 input_offset = 0 | 142 input_offset = 0 |
| 165 while read_set or write_set: | 143 while read_set or write_set: |
| 166 try: | 144 try: |
| 167 rlist, wlist, _ = select.select(read_set, write_set, []) | 145 rlist, wlist, _ = select.select(read_set, write_set, []) |
| 168 except select.error, e: | 146 except select.error, e: |
| 169 if e.args[0] == errno.EINTR: | 147 if e.args[0] == errno.EINTR: |
| (...skipping 17 matching lines...) Expand all Loading... |
| 187 try: | 165 try: |
| 188 data = os.read(self.stdout.fileno(), 1024) | 166 data = os.read(self.stdout.fileno(), 1024) |
| 189 except OSError: | 167 except OSError: |
| 190 pass | 168 pass |
| 191 if data == "": | 169 if data == "": |
| 192 self.stdout.close() | 170 self.stdout.close() |
| 193 read_set.remove(self.stdout) | 171 read_set.remove(self.stdout) |
| 194 else: | 172 else: |
| 195 stdout.append(data) | 173 stdout.append(data) |
| 196 combined.append(data) | 174 combined.append(data) |
| 197 output(sys.stdout, data) | 175 operation.Output(sys.stdout, data) |
| 198 if self.stderr in rlist: | 176 if self.stderr in rlist: |
| 199 data = "" | 177 data = "" |
| 200 # We will get an error on read if the pty is closed | 178 # We will get an error on read if the pty is closed |
| 201 try: | 179 try: |
| 202 data = os.read(self.stderr.fileno(), 1024) | 180 data = os.read(self.stderr.fileno(), 1024) |
| 203 except OSError: | 181 except OSError: |
| 204 pass | 182 pass |
| 205 if data == "": | 183 if data == "": |
| 206 self.stderr.close() | 184 self.stderr.close() |
| 207 read_set.remove(self.stderr) | 185 read_set.remove(self.stderr) |
| 208 else: | 186 else: |
| 209 stderr.append(data) | 187 stderr.append(data) |
| 210 combined.append(data) | 188 combined.append(data) |
| 211 output(sys.stderr, data) | 189 operation.Output(sys.stderr, data) |
| 212 | 190 |
| 213 # All data exchanged. Translate lists into strings. | 191 # All data exchanged. Translate lists into strings. |
| 214 if stdout is not None: | 192 if stdout is not None: |
| 215 stdout = ''.join(stdout) | 193 stdout = ''.join(stdout) |
| 216 if stderr is not None: | 194 if stderr is not None: |
| 217 stderr = ''.join(stderr) | 195 stderr = ''.join(stderr) |
| 218 combined = ''.join(combined) | 196 combined = ''.join(combined) |
| 219 | 197 |
| 220 # Translate newlines, if requested. We cannot let the file | 198 # Translate newlines, if requested. We cannot let the file |
| 221 # object do the translation: It is based on stdio, which is | 199 # object do the translation: It is based on stdio, which is |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 self.assertEqual(plist[0], oper.stdout_data) | 254 self.assertEqual(plist[0], oper.stdout_data) |
| 277 self.assertEqual(plist[1], oper.stderr_data) | 255 self.assertEqual(plist[1], oper.stderr_data) |
| 278 self.assertEqual(plist[2], oper.combined_data) | 256 self.assertEqual(plist[2], oper.combined_data) |
| 279 | 257 |
| 280 # The total length of stdout and stderr should equal the combined length | 258 # The total length of stdout and stderr should equal the combined length |
| 281 self.assertEqual(len(plist[0]) + len(plist[1]), len(plist[2])) | 259 self.assertEqual(len(plist[0]) + len(plist[1]), len(plist[2])) |
| 282 | 260 |
| 283 def test_simple(self): | 261 def test_simple(self): |
| 284 """Simple redirection: Get process list""" | 262 """Simple redirection: Get process list""" |
| 285 oper = TestSubprocess.MyOperation() | 263 oper = TestSubprocess.MyOperation() |
| 286 plist = Popen(['ps']).CommunicateFilter(oper.Output) | 264 plist = Popen(['ps']).CommunicateFilter(oper) |
| 287 self._BasicCheck(plist, oper) | 265 self._BasicCheck(plist, oper) |
| 288 | 266 |
| 289 def test_stderr(self): | 267 def test_stderr(self): |
| 290 """Check stdout and stderr""" | 268 """Check stdout and stderr""" |
| 291 oper = TestSubprocess.MyOperation() | 269 oper = TestSubprocess.MyOperation() |
| 292 cmd = 'echo fred >/dev/stderr && false || echo bad' | 270 cmd = 'echo fred >/dev/stderr && false || echo bad' |
| 293 plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output) | 271 plist = Popen([cmd], shell=True).CommunicateFilter(oper) |
| 294 self._BasicCheck(plist, oper) | 272 self._BasicCheck(plist, oper) |
| 295 self.assertEqual(plist [0], 'bad\r\n') | 273 self.assertEqual(plist [0], 'bad\r\n') |
| 296 self.assertEqual(plist [1], 'fred\r\n') | 274 self.assertEqual(plist [1], 'fred\r\n') |
| 297 | 275 |
| 298 def test_shell(self): | 276 def test_shell(self): |
| 299 """Check with and without shell works""" | 277 """Check with and without shell works""" |
| 300 oper = TestSubprocess.MyOperation() | 278 oper = TestSubprocess.MyOperation() |
| 301 cmd = 'echo test >/dev/stderr' | 279 cmd = 'echo test >/dev/stderr' |
| 302 self.assertRaises(OSError, Popen, [cmd], shell=False) | 280 self.assertRaises(OSError, Popen, [cmd], shell=False) |
| 303 plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output) | 281 plist = Popen([cmd], shell=True).CommunicateFilter(oper) |
| 304 self._BasicCheck(plist, oper) | 282 self._BasicCheck(plist, oper) |
| 305 self.assertEqual(len(plist [0]), 0) | 283 self.assertEqual(len(plist [0]), 0) |
| 306 self.assertEqual(plist [1], 'test\r\n') | 284 self.assertEqual(plist [1], 'test\r\n') |
| 307 | 285 |
| 308 def test_list_args(self): | 286 def test_list_args(self): |
| 309 """Check with and without shell works using list arguments""" | 287 """Check with and without shell works using list arguments""" |
| 310 oper = TestSubprocess.MyOperation() | 288 oper = TestSubprocess.MyOperation() |
| 311 cmd = ['echo', 'test', '>/dev/stderr'] | 289 cmd = ['echo', 'test', '>/dev/stderr'] |
| 312 plist = Popen(cmd, shell=False).CommunicateFilter(oper.Output) | 290 plist = Popen(cmd, shell=False).CommunicateFilter(oper) |
| 313 self._BasicCheck(plist, oper) | 291 self._BasicCheck(plist, oper) |
| 314 self.assertEqual(plist [0], ' '.join(cmd[1:]) + '\r\n') | 292 self.assertEqual(plist [0], ' '.join(cmd[1:]) + '\r\n') |
| 315 self.assertEqual(len(plist [1]), 0) | 293 self.assertEqual(len(plist [1]), 0) |
| 316 | 294 |
| 317 oper = TestSubprocess.MyOperation() | 295 oper = TestSubprocess.MyOperation() |
| 318 | 296 |
| 319 # this should be interpreted as 'echo' with the other args dropped | 297 # this should be interpreted as 'echo' with the other args dropped |
| 320 cmd = ['echo', 'test', '>/dev/stderr'] | 298 cmd = ['echo', 'test', '>/dev/stderr'] |
| 321 plist = Popen(cmd, shell=True).CommunicateFilter(oper.Output) | 299 plist = Popen(cmd, shell=True).CommunicateFilter(oper) |
| 322 self._BasicCheck(plist, oper) | 300 self._BasicCheck(plist, oper) |
| 323 self.assertEqual(plist [0], '\r\n') | 301 self.assertEqual(plist [0], '\r\n') |
| 324 | 302 |
| 325 def test_cwd(self): | 303 def test_cwd(self): |
| 326 """Check we can change directory""" | 304 """Check we can change directory""" |
| 327 for shell in (False, True): | 305 for shell in (False, True): |
| 328 oper = TestSubprocess.MyOperation() | 306 oper = TestSubprocess.MyOperation() |
| 329 plist = Popen('pwd', shell=shell, cwd='/tmp').CommunicateFilter(oper.Outpu
t) | 307 plist = Popen('pwd', shell=shell, cwd='/tmp').CommunicateFilter(oper) |
| 330 self._BasicCheck(plist, oper) | 308 self._BasicCheck(plist, oper) |
| 331 self.assertEqual(plist [0], '/tmp\r\n') | 309 self.assertEqual(plist [0], '/tmp\r\n') |
| 332 | 310 |
| 333 def test_env(self): | 311 def test_env(self): |
| 334 """Check we can change environment""" | 312 """Check we can change environment""" |
| 335 for add in (False, True): | 313 for add in (False, True): |
| 336 oper = TestSubprocess.MyOperation() | 314 oper = TestSubprocess.MyOperation() |
| 337 env = os.environ | 315 env = os.environ |
| 338 if add: | 316 if add: |
| 339 env ['FRED'] = 'fred' | 317 env ['FRED'] = 'fred' |
| 340 cmd = 'echo $FRED' | 318 cmd = 'echo $FRED' |
| 341 plist = Popen(cmd, shell=True, env=env).CommunicateFilter(oper.Output) | 319 plist = Popen(cmd, shell=True, env=env).CommunicateFilter(oper) |
| 342 self._BasicCheck(plist, oper) | 320 self._BasicCheck(plist, oper) |
| 343 self.assertEqual(plist [0], add and 'fred\r\n' or '\r\n') | 321 self.assertEqual(plist [0], add and 'fred\r\n' or '\r\n') |
| 344 | 322 |
| 345 def test_extra_args(self): | 323 def test_extra_args(self): |
| 346 """Check we can't add extra arguments""" | 324 """Check we can't add extra arguments""" |
| 347 self.assertRaises(ValueError, Popen, 'true', close_fds=False) | 325 self.assertRaises(ValueError, Popen, 'true', close_fds=False) |
| 348 | 326 |
| 349 def test_basic_input(self): | 327 def test_basic_input(self): |
| 350 """Check that incremental input works | 328 """Check that incremental input works |
| 351 | 329 |
| 352 We set up a subprocess which will prompt for name. When we see this prompt | 330 We set up a subprocess which will prompt for name. When we see this prompt |
| 353 we send the name as input to the process. It should then print the name | 331 we send the name as input to the process. It should then print the name |
| 354 properly to stdout. | 332 properly to stdout. |
| 355 """ | 333 """ |
| 356 oper = TestSubprocess.MyOperation('Flash') | 334 oper = TestSubprocess.MyOperation('Flash') |
| 357 prompt = 'What is your name?: ' | 335 prompt = 'What is your name?: ' |
| 358 cmd = 'echo -n "%s"; read name; echo Hello $name' % prompt | 336 cmd = 'echo -n "%s"; read name; echo Hello $name' % prompt |
| 359 plist = Popen([cmd], stdin=oper.stdin_read_pipe, | 337 plist = Popen([cmd], stdin=oper.stdin_read_pipe, |
| 360 shell=True).CommunicateFilter(oper.Output) | 338 shell=True).CommunicateFilter(oper) |
| 361 self._BasicCheck(plist, oper) | 339 self._BasicCheck(plist, oper) |
| 362 self.assertEqual(len(plist [1]), 0) | 340 self.assertEqual(len(plist [1]), 0) |
| 363 self.assertEqual(plist [0], prompt + 'Hello Flash\r\r\n') | 341 self.assertEqual(plist [0], prompt + 'Hello Flash\r\r\n') |
| 364 | 342 |
| 365 #TODO(sjg): Add test for passing PIPE in case underlying subprocess breaks. | 343 #TODO(sjg): Add test for passing PIPE in case underlying subprocess breaks. |
| 366 #TODO(sjg): Add test for passing a file handle also. | 344 #TODO(sjg): Add test for passing a file handle also. |
| 367 | 345 |
| 368 def test_isatty(self): | 346 def test_isatty(self): |
| 369 """Check that ptys appear as terminals to the subprocess""" | 347 """Check that ptys appear as terminals to the subprocess""" |
| 370 oper = TestSubprocess.MyOperation() | 348 oper = TestSubprocess.MyOperation() |
| 371 cmd = ('if [ -t %d ]; then echo "terminal %d" >&%d; ' | 349 cmd = ('if [ -t %d ]; then echo "terminal %d" >&%d; ' |
| 372 'else echo "not %d" >&%d; fi;') | 350 'else echo "not %d" >&%d; fi;') |
| 373 both_cmds = '' | 351 both_cmds = '' |
| 374 for fd in (1, 2): | 352 for fd in (1, 2): |
| 375 both_cmds += cmd % (fd, fd, fd, fd, fd) | 353 both_cmds += cmd % (fd, fd, fd, fd, fd) |
| 376 plist = Popen(both_cmds, shell=True).CommunicateFilter(oper.Output) | 354 plist = Popen(both_cmds, shell=True).CommunicateFilter(oper) |
| 377 self._BasicCheck(plist, oper) | 355 self._BasicCheck(plist, oper) |
| 378 self.assertEqual(plist [0], 'terminal 1\r\n') | 356 self.assertEqual(plist [0], 'terminal 1\r\n') |
| 379 self.assertEqual(plist [1], 'terminal 2\r\n') | 357 self.assertEqual(plist [1], 'terminal 2\r\n') |
| 380 | 358 |
| 381 # Now try with PIPE and make sure it is not a terminal | 359 # Now try with PIPE and make sure it is not a terminal |
| 382 oper = TestSubprocess.MyOperation() | 360 oper = TestSubprocess.MyOperation() |
| 383 plist = Popen(both_cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 361 plist = Popen(both_cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 384 shell=True).CommunicateFilter(oper.Output) | 362 shell=True).CommunicateFilter(oper) |
| 385 self._BasicCheck(plist, oper) | 363 self._BasicCheck(plist, oper) |
| 386 self.assertEqual(plist [0], 'not 1\n') | 364 self.assertEqual(plist [0], 'not 1\n') |
| 387 self.assertEqual(plist [1], 'not 2\n') | 365 self.assertEqual(plist [1], 'not 2\n') |
| 388 | 366 |
| 389 if __name__ == '__main__': | 367 if __name__ == '__main__': |
| 390 unittest.main() | 368 unittest.main() |
| OLD | NEW |