# Copyright 2020 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.
"""Converts exceptions to return codes and prints error messages.
This makes it easier to query build tables
for particular error types
as
exit codes are visible to queries
while exception stack traces are
not.
"""
import errno
import fcntl
import logging
import os
import subprocess
import sys
import traceback
from target
import FuchsiaTargetException
def _PrintException(value, trace):
"""Prints stack trace and error message for the current exception."""
traceback.print_tb(trace)
print(str(value))
def IsStdoutBlocking():
"""Returns True if sys.stdout is blocking or False if non-blocking.
sys.stdout should always be blocking. Non-blocking
is associated
with
intermittent IOErrors (crbug.com/1080858).
"""
nonblocking = fcntl.fcntl(sys.stdout, fcntl.F_GETFL) & os.O_NONBLOCK
return not nonblocking
def HandleExceptionAndReturnExitCode():
"""Maps the current exception to a return code and prints error messages.
Mapped exception types are assigned blocks of 8
return codes starting at 64.
The choice of 64
as the starting code
is based on the Advanced Bash-Scripting
Guide (
http://tldp.org/LDP/abs/html/exitcodes.html).
A generic exception
is mapped to the start of the block. More specific
exceptions are mapped to numbers inside the block.
For example, a
FuchsiaTargetException
is mapped to
return code 64, unless it involves SSH
in which case it
is mapped to
return code 65.
Exceptions
not specifically mapped go to
return code 1.
Returns the mapped
return code.
"""
(type, value, trace) = sys.exc_info()
_PrintException(value, trace)
if type
is FuchsiaTargetException:
if 'ssh' in str(value).lower():
print(
'Error: FuchsiaTargetException: SSH to Fuchsia target failed.')
return 65
return 64
elif type
is IOError:
if value.errno == errno.EAGAIN:
logging.info(
'Python print to sys.stdout probably failed')
if not IsStdoutBlocking():
logging.warn(
'sys.stdout is non-blocking')
return 73
return 72
elif type
is subprocess.CalledProcessError:
if os.path.basename(value.cmd[0]) ==
'scp':
print(
'Error: scp operation failed - %s' % str(value))
return 81
if os.path.basename(value.cmd[0]) ==
'qemu-img':
print(
'Error: qemu-img fuchsia image generation failed.')
return 82
return 80
else:
return 1