aboutsummaryrefslogtreecommitdiff
path: root/catapult/devil/devil/utils/cmd_helper.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/devil/devil/utils/cmd_helper.py')
-rw-r--r--catapult/devil/devil/utils/cmd_helper.py153
1 files changed, 107 insertions, 46 deletions
diff --git a/catapult/devil/devil/utils/cmd_helper.py b/catapult/devil/devil/utils/cmd_helper.py
index 3c4a06ed..634c9716 100644
--- a/catapult/devil/devil/utils/cmd_helper.py
+++ b/catapult/devil/devil/utils/cmd_helper.py
@@ -1,7 +1,6 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A wrapper for subprocess to make calling shell commands easier."""
import codecs
@@ -86,6 +85,7 @@ def ShrinkToSnippet(cmd_parts, var_name, var_value):
Returns:
A shell snippet that does not include setting the variable.
"""
+
def shrink(value):
parts = (x and SingleQuote(x) for x in value.split(var_value))
with_substitutions = ('"$%s"' % var_name).join(parts)
@@ -94,23 +94,36 @@ def ShrinkToSnippet(cmd_parts, var_name, var_value):
return ' '.join(shrink(part) for part in cmd_parts)
-def Popen(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
+def Popen(args,
+ stdin=None,
+ stdout=None,
+ stderr=None,
+ shell=None,
+ cwd=None,
+ env=None):
# preexec_fn isn't supported on windows.
if sys.platform == 'win32':
- close_fds = (stdout is None and stderr is None)
+ close_fds = (stdin is None and stdout is None and stderr is None)
preexec_fn = None
else:
close_fds = True
preexec_fn = lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)
return subprocess.Popen(
- args=args, cwd=cwd, stdout=stdout, stderr=stderr,
- shell=shell, close_fds=close_fds, env=env, preexec_fn=preexec_fn)
+ args=args,
+ cwd=cwd,
+ stdin=stdin,
+ stdout=stdout,
+ stderr=stderr,
+ shell=shell,
+ close_fds=close_fds,
+ env=env,
+ preexec_fn=preexec_fn)
def Call(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
- pipe = Popen(args, stdout=stdout, stderr=stderr, shell=shell, cwd=cwd,
- env=env)
+ pipe = Popen(
+ args, stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, env=env)
pipe.communicate()
return pipe.wait()
@@ -127,7 +140,7 @@ def RunCmd(args, cwd=None):
Returns:
Return code from the command execution.
"""
- logger.info(str(args) + ' ' + (cwd or ''))
+ logger.debug(str(args) + ' ' + (cwd or ''))
return Call(args, cwd=cwd)
@@ -167,7 +180,11 @@ def _ValidateAndLogCommand(args, cwd, shell):
return args
-def GetCmdStatusAndOutput(args, cwd=None, shell=False, env=None):
+def GetCmdStatusAndOutput(args,
+ cwd=None,
+ shell=False,
+ env=None,
+ merge_stderr=False):
"""Executes a subprocess and returns its exit code and output.
Args:
@@ -179,12 +196,13 @@ def GetCmdStatusAndOutput(args, cwd=None, shell=False, env=None):
is a string and False if args is a sequence.
env: If not None, a mapping that defines environment variables for the
subprocess.
+ merge_stderr: If True, captures stderr as part of stdout.
Returns:
The 2-tuple (exit code, stdout).
"""
status, stdout, stderr = GetCmdStatusOutputAndError(
- args, cwd=cwd, shell=shell, env=env)
+ args, cwd=cwd, shell=shell, env=env, merge_stderr=merge_stderr)
if stderr:
logger.critical('STDERR: %s', stderr)
@@ -210,11 +228,20 @@ def StartCmd(args, cwd=None, shell=False, env=None):
A process handle from subprocess.Popen.
"""
_ValidateAndLogCommand(args, cwd, shell)
- return Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- shell=shell, cwd=cwd, env=env)
-
-
-def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
+ return Popen(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=shell,
+ cwd=cwd,
+ env=env)
+
+
+def GetCmdStatusOutputAndError(args,
+ cwd=None,
+ shell=False,
+ env=None,
+ merge_stderr=False):
"""Executes a subprocess and returns its exit code, output, and errors.
Args:
@@ -226,13 +253,20 @@ def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
is a string and False if args is a sequence.
env: If not None, a mapping that defines environment variables for the
subprocess.
+ merge_stderr: If True, captures stderr as part of stdout.
Returns:
The 3-tuple (exit code, stdout, stderr).
"""
_ValidateAndLogCommand(args, cwd, shell)
- pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- shell=shell, cwd=cwd, env=env)
+ stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
+ pipe = Popen(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=stderr,
+ shell=shell,
+ cwd=cwd,
+ env=env)
stdout, stderr = pipe.communicate()
return (pipe.returncode, stdout, stderr)
@@ -249,9 +283,11 @@ class TimeoutError(base_error.BaseError):
return self._output
-def _IterProcessStdoutFcntl(
- process, iter_timeout=None, timeout=None, buffer_size=4096,
- poll_interval=1):
+def _IterProcessStdoutFcntl(process,
+ iter_timeout=None,
+ timeout=None,
+ buffer_size=4096,
+ poll_interval=1):
"""An fcntl-based implementation of _IterProcessStdout."""
# pylint: disable=too-many-nested-blocks
import fcntl
@@ -272,14 +308,13 @@ def _IterProcessStdoutFcntl(
iter_end_time = time.time() + iter_timeout
if iter_end_time:
- iter_aware_poll_interval = min(
- poll_interval,
- max(0, iter_end_time - time.time()))
+ iter_aware_poll_interval = min(poll_interval,
+ max(0, iter_end_time - time.time()))
else:
iter_aware_poll_interval = poll_interval
- read_fds, _, _ = select.select(
- [child_fd], [], [], iter_aware_poll_interval)
+ read_fds, _, _ = select.select([child_fd], [], [],
+ iter_aware_poll_interval)
if child_fd in read_fds:
data = os.read(child_fd, buffer_size)
if not data:
@@ -290,8 +325,8 @@ def _IterProcessStdoutFcntl(
# If process is closed, keep checking for output data (because of timing
# issues).
while True:
- read_fds, _, _ = select.select(
- [child_fd], [], [], iter_aware_poll_interval)
+ read_fds, _, _ = select.select([child_fd], [], [],
+ iter_aware_poll_interval)
if child_fd in read_fds:
data = os.read(child_fd, buffer_size)
if data:
@@ -310,9 +345,11 @@ def _IterProcessStdoutFcntl(
process.wait()
-def _IterProcessStdoutQueue(
- process, iter_timeout=None, timeout=None, buffer_size=4096,
- poll_interval=1):
+def _IterProcessStdoutQueue(process,
+ iter_timeout=None,
+ timeout=None,
+ buffer_size=4096,
+ poll_interval=1):
"""A Queue.Queue-based implementation of _IterProcessStdout.
TODO(jbudorick): Evaluate whether this is a suitable replacement for
@@ -363,10 +400,8 @@ def _IterProcessStdoutQueue(
reader_thread.join()
-_IterProcessStdout = (
- _IterProcessStdoutQueue
- if sys.platform == 'win32'
- else _IterProcessStdoutFcntl)
+_IterProcessStdout = (_IterProcessStdoutQueue
+ if sys.platform == 'win32' else _IterProcessStdoutFcntl)
"""Iterate over a process's stdout.
This is intentionally not public.
@@ -390,8 +425,12 @@ Yields:
"""
-def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
- logfile=None, env=None):
+def GetCmdStatusAndOutputWithTimeout(args,
+ timeout,
+ cwd=None,
+ shell=False,
+ logfile=None,
+ env=None):
"""Executes a subprocess with a timeout.
Args:
@@ -414,8 +453,13 @@ def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
"""
_ValidateAndLogCommand(args, cwd, shell)
output = StringIO.StringIO()
- process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT, env=env)
+ process = Popen(
+ args,
+ cwd=cwd,
+ shell=shell,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ env=env)
try:
for data in _IterProcessStdout(process, timeout=timeout):
if logfile:
@@ -430,8 +474,13 @@ def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
return process.returncode, str_output
-def IterCmdOutputLines(args, iter_timeout=None, timeout=None, cwd=None,
- shell=False, env=None, check_status=True):
+def IterCmdOutputLines(args,
+ iter_timeout=None,
+ timeout=None,
+ cwd=None,
+ shell=False,
+ env=None,
+ check_status=True):
"""Executes a subprocess and continuously yields lines from its output.
Args:
@@ -455,13 +504,25 @@ def IterCmdOutputLines(args, iter_timeout=None, timeout=None, cwd=None,
non-zero exit status.
"""
cmd = _ValidateAndLogCommand(args, cwd, shell)
- process = Popen(args, cwd=cwd, shell=shell, env=env,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ process = Popen(
+ args,
+ cwd=cwd,
+ shell=shell,
+ env=env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
return _IterCmdOutputLines(
- process, cmd, iter_timeout=iter_timeout, timeout=timeout,
+ process,
+ cmd,
+ iter_timeout=iter_timeout,
+ timeout=timeout,
check_status=check_status)
-def _IterCmdOutputLines(process, cmd, iter_timeout=None, timeout=None,
+
+def _IterCmdOutputLines(process,
+ cmd,
+ iter_timeout=None,
+ timeout=None,
check_status=True):
buffer_output = ''
@@ -471,8 +532,8 @@ def _IterCmdOutputLines(process, cmd, iter_timeout=None, timeout=None,
iter_end = time.time() + iter_timeout
cur_iter_timeout = iter_timeout
- for data in _IterProcessStdout(process, iter_timeout=cur_iter_timeout,
- timeout=timeout):
+ for data in _IterProcessStdout(
+ process, iter_timeout=cur_iter_timeout, timeout=timeout):
if iter_timeout:
# Check whether the current iteration has timed out.
cur_iter_timeout = iter_end - time.time()