exceptions.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #
  2. # SPDX-License-Identifier: GPL-2.0-only
  3. #
  4. import inspect
  5. import traceback
  6. import bb.namedtuple_with_abc
  7. from collections import namedtuple
  8. class TracebackEntry(namedtuple.abc):
  9. """Pickleable representation of a traceback entry"""
  10. _fields = 'filename lineno function args code_context index'
  11. _header = ' File "{0.filename}", line {0.lineno}, in {0.function}{0.args}'
  12. def format(self, formatter=None):
  13. if not self.code_context:
  14. return self._header.format(self) + '\n'
  15. formatted = [self._header.format(self) + ':\n']
  16. for lineindex, line in enumerate(self.code_context):
  17. if formatter:
  18. line = formatter(line)
  19. if lineindex == self.index:
  20. formatted.append(' >%s' % line)
  21. else:
  22. formatted.append(' %s' % line)
  23. return formatted
  24. def __str__(self):
  25. return ''.join(self.format())
  26. def _get_frame_args(frame):
  27. """Get the formatted arguments and class (if available) for a frame"""
  28. arginfo = inspect.getargvalues(frame)
  29. try:
  30. if not arginfo.args:
  31. return '', None
  32. # There have been reports from the field of python 2.6 which doesn't
  33. # return a namedtuple here but simply a tuple so fallback gracefully if
  34. # args isn't present.
  35. except AttributeError:
  36. return '', None
  37. firstarg = arginfo.args[0]
  38. if firstarg == 'self':
  39. self = arginfo.locals['self']
  40. cls = self.__class__.__name__
  41. arginfo.args.pop(0)
  42. del arginfo.locals['self']
  43. else:
  44. cls = None
  45. formatted = inspect.formatargvalues(*arginfo)
  46. return formatted, cls
  47. def extract_traceback(tb, context=1):
  48. frames = inspect.getinnerframes(tb, context)
  49. for frame, filename, lineno, function, code_context, index in frames:
  50. formatted_args, cls = _get_frame_args(frame)
  51. if cls:
  52. function = '%s.%s' % (cls, function)
  53. yield TracebackEntry(filename, lineno, function, formatted_args,
  54. code_context, index)
  55. def format_extracted(extracted, formatter=None, limit=None):
  56. if limit:
  57. extracted = extracted[-limit:]
  58. formatted = []
  59. for tracebackinfo in extracted:
  60. formatted.extend(tracebackinfo.format(formatter))
  61. return formatted
  62. def format_exception(etype, value, tb, context=1, limit=None, formatter=None):
  63. formatted = ['Traceback (most recent call last):\n']
  64. if hasattr(tb, 'tb_next'):
  65. tb = extract_traceback(tb, context)
  66. formatted.extend(format_extracted(tb, formatter, limit))
  67. formatted.extend(traceback.format_exception_only(etype, value))
  68. return formatted
  69. def to_string(exc):
  70. if isinstance(exc, SystemExit):
  71. if not isinstance(exc.code, str):
  72. return 'Exited with "%d"' % exc.code
  73. return str(exc)