in tools/wrapper_common/execute.py [0:0]
def execute_and_filter_output(cmd_args,
filtering=None,
trim_paths=False,
custom_env=None,
inputstr=None,
print_output=False,
raise_on_failure=False,
timeout=900):
"""Execute a command with arguments, and suppress STDERR output.
Args:
cmd_args: A list of strings beginning with the command to execute followed
by its arguments.
filtering: Optionally specify a filter for stdout/stderr. It must be
callable and have the following signature: myFilter(tool_exit_status,
stdout_string, stderr_string) -> (stdout_string, stderr_string) The
filter can then use the tool's exit status to process the output as
they wish, returning what ever should be used.
trim_paths: Optionally specify whether or not to trim the current working
directory from any paths in the output. Based on output after filtering,
if a filter has been specified.
custom_env: A dictionary of custom environment variables for this session.
inputstr: Data to send directly to the child process as input.
print_output: Wheither to always print the output of stdout and stderr for
this subprocess.
raise_on_failure: Raises an exception if the subprocess does not return a
successful result.
timeout: Timeout in seconds.
Returns:
A tuple consisting of the result of running the command, stdout output from
the command as a string, and the stderr output from the command as a string.
Raises:
CalledProcessError: If the process did not indicate a successful result and
raise_on_failure is True.
"""
env = os.environ.copy()
if custom_env:
env.update(custom_env)
proc = subprocess.Popen(
cmd_args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env)
try:
stdout, stderr = proc.communicate(
input=inputstr,
timeout=timeout,
)
except subprocess.TimeoutExpired:
proc.kill()
stdout, stderr = proc.communicate()
cmd_result = proc.returncode
# The invoked tools don't specify what encoding they use, so for lack of a
# better option, just use utf8 with error replacement. This will replace
# incorrect utf8 byte sequences with '?', which avoids UnicodeDecodeError
# from raising.
#
# NOTE: Not using `encoding` and `errors` on `subprocess.Popen` as that also
# impacts stdin. This way the callers can control sending `bytes` or `str`
# thru as input.
stdout = stdout.decode("utf8", "replace")
stderr = stderr.decode("utf8", "replace")
if (stdout or stderr) and filtering:
if not callable(filtering):
raise TypeError("'filtering' must be callable.")
stdout, stderr = filtering(cmd_result, stdout, stderr)
if trim_paths:
stdout = _trim_paths(stdout)
stderr = _trim_paths(stderr)
if cmd_result != 0 and raise_on_failure:
# print the stdout and stderr, as the exception won't print it.
print("ERROR:{stdout}\n\n{stderr}".format(stdout=stdout, stderr=stderr))
raise subprocess.CalledProcessError(proc.returncode, cmd_args)
elif print_output:
# The default encoding of stdout/stderr is 'ascii', so we need to reopen the
# streams in utf8 mode since some messages from Apple's tools use characters
# like curly quotes.
def _ensure_utf8_encoding(s):
# Tests might hook sys.stdout/sys.stderr, so be defensive.
if (getattr(s, "encoding", "utf8") != "utf8" and
callable(getattr(s, "reconfigure", None))):
s.reconfigure(encoding="utf8")
if stdout:
_ensure_utf8_encoding(sys.stdout)
sys.stdout.write(stdout)
if stderr:
_ensure_utf8_encoding(sys.stderr)
sys.stderr.write(stderr)
return cmd_result, stdout, stderr