Source code for slash.core.error
import sys
import traceback
import arrow
import logbook
from vintage import deprecated, get_no_deprecations_context
from io import StringIO
from ..conf import config
from ..exception_handling import is_exception_fatal, get_exception_frame_correction
from ..exceptions import FAILURE_EXCEPTION_TYPES
from ..utils.formatter import Formatter
from ..utils.python import safe_set_attribute
from ..utils.traceback_utils import distill_call_stack, distill_traceback, distill_object_attributes
_logger = logbook.Logger(__name__)
_CAPTURED_ERROR_MARKER = "__slash_captured_error__"
[docs]class Error(object):
traceback = exception_type = arg = _cached_detailed_traceback_str = None
def __init__(self, msg=None, exc_info=None, frame_correction=0):
super(Error, self).__init__()
self.time = arrow.utcnow()
self._fatal = False
self._has_custom_message = (msg is not None)
if msg is None and exc_info is not None:
msg = traceback.format_exception_only(exc_info[0], exc_info[1])[0].strip()
if not isinstance(msg, str):
self.arg = msg
msg = repr(msg)
self.message = msg
#: A string representation of the exception caught, if exists
self.exception_str = exception = None
#: A dictionary of distilled attributes of the exception object
self._exception_attributes = None
self.exc_info = exc_info
if exc_info is not None:
self.exception_type, exception, tb = exc_info # pylint: disable=unpacking-non-sequence
self.exception_str = repr(exception)
self._exception_attributes = distill_object_attributes(exception, truncate=False)
self.traceback = distill_traceback(
tb, frame_correction=get_exception_frame_correction(exception))
else:
self.traceback = distill_call_stack(frame_correction=frame_correction+4)
self._is_failure = False
self._fatal = exception is not None and is_exception_fatal(exception)
self._is_failure = isinstance(exception, FAILURE_EXCEPTION_TYPES)
@property
@deprecated(since='1.5.0', what='error.exception_attributes')
def exception_attributes(self):
return self._exception_attributes
def forget_exc_info(self):
assert hasattr(self, 'exc_info')
self.exc_info = None
for frame in self.traceback.frames:
frame.forget_python_frame()
def log_added(self):
tb = self.traceback.to_string(include_vars=config.root.log.traceback_variables)
_logger.trace('Error added: {}\n{}', self, tb, extra={'highlight': True})
def has_custom_message(self):
return self._has_custom_message
def mark_as_failure(self):
self._is_failure = True
def is_fatal(self):
return self._fatal
@property
@deprecated('Use error.exception_str', what='error.exception', since='1.2.3')
def exception(self):
return self.exception_str
[docs] def mark_fatal(self):
"""Marks this error as fatal, causing session termination
"""
self._fatal = True
return self
def is_failure(self):
return self._is_failure
@classmethod
def capture_exception(cls, exc_info=None):
if exc_info is None:
exc_info = sys.exc_info()
_, exc_value, _ = exc_info
if exc_value is None:
return None
cached = getattr(exc_value, _CAPTURED_ERROR_MARKER, None)
if cached is not None:
return cached
returned = cls(exc_info=exc_info)
safe_set_attribute(exc_value, _CAPTURED_ERROR_MARKER, returned)
return returned
@property
def cause(self):
if self.traceback is not None:
return self.traceback.cause
@property
def filename(self):
if self.traceback is not None:
return self.traceback.cause.filename
@property
def lineno(self):
"""Line number from which the error was raised
"""
if self.traceback is not None:
return self.traceback.cause.lineno
@property
def func_name(self):
"""Function name from which the error was raised
"""
if self.traceback is not None:
return self.traceback.cause.func_name
def __repr__(self):
return '<{0.__class__.__name__}: {0.message}>'.format(self)
[docs] def get_detailed_traceback_str(self):
"""Returns a formatted traceback string for the exception caught
"""
if self._cached_detailed_traceback_str is None:
stream = StringIO()
f = Formatter(stream, indentation_string=' ')
f.writeln("Traceback (most recent call last):")
with f.indented():
for frame in self.traceback.frames:
with get_no_deprecations_context():
locals_ = frame.locals
globals_ = frame.globals
f.writeln('File "{f.filename}", line {f.lineno}, in {f.func_name}:'.format(f=frame))
with f.indented():
f.writeln('>', frame.code_line.strip() or '?')
with f.indented():
for title, vars in [('globals', globals_), ('locals', locals_)]:
for index, (var_name, var_repr) in enumerate(vars.items()):
if index == 0:
f.writeln(title)
f.indent()
f.writeln(' - {}: {}'.format(var_name, var_repr['value']))
f.dedent()
self._cached_detailed_traceback_str = stream.getvalue()
return self._cached_detailed_traceback_str
def get_detailed_str(self):
return '{}*** {}'.format(
self.get_detailed_traceback_str(), self)