| Index: lib/cros_subprocess.py
|
| diff --git a/lib/cros_subprocess.py b/lib/cros_subprocess.py
|
| index cc1d0e938de0c16d541758d539b8bc328ec01597..95e5fb9a35370f1409d8ed6d9a801b3eb425b228 100644
|
| --- a/lib/cros_subprocess.py
|
| +++ b/lib/cros_subprocess.py
|
| @@ -23,11 +23,6 @@ import sys
|
| import unittest
|
|
|
|
|
| -# Import these here so the caller does not need to import subprocess also.
|
| -PIPE = subprocess.PIPE
|
| -STDOUT = subprocess.STDOUT
|
| -PIPE_PTY = -3 # Pipe output through a pty
|
| -
|
|
|
| class Popen(subprocess.Popen):
|
| """Like subprocess.Popen with ptys and incremental output
|
| @@ -51,6 +46,7 @@ class Popen(subprocess.Popen):
|
| Use CommunicateFilter() to handle output from the subprocess.
|
|
|
| """
|
| + PIPE_PTY = -3
|
|
|
| def __init__(self, args, stdin=None, stdout=PIPE_PTY, stderr=PIPE_PTY,
|
| shell=False, cwd=None, env=None, **kwargs):
|
| @@ -60,9 +56,9 @@ class Popen(subprocess.Popen):
|
| args: Program and arguments for subprocess to execute.
|
| stdin: See subprocess.Popen()
|
| stdout: See subprocess.Popen(), except that we support the sentinel
|
| - value of cros_subprocess.PIPE_PTY.
|
| + value of cros_subprocess.Popen.PIPE_PTY.
|
| stderr: See subprocess.Popen(), except that we support the sentinel
|
| - value of cros_subprocess.PIPE_PTY.
|
| + value of cros_subprocess.Popen.PIPE_PTY.
|
| shell: See subprocess.Popen()
|
| cwd: Working directory to change to for subprocess, or None if none.
|
| env: Environment to use for this subprocess, or None to inherit parent.
|
| @@ -72,10 +68,10 @@ class Popen(subprocess.Popen):
|
| stdout_pty = None
|
| stderr_pty = None
|
|
|
| - if stdout == PIPE_PTY:
|
| + if stdout == Popen.PIPE_PTY:
|
| stdout_pty = pty.openpty()
|
| stdout = os.fdopen(stdout_pty[1])
|
| - if stderr == PIPE_PTY:
|
| + if stderr == Popen.PIPE_PTY:
|
| stderr_pty = pty.openpty()
|
| stderr = os.fdopen(stderr_pty[1])
|
|
|
| @@ -87,9 +83,6 @@ class Popen(subprocess.Popen):
|
| # We want to use the master half on our end from now on. Setting this here
|
| # does make some assumptions about the implementation of subprocess, but
|
| # those assumptions are pretty minor.
|
| -
|
| - # Note that if stderr is STDOUT, then self.stderr will be set to None by
|
| - # this constructor.
|
| if stdout_pty is not None:
|
| self.stdout = os.fdopen(stdout_pty[0])
|
| if stderr_pty is not None:
|
| @@ -99,16 +92,16 @@ class Popen(subprocess.Popen):
|
| if kwargs:
|
| raise ValueError("Unit tests do not test extra args - please add tests")
|
|
|
| - def CommunicateFilter(self, output):
|
| + def CommunicateFilter(self, operation):
|
| """Interact with process: Read data from stdout and stderr.
|
|
|
| This method runs until end-of-file is reached, then waits for the
|
| subprocess to terminate.
|
|
|
| - The output function is sent all output from the subprocess and must be
|
| - defined like this:
|
| + The operation object is send all output from the subprocess through its
|
| + Output() method which must be defined like this:
|
|
|
| - def Output([self,] stream, data)
|
| + def Output(self, stream, data)
|
| Args:
|
| stream: the stream the output was received on, which will be
|
| sys.stdout or sys.stderr.
|
| @@ -118,26 +111,11 @@ class Popen(subprocess.Popen):
|
| method if the data size is large or unlimited.
|
|
|
| Args:
|
| - output: Function to call with each fragment of output.
|
| + operation: Operation to use for this subprocess.
|
|
|
| Returns:
|
| A tuple (stdout, stderr, combined) which is the data received on
|
| stdout, stderr and the combined data (interleaved stdout and stderr).
|
| -
|
| - Note that the interleaved output will only be sensible if you have
|
| - set both stdout and stderr to PIPE or PIPE_PTY. Even then it depends on
|
| - the timing of the output in the subprocess. If a subprocess flips
|
| - between stdout and stderr quickly in succession, by the time we come to
|
| - read the output from each we may see several lines in each, and will read
|
| - all the stdout lines, then all the stderr lines. So the interleaving
|
| - may not be correct. In this case you might want to pass
|
| - stderr=cros_subprocess.STDOUT to the constructor.
|
| -
|
| - This feature is still useful for subprocesses where stderr is
|
| - rarely used and indicates an error.
|
| -
|
| - Note also that if you set stderr to STDOUT, then stderr will be empty
|
| - and the combined output will just be the same as stdout.
|
| """
|
|
|
| read_set = []
|
| @@ -156,7 +134,7 @@ class Popen(subprocess.Popen):
|
| if self.stdout:
|
| read_set.append(self.stdout)
|
| stdout = []
|
| - if self.stderr and self.stderr != self.stdout:
|
| + if self.stderr:
|
| read_set.append(self.stderr)
|
| stderr = []
|
| combined = []
|
| @@ -194,7 +172,7 @@ class Popen(subprocess.Popen):
|
| else:
|
| stdout.append(data)
|
| combined.append(data)
|
| - output(sys.stdout, data)
|
| + operation.Output(sys.stdout, data)
|
| if self.stderr in rlist:
|
| data = ""
|
| # We will get an error on read if the pty is closed
|
| @@ -208,7 +186,7 @@ class Popen(subprocess.Popen):
|
| else:
|
| stderr.append(data)
|
| combined.append(data)
|
| - output(sys.stderr, data)
|
| + operation.Output(sys.stderr, data)
|
|
|
| # All data exchanged. Translate lists into strings.
|
| if stdout is not None:
|
| @@ -283,14 +261,14 @@ class TestSubprocess(unittest.TestCase):
|
| def test_simple(self):
|
| """Simple redirection: Get process list"""
|
| oper = TestSubprocess.MyOperation()
|
| - plist = Popen(['ps']).CommunicateFilter(oper.Output)
|
| + plist = Popen(['ps']).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
|
|
| def test_stderr(self):
|
| """Check stdout and stderr"""
|
| oper = TestSubprocess.MyOperation()
|
| cmd = 'echo fred >/dev/stderr && false || echo bad'
|
| - plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output)
|
| + plist = Popen([cmd], shell=True).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(plist [0], 'bad\r\n')
|
| self.assertEqual(plist [1], 'fred\r\n')
|
| @@ -300,7 +278,7 @@ class TestSubprocess(unittest.TestCase):
|
| oper = TestSubprocess.MyOperation()
|
| cmd = 'echo test >/dev/stderr'
|
| self.assertRaises(OSError, Popen, [cmd], shell=False)
|
| - plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output)
|
| + plist = Popen([cmd], shell=True).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(len(plist [0]), 0)
|
| self.assertEqual(plist [1], 'test\r\n')
|
| @@ -309,7 +287,7 @@ class TestSubprocess(unittest.TestCase):
|
| """Check with and without shell works using list arguments"""
|
| oper = TestSubprocess.MyOperation()
|
| cmd = ['echo', 'test', '>/dev/stderr']
|
| - plist = Popen(cmd, shell=False).CommunicateFilter(oper.Output)
|
| + plist = Popen(cmd, shell=False).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(plist [0], ' '.join(cmd[1:]) + '\r\n')
|
| self.assertEqual(len(plist [1]), 0)
|
| @@ -318,7 +296,7 @@ class TestSubprocess(unittest.TestCase):
|
|
|
| # this should be interpreted as 'echo' with the other args dropped
|
| cmd = ['echo', 'test', '>/dev/stderr']
|
| - plist = Popen(cmd, shell=True).CommunicateFilter(oper.Output)
|
| + plist = Popen(cmd, shell=True).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(plist [0], '\r\n')
|
|
|
| @@ -326,7 +304,7 @@ class TestSubprocess(unittest.TestCase):
|
| """Check we can change directory"""
|
| for shell in (False, True):
|
| oper = TestSubprocess.MyOperation()
|
| - plist = Popen('pwd', shell=shell, cwd='/tmp').CommunicateFilter(oper.Output)
|
| + plist = Popen('pwd', shell=shell, cwd='/tmp').CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(plist [0], '/tmp\r\n')
|
|
|
| @@ -338,7 +316,7 @@ class TestSubprocess(unittest.TestCase):
|
| if add:
|
| env ['FRED'] = 'fred'
|
| cmd = 'echo $FRED'
|
| - plist = Popen(cmd, shell=True, env=env).CommunicateFilter(oper.Output)
|
| + plist = Popen(cmd, shell=True, env=env).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(plist [0], add and 'fred\r\n' or '\r\n')
|
|
|
| @@ -357,7 +335,7 @@ class TestSubprocess(unittest.TestCase):
|
| prompt = 'What is your name?: '
|
| cmd = 'echo -n "%s"; read name; echo Hello $name' % prompt
|
| plist = Popen([cmd], stdin=oper.stdin_read_pipe,
|
| - shell=True).CommunicateFilter(oper.Output)
|
| + shell=True).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(len(plist [1]), 0)
|
| self.assertEqual(plist [0], prompt + 'Hello Flash\r\r\n')
|
| @@ -373,7 +351,7 @@ class TestSubprocess(unittest.TestCase):
|
| both_cmds = ''
|
| for fd in (1, 2):
|
| both_cmds += cmd % (fd, fd, fd, fd, fd)
|
| - plist = Popen(both_cmds, shell=True).CommunicateFilter(oper.Output)
|
| + plist = Popen(both_cmds, shell=True).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(plist [0], 'terminal 1\r\n')
|
| self.assertEqual(plist [1], 'terminal 2\r\n')
|
| @@ -381,7 +359,7 @@ class TestSubprocess(unittest.TestCase):
|
| # Now try with PIPE and make sure it is not a terminal
|
| oper = TestSubprocess.MyOperation()
|
| plist = Popen(both_cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
| - shell=True).CommunicateFilter(oper.Output)
|
| + shell=True).CommunicateFilter(oper)
|
| self._BasicCheck(plist, oper)
|
| self.assertEqual(plist [0], 'not 1\n')
|
| self.assertEqual(plist [1], 'not 2\n')
|
|
|