exceptions.py 2.8 KB

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