Commit e1515d58 authored by Dylan Baker's avatar Dylan Baker

framework/exceptions.py: add a new module for unified piglit exceptions

This is the basis of providing clean, unified exception handling to
piglit, this provides a set of Exception classes and a function
decorator to be used on main functions. Together these provide well
formatted error messages that differentiate between expected failures
and unexpected failures and provide clean, readable error messages.

The goal is that this will give us the ability to get clean errors when
they are expected, and the ability to hide stack traces when they're
not.

Stack traces for unexpected errors can be turned back on by setting the
PIGLIT_DEBUG environment variable
Signed-off-by: default avatarDylan Baker <dylanx.c.baker@intel.com>
parent ab6cee32
# Copyright (c) 2015 Intel Corporation
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Exception and error classes for piglit, and exception handlers."""
from __future__ import print_function, absolute_import, division
import os
import sys
import functools
__all__ = [
'PiglitInternalError',
'PiglitFatalError',
'PiglitException',
'handler',
]
_DEBUG = bool(os.environ.get('PIGLIT_DEBUG', False))
def handler(func):
"""Decorator function for handling errors in an entry point.
This will handle expected errors (PiglitFatalError), and unexpected errors,
either PiglitInternalErrors or PiglitExceptions, as well as handling
generic Exceptions
"""
@functools.wraps(func)
def _inner(*args, **kwargs):
try:
func(*args, **kwargs)
except PiglitFatalError as e:
print('Fatal Error: {}'.format(e.message), file=sys.stderr)
sys.exit(1)
except (PiglitInternalError, PiglitException) as e:
print('Warning: An internal exception that should have '
'been handled was not. This is bug and should be reported.\n'
'BUG: {}'.format(e.message),
file=sys.stderr)
if _DEBUG:
raise e
sys.exit(1)
except Exception as e: # pylint: disable=broad-except
print('Warning: A python exception that should have '
'been handled was not. This is bug and should be reported.\n'
'BUG: {}'.format(e.message),
file=sys.stderr)
if _DEBUG:
raise e
sys.exit(1)
return _inner
class PiglitException(Exception):
"""Class for non-error exceptions.
These should *always* be caught. If this class (or any subclass) is
uncaught that is a bug in piglit.
"""
class PiglitInternalError(Exception):
"""Class for errors in piglit.
These should always be handled.
"""
class PiglitFatalError(Exception):
"""Class for errors in piglit that cannot be recovered from.
When this class (or a subclass) is raised it should be raised all the way
to the top of the program where it exits.
"""
......@@ -29,10 +29,9 @@ import time
import ConfigParser
import ctypes
import framework.core as core
from framework import core, backends, exceptions
import framework.results
import framework.profile
import framework.backends as backends
__all__ = ['run',
'resume']
......@@ -232,6 +231,7 @@ def _disable_windows_exception_messages():
ctypes.windll.kernel32.SetErrorMode(uMode)
@exceptions.handler
def run(input_):
""" Function for piglit run command
......@@ -302,6 +302,7 @@ def run(input_):
'Results have been written to ' + args.results_path)
@exceptions.handler
def resume(input_):
parser = argparse.ArgumentParser()
parser.add_argument("results_path",
......
......@@ -27,7 +27,7 @@ import os.path as path
import sys
import errno
from framework import summary, status, core, backends
from framework import summary, status, core, backends, exceptions
__all__ = [
'aggregate',
......@@ -37,6 +37,7 @@ __all__ = [
]
@exceptions.handler
def html(input_):
# Make a copy of the status text list and add all. This is used as the
# argument list for -e/--exclude
......@@ -101,6 +102,7 @@ def html(input_):
output.generate_html(args.summaryDir, args.exclude_details)
@exceptions.handler
def console(input_):
parser = argparse.ArgumentParser()
......@@ -149,6 +151,7 @@ def console(input_):
output.generate_text(args.mode or 'all')
@exceptions.handler
def csv(input_):
parser = argparse.ArgumentParser()
parser.add_argument("-o", "--output",
......@@ -181,6 +184,7 @@ def csv(input_):
write_results(sys.stdout)
@exceptions.handler
def aggregate(input_):
"""Combine files in a tests/ directory into a single results file."""
parser = argparse.ArgumentParser()
......
# Copyright (c) 2015 Intel Corporation
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Tests for the exceptions module."""
from __future__ import print_function, absolute_import, division
import nose.tools as nt
from framework.tests import utils
from framework import exceptions
@nt.raises(SystemExit)
@utils.capture_stderr
@exceptions.handler
def test_handle_PiglitFatalError():
"""exceptions.handler: Handles PiglitFatalError"""
raise exceptions.PiglitFatalError
@nt.raises(SystemExit)
@utils.capture_stderr
@exceptions.handler
def test_handle_PiglitInternalError():
"""exceptions.handler: Handles PiglitInternalError"""
raise exceptions.PiglitInternalError
@nt.raises(SystemExit)
@utils.capture_stderr
@exceptions.handler
def test_handle_PiglitException():
"""exceptions.handler: Handles PiglitException"""
raise exceptions.PiglitException
@nt.raises(SystemExit)
@utils.capture_stderr
@exceptions.handler
def test_handle_Exception():
"""exceptions.handler: Handles Exception"""
raise Exception
......@@ -341,3 +341,22 @@ def no_error(func):
"""
return not_raises(Exception)(func)
def capture_stderr(func):
"""Redirect stderr to stdout for nose capture.
It would probably be better to implement a full stderr handler as a
plugin...
"""
@functools.wraps(func)
def _inner(*args, **kwargs):
restore = sys.stderr
sys.stderr = sys.stdout
try:
func(*args, **kwargs)
finally:
sys.stderr = restore
return _inner
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment