92 lines
2.8 KiB
Python
92 lines
2.8 KiB
Python
|
|
import inspect
|
|
import traceback
|
|
import bb.namedtuple_with_abc
|
|
from collections import namedtuple
|
|
|
|
|
|
class TracebackEntry(namedtuple.abc):
|
|
"""Pickleable representation of a traceback entry"""
|
|
_fields = 'filename lineno function args code_context index'
|
|
_header = ' File "{0.filename}", line {0.lineno}, in {0.function}{0.args}'
|
|
|
|
def format(self, formatter=None):
|
|
if not self.code_context:
|
|
return self._header.format(self) + '\n'
|
|
|
|
formatted = [self._header.format(self) + ':\n']
|
|
|
|
for lineindex, line in enumerate(self.code_context):
|
|
if formatter:
|
|
line = formatter(line)
|
|
|
|
if lineindex == self.index:
|
|
formatted.append(' >%s' % line)
|
|
else:
|
|
formatted.append(' %s' % line)
|
|
return formatted
|
|
|
|
def __str__(self):
|
|
return ''.join(self.format())
|
|
|
|
def _get_frame_args(frame):
|
|
"""Get the formatted arguments and class (if available) for a frame"""
|
|
arginfo = inspect.getargvalues(frame)
|
|
|
|
try:
|
|
if not arginfo.args:
|
|
return '', None
|
|
# There have been reports from the field of python 2.6 which doesn't
|
|
# return a namedtuple here but simply a tuple so fallback gracefully if
|
|
# args isn't present.
|
|
except AttributeError:
|
|
return '', None
|
|
|
|
firstarg = arginfo.args[0]
|
|
if firstarg == 'self':
|
|
self = arginfo.locals['self']
|
|
cls = self.__class__.__name__
|
|
|
|
arginfo.args.pop(0)
|
|
del arginfo.locals['self']
|
|
else:
|
|
cls = None
|
|
|
|
formatted = inspect.formatargvalues(*arginfo)
|
|
return formatted, cls
|
|
|
|
def extract_traceback(tb, context=1):
|
|
frames = inspect.getinnerframes(tb, context)
|
|
for frame, filename, lineno, function, code_context, index in frames:
|
|
formatted_args, cls = _get_frame_args(frame)
|
|
if cls:
|
|
function = '%s.%s' % (cls, function)
|
|
yield TracebackEntry(filename, lineno, function, formatted_args,
|
|
code_context, index)
|
|
|
|
def format_extracted(extracted, formatter=None, limit=None):
|
|
if limit:
|
|
extracted = extracted[-limit:]
|
|
|
|
formatted = []
|
|
for tracebackinfo in extracted:
|
|
formatted.extend(tracebackinfo.format(formatter))
|
|
return formatted
|
|
|
|
|
|
def format_exception(etype, value, tb, context=1, limit=None, formatter=None):
|
|
formatted = ['Traceback (most recent call last):\n']
|
|
|
|
if hasattr(tb, 'tb_next'):
|
|
tb = extract_traceback(tb, context)
|
|
|
|
formatted.extend(format_extracted(tb, formatter, limit))
|
|
formatted.extend(traceback.format_exception_only(etype, value))
|
|
return formatted
|
|
|
|
def to_string(exc):
|
|
if isinstance(exc, SystemExit):
|
|
if not isinstance(exc.code, str):
|
|
return 'Exited with "%d"' % exc.code
|
|
return str(exc)
|