| # |
| # SPDX-License-Identifier: GPL-2.0-only |
| # |
| |
| 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) |