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