xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/traceback.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Extract, format and print information about Python stack traces."""
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard Workerimport collections.abc
4*cda5da8dSAndroid Build Coastguard Workerimport itertools
5*cda5da8dSAndroid Build Coastguard Workerimport linecache
6*cda5da8dSAndroid Build Coastguard Workerimport sys
7*cda5da8dSAndroid Build Coastguard Workerimport textwrap
8*cda5da8dSAndroid Build Coastguard Workerfrom contextlib import suppress
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard Worker__all__ = ['extract_stack', 'extract_tb', 'format_exception',
11*cda5da8dSAndroid Build Coastguard Worker           'format_exception_only', 'format_list', 'format_stack',
12*cda5da8dSAndroid Build Coastguard Worker           'format_tb', 'print_exc', 'format_exc', 'print_exception',
13*cda5da8dSAndroid Build Coastguard Worker           'print_last', 'print_stack', 'print_tb', 'clear_frames',
14*cda5da8dSAndroid Build Coastguard Worker           'FrameSummary', 'StackSummary', 'TracebackException',
15*cda5da8dSAndroid Build Coastguard Worker           'walk_stack', 'walk_tb']
16*cda5da8dSAndroid Build Coastguard Worker
17*cda5da8dSAndroid Build Coastguard Worker#
18*cda5da8dSAndroid Build Coastguard Worker# Formatting and printing lists of traceback lines.
19*cda5da8dSAndroid Build Coastguard Worker#
20*cda5da8dSAndroid Build Coastguard Worker
21*cda5da8dSAndroid Build Coastguard Workerdef print_list(extracted_list, file=None):
22*cda5da8dSAndroid Build Coastguard Worker    """Print the list of tuples as returned by extract_tb() or
23*cda5da8dSAndroid Build Coastguard Worker    extract_stack() as a formatted stack trace to the given file."""
24*cda5da8dSAndroid Build Coastguard Worker    if file is None:
25*cda5da8dSAndroid Build Coastguard Worker        file = sys.stderr
26*cda5da8dSAndroid Build Coastguard Worker    for item in StackSummary.from_list(extracted_list).format():
27*cda5da8dSAndroid Build Coastguard Worker        print(item, file=file, end="")
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Workerdef format_list(extracted_list):
30*cda5da8dSAndroid Build Coastguard Worker    """Format a list of tuples or FrameSummary objects for printing.
31*cda5da8dSAndroid Build Coastguard Worker
32*cda5da8dSAndroid Build Coastguard Worker    Given a list of tuples or FrameSummary objects as returned by
33*cda5da8dSAndroid Build Coastguard Worker    extract_tb() or extract_stack(), return a list of strings ready
34*cda5da8dSAndroid Build Coastguard Worker    for printing.
35*cda5da8dSAndroid Build Coastguard Worker
36*cda5da8dSAndroid Build Coastguard Worker    Each string in the resulting list corresponds to the item with the
37*cda5da8dSAndroid Build Coastguard Worker    same index in the argument list.  Each string ends in a newline;
38*cda5da8dSAndroid Build Coastguard Worker    the strings may contain internal newlines as well, for those items
39*cda5da8dSAndroid Build Coastguard Worker    whose source text line is not None.
40*cda5da8dSAndroid Build Coastguard Worker    """
41*cda5da8dSAndroid Build Coastguard Worker    return StackSummary.from_list(extracted_list).format()
42*cda5da8dSAndroid Build Coastguard Worker
43*cda5da8dSAndroid Build Coastguard Worker#
44*cda5da8dSAndroid Build Coastguard Worker# Printing and Extracting Tracebacks.
45*cda5da8dSAndroid Build Coastguard Worker#
46*cda5da8dSAndroid Build Coastguard Worker
47*cda5da8dSAndroid Build Coastguard Workerdef print_tb(tb, limit=None, file=None):
48*cda5da8dSAndroid Build Coastguard Worker    """Print up to 'limit' stack trace entries from the traceback 'tb'.
49*cda5da8dSAndroid Build Coastguard Worker
50*cda5da8dSAndroid Build Coastguard Worker    If 'limit' is omitted or None, all entries are printed.  If 'file'
51*cda5da8dSAndroid Build Coastguard Worker    is omitted or None, the output goes to sys.stderr; otherwise
52*cda5da8dSAndroid Build Coastguard Worker    'file' should be an open file or file-like object with a write()
53*cda5da8dSAndroid Build Coastguard Worker    method.
54*cda5da8dSAndroid Build Coastguard Worker    """
55*cda5da8dSAndroid Build Coastguard Worker    print_list(extract_tb(tb, limit=limit), file=file)
56*cda5da8dSAndroid Build Coastguard Worker
57*cda5da8dSAndroid Build Coastguard Workerdef format_tb(tb, limit=None):
58*cda5da8dSAndroid Build Coastguard Worker    """A shorthand for 'format_list(extract_tb(tb, limit))'."""
59*cda5da8dSAndroid Build Coastguard Worker    return extract_tb(tb, limit=limit).format()
60*cda5da8dSAndroid Build Coastguard Worker
61*cda5da8dSAndroid Build Coastguard Workerdef extract_tb(tb, limit=None):
62*cda5da8dSAndroid Build Coastguard Worker    """
63*cda5da8dSAndroid Build Coastguard Worker    Return a StackSummary object representing a list of
64*cda5da8dSAndroid Build Coastguard Worker    pre-processed entries from traceback.
65*cda5da8dSAndroid Build Coastguard Worker
66*cda5da8dSAndroid Build Coastguard Worker    This is useful for alternate formatting of stack traces.  If
67*cda5da8dSAndroid Build Coastguard Worker    'limit' is omitted or None, all entries are extracted.  A
68*cda5da8dSAndroid Build Coastguard Worker    pre-processed stack trace entry is a FrameSummary object
69*cda5da8dSAndroid Build Coastguard Worker    containing attributes filename, lineno, name, and line
70*cda5da8dSAndroid Build Coastguard Worker    representing the information that is usually printed for a stack
71*cda5da8dSAndroid Build Coastguard Worker    trace.  The line is a string with leading and trailing
72*cda5da8dSAndroid Build Coastguard Worker    whitespace stripped; if the source is not available it is None.
73*cda5da8dSAndroid Build Coastguard Worker    """
74*cda5da8dSAndroid Build Coastguard Worker    return StackSummary._extract_from_extended_frame_gen(
75*cda5da8dSAndroid Build Coastguard Worker        _walk_tb_with_full_positions(tb), limit=limit)
76*cda5da8dSAndroid Build Coastguard Worker
77*cda5da8dSAndroid Build Coastguard Worker#
78*cda5da8dSAndroid Build Coastguard Worker# Exception formatting and output.
79*cda5da8dSAndroid Build Coastguard Worker#
80*cda5da8dSAndroid Build Coastguard Worker
81*cda5da8dSAndroid Build Coastguard Worker_cause_message = (
82*cda5da8dSAndroid Build Coastguard Worker    "\nThe above exception was the direct cause "
83*cda5da8dSAndroid Build Coastguard Worker    "of the following exception:\n\n")
84*cda5da8dSAndroid Build Coastguard Worker
85*cda5da8dSAndroid Build Coastguard Worker_context_message = (
86*cda5da8dSAndroid Build Coastguard Worker    "\nDuring handling of the above exception, "
87*cda5da8dSAndroid Build Coastguard Worker    "another exception occurred:\n\n")
88*cda5da8dSAndroid Build Coastguard Worker
89*cda5da8dSAndroid Build Coastguard Worker
90*cda5da8dSAndroid Build Coastguard Workerclass _Sentinel:
91*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
92*cda5da8dSAndroid Build Coastguard Worker        return "<implicit>"
93*cda5da8dSAndroid Build Coastguard Worker
94*cda5da8dSAndroid Build Coastguard Worker_sentinel = _Sentinel()
95*cda5da8dSAndroid Build Coastguard Worker
96*cda5da8dSAndroid Build Coastguard Workerdef _parse_value_tb(exc, value, tb):
97*cda5da8dSAndroid Build Coastguard Worker    if (value is _sentinel) != (tb is _sentinel):
98*cda5da8dSAndroid Build Coastguard Worker        raise ValueError("Both or neither of value and tb must be given")
99*cda5da8dSAndroid Build Coastguard Worker    if value is tb is _sentinel:
100*cda5da8dSAndroid Build Coastguard Worker        if exc is not None:
101*cda5da8dSAndroid Build Coastguard Worker            if isinstance(exc, BaseException):
102*cda5da8dSAndroid Build Coastguard Worker                return exc, exc.__traceback__
103*cda5da8dSAndroid Build Coastguard Worker
104*cda5da8dSAndroid Build Coastguard Worker            raise TypeError(f'Exception expected for value, '
105*cda5da8dSAndroid Build Coastguard Worker                            f'{type(exc).__name__} found')
106*cda5da8dSAndroid Build Coastguard Worker        else:
107*cda5da8dSAndroid Build Coastguard Worker            return None, None
108*cda5da8dSAndroid Build Coastguard Worker    return value, tb
109*cda5da8dSAndroid Build Coastguard Worker
110*cda5da8dSAndroid Build Coastguard Worker
111*cda5da8dSAndroid Build Coastguard Workerdef print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
112*cda5da8dSAndroid Build Coastguard Worker                    file=None, chain=True):
113*cda5da8dSAndroid Build Coastguard Worker    """Print exception up to 'limit' stack trace entries from 'tb' to 'file'.
114*cda5da8dSAndroid Build Coastguard Worker
115*cda5da8dSAndroid Build Coastguard Worker    This differs from print_tb() in the following ways: (1) if
116*cda5da8dSAndroid Build Coastguard Worker    traceback is not None, it prints a header "Traceback (most recent
117*cda5da8dSAndroid Build Coastguard Worker    call last):"; (2) it prints the exception type and value after the
118*cda5da8dSAndroid Build Coastguard Worker    stack trace; (3) if type is SyntaxError and value has the
119*cda5da8dSAndroid Build Coastguard Worker    appropriate format, it prints the line where the syntax error
120*cda5da8dSAndroid Build Coastguard Worker    occurred with a caret on the next line indicating the approximate
121*cda5da8dSAndroid Build Coastguard Worker    position of the error.
122*cda5da8dSAndroid Build Coastguard Worker    """
123*cda5da8dSAndroid Build Coastguard Worker    value, tb = _parse_value_tb(exc, value, tb)
124*cda5da8dSAndroid Build Coastguard Worker    te = TracebackException(type(value), value, tb, limit=limit, compact=True)
125*cda5da8dSAndroid Build Coastguard Worker    te.print(file=file, chain=chain)
126*cda5da8dSAndroid Build Coastguard Worker
127*cda5da8dSAndroid Build Coastguard Worker
128*cda5da8dSAndroid Build Coastguard Workerdef format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
129*cda5da8dSAndroid Build Coastguard Worker                     chain=True):
130*cda5da8dSAndroid Build Coastguard Worker    """Format a stack trace and the exception information.
131*cda5da8dSAndroid Build Coastguard Worker
132*cda5da8dSAndroid Build Coastguard Worker    The arguments have the same meaning as the corresponding arguments
133*cda5da8dSAndroid Build Coastguard Worker    to print_exception().  The return value is a list of strings, each
134*cda5da8dSAndroid Build Coastguard Worker    ending in a newline and some containing internal newlines.  When
135*cda5da8dSAndroid Build Coastguard Worker    these lines are concatenated and printed, exactly the same text is
136*cda5da8dSAndroid Build Coastguard Worker    printed as does print_exception().
137*cda5da8dSAndroid Build Coastguard Worker    """
138*cda5da8dSAndroid Build Coastguard Worker    value, tb = _parse_value_tb(exc, value, tb)
139*cda5da8dSAndroid Build Coastguard Worker    te = TracebackException(type(value), value, tb, limit=limit, compact=True)
140*cda5da8dSAndroid Build Coastguard Worker    return list(te.format(chain=chain))
141*cda5da8dSAndroid Build Coastguard Worker
142*cda5da8dSAndroid Build Coastguard Worker
143*cda5da8dSAndroid Build Coastguard Workerdef format_exception_only(exc, /, value=_sentinel):
144*cda5da8dSAndroid Build Coastguard Worker    """Format the exception part of a traceback.
145*cda5da8dSAndroid Build Coastguard Worker
146*cda5da8dSAndroid Build Coastguard Worker    The return value is a list of strings, each ending in a newline.
147*cda5da8dSAndroid Build Coastguard Worker
148*cda5da8dSAndroid Build Coastguard Worker    Normally, the list contains a single string; however, for
149*cda5da8dSAndroid Build Coastguard Worker    SyntaxError exceptions, it contains several lines that (when
150*cda5da8dSAndroid Build Coastguard Worker    printed) display detailed information about where the syntax
151*cda5da8dSAndroid Build Coastguard Worker    error occurred.
152*cda5da8dSAndroid Build Coastguard Worker
153*cda5da8dSAndroid Build Coastguard Worker    The message indicating which exception occurred is always the last
154*cda5da8dSAndroid Build Coastguard Worker    string in the list.
155*cda5da8dSAndroid Build Coastguard Worker
156*cda5da8dSAndroid Build Coastguard Worker    """
157*cda5da8dSAndroid Build Coastguard Worker    if value is _sentinel:
158*cda5da8dSAndroid Build Coastguard Worker        value = exc
159*cda5da8dSAndroid Build Coastguard Worker    te = TracebackException(type(value), value, None, compact=True)
160*cda5da8dSAndroid Build Coastguard Worker    return list(te.format_exception_only())
161*cda5da8dSAndroid Build Coastguard Worker
162*cda5da8dSAndroid Build Coastguard Worker
163*cda5da8dSAndroid Build Coastguard Worker# -- not official API but folk probably use these two functions.
164*cda5da8dSAndroid Build Coastguard Worker
165*cda5da8dSAndroid Build Coastguard Workerdef _format_final_exc_line(etype, value):
166*cda5da8dSAndroid Build Coastguard Worker    valuestr = _safe_string(value, 'exception')
167*cda5da8dSAndroid Build Coastguard Worker    if value is None or not valuestr:
168*cda5da8dSAndroid Build Coastguard Worker        line = "%s\n" % etype
169*cda5da8dSAndroid Build Coastguard Worker    else:
170*cda5da8dSAndroid Build Coastguard Worker        line = "%s: %s\n" % (etype, valuestr)
171*cda5da8dSAndroid Build Coastguard Worker    return line
172*cda5da8dSAndroid Build Coastguard Worker
173*cda5da8dSAndroid Build Coastguard Workerdef _safe_string(value, what, func=str):
174*cda5da8dSAndroid Build Coastguard Worker    try:
175*cda5da8dSAndroid Build Coastguard Worker        return func(value)
176*cda5da8dSAndroid Build Coastguard Worker    except:
177*cda5da8dSAndroid Build Coastguard Worker        return f'<{what} {func.__name__}() failed>'
178*cda5da8dSAndroid Build Coastguard Worker
179*cda5da8dSAndroid Build Coastguard Worker# --
180*cda5da8dSAndroid Build Coastguard Worker
181*cda5da8dSAndroid Build Coastguard Workerdef print_exc(limit=None, file=None, chain=True):
182*cda5da8dSAndroid Build Coastguard Worker    """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'."""
183*cda5da8dSAndroid Build Coastguard Worker    print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
184*cda5da8dSAndroid Build Coastguard Worker
185*cda5da8dSAndroid Build Coastguard Workerdef format_exc(limit=None, chain=True):
186*cda5da8dSAndroid Build Coastguard Worker    """Like print_exc() but return a string."""
187*cda5da8dSAndroid Build Coastguard Worker    return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain))
188*cda5da8dSAndroid Build Coastguard Worker
189*cda5da8dSAndroid Build Coastguard Workerdef print_last(limit=None, file=None, chain=True):
190*cda5da8dSAndroid Build Coastguard Worker    """This is a shorthand for 'print_exception(sys.last_type,
191*cda5da8dSAndroid Build Coastguard Worker    sys.last_value, sys.last_traceback, limit, file)'."""
192*cda5da8dSAndroid Build Coastguard Worker    if not hasattr(sys, "last_type"):
193*cda5da8dSAndroid Build Coastguard Worker        raise ValueError("no last exception")
194*cda5da8dSAndroid Build Coastguard Worker    print_exception(sys.last_type, sys.last_value, sys.last_traceback,
195*cda5da8dSAndroid Build Coastguard Worker                    limit, file, chain)
196*cda5da8dSAndroid Build Coastguard Worker
197*cda5da8dSAndroid Build Coastguard Worker#
198*cda5da8dSAndroid Build Coastguard Worker# Printing and Extracting Stacks.
199*cda5da8dSAndroid Build Coastguard Worker#
200*cda5da8dSAndroid Build Coastguard Worker
201*cda5da8dSAndroid Build Coastguard Workerdef print_stack(f=None, limit=None, file=None):
202*cda5da8dSAndroid Build Coastguard Worker    """Print a stack trace from its invocation point.
203*cda5da8dSAndroid Build Coastguard Worker
204*cda5da8dSAndroid Build Coastguard Worker    The optional 'f' argument can be used to specify an alternate
205*cda5da8dSAndroid Build Coastguard Worker    stack frame at which to start. The optional 'limit' and 'file'
206*cda5da8dSAndroid Build Coastguard Worker    arguments have the same meaning as for print_exception().
207*cda5da8dSAndroid Build Coastguard Worker    """
208*cda5da8dSAndroid Build Coastguard Worker    if f is None:
209*cda5da8dSAndroid Build Coastguard Worker        f = sys._getframe().f_back
210*cda5da8dSAndroid Build Coastguard Worker    print_list(extract_stack(f, limit=limit), file=file)
211*cda5da8dSAndroid Build Coastguard Worker
212*cda5da8dSAndroid Build Coastguard Worker
213*cda5da8dSAndroid Build Coastguard Workerdef format_stack(f=None, limit=None):
214*cda5da8dSAndroid Build Coastguard Worker    """Shorthand for 'format_list(extract_stack(f, limit))'."""
215*cda5da8dSAndroid Build Coastguard Worker    if f is None:
216*cda5da8dSAndroid Build Coastguard Worker        f = sys._getframe().f_back
217*cda5da8dSAndroid Build Coastguard Worker    return format_list(extract_stack(f, limit=limit))
218*cda5da8dSAndroid Build Coastguard Worker
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Workerdef extract_stack(f=None, limit=None):
221*cda5da8dSAndroid Build Coastguard Worker    """Extract the raw traceback from the current stack frame.
222*cda5da8dSAndroid Build Coastguard Worker
223*cda5da8dSAndroid Build Coastguard Worker    The return value has the same format as for extract_tb().  The
224*cda5da8dSAndroid Build Coastguard Worker    optional 'f' and 'limit' arguments have the same meaning as for
225*cda5da8dSAndroid Build Coastguard Worker    print_stack().  Each item in the list is a quadruple (filename,
226*cda5da8dSAndroid Build Coastguard Worker    line number, function name, text), and the entries are in order
227*cda5da8dSAndroid Build Coastguard Worker    from oldest to newest stack frame.
228*cda5da8dSAndroid Build Coastguard Worker    """
229*cda5da8dSAndroid Build Coastguard Worker    if f is None:
230*cda5da8dSAndroid Build Coastguard Worker        f = sys._getframe().f_back
231*cda5da8dSAndroid Build Coastguard Worker    stack = StackSummary.extract(walk_stack(f), limit=limit)
232*cda5da8dSAndroid Build Coastguard Worker    stack.reverse()
233*cda5da8dSAndroid Build Coastguard Worker    return stack
234*cda5da8dSAndroid Build Coastguard Worker
235*cda5da8dSAndroid Build Coastguard Worker
236*cda5da8dSAndroid Build Coastguard Workerdef clear_frames(tb):
237*cda5da8dSAndroid Build Coastguard Worker    "Clear all references to local variables in the frames of a traceback."
238*cda5da8dSAndroid Build Coastguard Worker    while tb is not None:
239*cda5da8dSAndroid Build Coastguard Worker        try:
240*cda5da8dSAndroid Build Coastguard Worker            tb.tb_frame.clear()
241*cda5da8dSAndroid Build Coastguard Worker        except RuntimeError:
242*cda5da8dSAndroid Build Coastguard Worker            # Ignore the exception raised if the frame is still executing.
243*cda5da8dSAndroid Build Coastguard Worker            pass
244*cda5da8dSAndroid Build Coastguard Worker        tb = tb.tb_next
245*cda5da8dSAndroid Build Coastguard Worker
246*cda5da8dSAndroid Build Coastguard Worker
247*cda5da8dSAndroid Build Coastguard Workerclass FrameSummary:
248*cda5da8dSAndroid Build Coastguard Worker    """Information about a single frame from a traceback.
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker    - :attr:`filename` The filename for the frame.
251*cda5da8dSAndroid Build Coastguard Worker    - :attr:`lineno` The line within filename for the frame that was
252*cda5da8dSAndroid Build Coastguard Worker      active when the frame was captured.
253*cda5da8dSAndroid Build Coastguard Worker    - :attr:`name` The name of the function or method that was executing
254*cda5da8dSAndroid Build Coastguard Worker      when the frame was captured.
255*cda5da8dSAndroid Build Coastguard Worker    - :attr:`line` The text from the linecache module for the
256*cda5da8dSAndroid Build Coastguard Worker      of code that was running when the frame was captured.
257*cda5da8dSAndroid Build Coastguard Worker    - :attr:`locals` Either None if locals were not supplied, or a dict
258*cda5da8dSAndroid Build Coastguard Worker      mapping the name to the repr() of the variable.
259*cda5da8dSAndroid Build Coastguard Worker    """
260*cda5da8dSAndroid Build Coastguard Worker
261*cda5da8dSAndroid Build Coastguard Worker    __slots__ = ('filename', 'lineno', 'end_lineno', 'colno', 'end_colno',
262*cda5da8dSAndroid Build Coastguard Worker                 'name', '_line', 'locals')
263*cda5da8dSAndroid Build Coastguard Worker
264*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, filename, lineno, name, *, lookup_line=True,
265*cda5da8dSAndroid Build Coastguard Worker            locals=None, line=None,
266*cda5da8dSAndroid Build Coastguard Worker            end_lineno=None, colno=None, end_colno=None):
267*cda5da8dSAndroid Build Coastguard Worker        """Construct a FrameSummary.
268*cda5da8dSAndroid Build Coastguard Worker
269*cda5da8dSAndroid Build Coastguard Worker        :param lookup_line: If True, `linecache` is consulted for the source
270*cda5da8dSAndroid Build Coastguard Worker            code line. Otherwise, the line will be looked up when first needed.
271*cda5da8dSAndroid Build Coastguard Worker        :param locals: If supplied the frame locals, which will be captured as
272*cda5da8dSAndroid Build Coastguard Worker            object representations.
273*cda5da8dSAndroid Build Coastguard Worker        :param line: If provided, use this instead of looking up the line in
274*cda5da8dSAndroid Build Coastguard Worker            the linecache.
275*cda5da8dSAndroid Build Coastguard Worker        """
276*cda5da8dSAndroid Build Coastguard Worker        self.filename = filename
277*cda5da8dSAndroid Build Coastguard Worker        self.lineno = lineno
278*cda5da8dSAndroid Build Coastguard Worker        self.name = name
279*cda5da8dSAndroid Build Coastguard Worker        self._line = line
280*cda5da8dSAndroid Build Coastguard Worker        if lookup_line:
281*cda5da8dSAndroid Build Coastguard Worker            self.line
282*cda5da8dSAndroid Build Coastguard Worker        self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
283*cda5da8dSAndroid Build Coastguard Worker        self.end_lineno = end_lineno
284*cda5da8dSAndroid Build Coastguard Worker        self.colno = colno
285*cda5da8dSAndroid Build Coastguard Worker        self.end_colno = end_colno
286*cda5da8dSAndroid Build Coastguard Worker
287*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
288*cda5da8dSAndroid Build Coastguard Worker        if isinstance(other, FrameSummary):
289*cda5da8dSAndroid Build Coastguard Worker            return (self.filename == other.filename and
290*cda5da8dSAndroid Build Coastguard Worker                    self.lineno == other.lineno and
291*cda5da8dSAndroid Build Coastguard Worker                    self.name == other.name and
292*cda5da8dSAndroid Build Coastguard Worker                    self.locals == other.locals)
293*cda5da8dSAndroid Build Coastguard Worker        if isinstance(other, tuple):
294*cda5da8dSAndroid Build Coastguard Worker            return (self.filename, self.lineno, self.name, self.line) == other
295*cda5da8dSAndroid Build Coastguard Worker        return NotImplemented
296*cda5da8dSAndroid Build Coastguard Worker
297*cda5da8dSAndroid Build Coastguard Worker    def __getitem__(self, pos):
298*cda5da8dSAndroid Build Coastguard Worker        return (self.filename, self.lineno, self.name, self.line)[pos]
299*cda5da8dSAndroid Build Coastguard Worker
300*cda5da8dSAndroid Build Coastguard Worker    def __iter__(self):
301*cda5da8dSAndroid Build Coastguard Worker        return iter([self.filename, self.lineno, self.name, self.line])
302*cda5da8dSAndroid Build Coastguard Worker
303*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
304*cda5da8dSAndroid Build Coastguard Worker        return "<FrameSummary file {filename}, line {lineno} in {name}>".format(
305*cda5da8dSAndroid Build Coastguard Worker            filename=self.filename, lineno=self.lineno, name=self.name)
306*cda5da8dSAndroid Build Coastguard Worker
307*cda5da8dSAndroid Build Coastguard Worker    def __len__(self):
308*cda5da8dSAndroid Build Coastguard Worker        return 4
309*cda5da8dSAndroid Build Coastguard Worker
310*cda5da8dSAndroid Build Coastguard Worker    @property
311*cda5da8dSAndroid Build Coastguard Worker    def _original_line(self):
312*cda5da8dSAndroid Build Coastguard Worker        # Returns the line as-is from the source, without modifying whitespace.
313*cda5da8dSAndroid Build Coastguard Worker        self.line
314*cda5da8dSAndroid Build Coastguard Worker        return self._line
315*cda5da8dSAndroid Build Coastguard Worker
316*cda5da8dSAndroid Build Coastguard Worker    @property
317*cda5da8dSAndroid Build Coastguard Worker    def line(self):
318*cda5da8dSAndroid Build Coastguard Worker        if self._line is None:
319*cda5da8dSAndroid Build Coastguard Worker            if self.lineno is None:
320*cda5da8dSAndroid Build Coastguard Worker                return None
321*cda5da8dSAndroid Build Coastguard Worker            self._line = linecache.getline(self.filename, self.lineno)
322*cda5da8dSAndroid Build Coastguard Worker        return self._line.strip()
323*cda5da8dSAndroid Build Coastguard Worker
324*cda5da8dSAndroid Build Coastguard Worker
325*cda5da8dSAndroid Build Coastguard Workerdef walk_stack(f):
326*cda5da8dSAndroid Build Coastguard Worker    """Walk a stack yielding the frame and line number for each frame.
327*cda5da8dSAndroid Build Coastguard Worker
328*cda5da8dSAndroid Build Coastguard Worker    This will follow f.f_back from the given frame. If no frame is given, the
329*cda5da8dSAndroid Build Coastguard Worker    current stack is used. Usually used with StackSummary.extract.
330*cda5da8dSAndroid Build Coastguard Worker    """
331*cda5da8dSAndroid Build Coastguard Worker    if f is None:
332*cda5da8dSAndroid Build Coastguard Worker        f = sys._getframe().f_back.f_back.f_back.f_back
333*cda5da8dSAndroid Build Coastguard Worker    while f is not None:
334*cda5da8dSAndroid Build Coastguard Worker        yield f, f.f_lineno
335*cda5da8dSAndroid Build Coastguard Worker        f = f.f_back
336*cda5da8dSAndroid Build Coastguard Worker
337*cda5da8dSAndroid Build Coastguard Worker
338*cda5da8dSAndroid Build Coastguard Workerdef walk_tb(tb):
339*cda5da8dSAndroid Build Coastguard Worker    """Walk a traceback yielding the frame and line number for each frame.
340*cda5da8dSAndroid Build Coastguard Worker
341*cda5da8dSAndroid Build Coastguard Worker    This will follow tb.tb_next (and thus is in the opposite order to
342*cda5da8dSAndroid Build Coastguard Worker    walk_stack). Usually used with StackSummary.extract.
343*cda5da8dSAndroid Build Coastguard Worker    """
344*cda5da8dSAndroid Build Coastguard Worker    while tb is not None:
345*cda5da8dSAndroid Build Coastguard Worker        yield tb.tb_frame, tb.tb_lineno
346*cda5da8dSAndroid Build Coastguard Worker        tb = tb.tb_next
347*cda5da8dSAndroid Build Coastguard Worker
348*cda5da8dSAndroid Build Coastguard Worker
349*cda5da8dSAndroid Build Coastguard Workerdef _walk_tb_with_full_positions(tb):
350*cda5da8dSAndroid Build Coastguard Worker    # Internal version of walk_tb that yields full code positions including
351*cda5da8dSAndroid Build Coastguard Worker    # end line and column information.
352*cda5da8dSAndroid Build Coastguard Worker    while tb is not None:
353*cda5da8dSAndroid Build Coastguard Worker        positions = _get_code_position(tb.tb_frame.f_code, tb.tb_lasti)
354*cda5da8dSAndroid Build Coastguard Worker        # Yield tb_lineno when co_positions does not have a line number to
355*cda5da8dSAndroid Build Coastguard Worker        # maintain behavior with walk_tb.
356*cda5da8dSAndroid Build Coastguard Worker        if positions[0] is None:
357*cda5da8dSAndroid Build Coastguard Worker            yield tb.tb_frame, (tb.tb_lineno, ) + positions[1:]
358*cda5da8dSAndroid Build Coastguard Worker        else:
359*cda5da8dSAndroid Build Coastguard Worker            yield tb.tb_frame, positions
360*cda5da8dSAndroid Build Coastguard Worker        tb = tb.tb_next
361*cda5da8dSAndroid Build Coastguard Worker
362*cda5da8dSAndroid Build Coastguard Worker
363*cda5da8dSAndroid Build Coastguard Workerdef _get_code_position(code, instruction_index):
364*cda5da8dSAndroid Build Coastguard Worker    if instruction_index < 0:
365*cda5da8dSAndroid Build Coastguard Worker        return (None, None, None, None)
366*cda5da8dSAndroid Build Coastguard Worker    positions_gen = code.co_positions()
367*cda5da8dSAndroid Build Coastguard Worker    return next(itertools.islice(positions_gen, instruction_index // 2, None))
368*cda5da8dSAndroid Build Coastguard Worker
369*cda5da8dSAndroid Build Coastguard Worker
370*cda5da8dSAndroid Build Coastguard Worker_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c.
371*cda5da8dSAndroid Build Coastguard Worker
372*cda5da8dSAndroid Build Coastguard Workerclass StackSummary(list):
373*cda5da8dSAndroid Build Coastguard Worker    """A list of FrameSummary objects, representing a stack of frames."""
374*cda5da8dSAndroid Build Coastguard Worker
375*cda5da8dSAndroid Build Coastguard Worker    @classmethod
376*cda5da8dSAndroid Build Coastguard Worker    def extract(klass, frame_gen, *, limit=None, lookup_lines=True,
377*cda5da8dSAndroid Build Coastguard Worker            capture_locals=False):
378*cda5da8dSAndroid Build Coastguard Worker        """Create a StackSummary from a traceback or stack object.
379*cda5da8dSAndroid Build Coastguard Worker
380*cda5da8dSAndroid Build Coastguard Worker        :param frame_gen: A generator that yields (frame, lineno) tuples
381*cda5da8dSAndroid Build Coastguard Worker            whose summaries are to be included in the stack.
382*cda5da8dSAndroid Build Coastguard Worker        :param limit: None to include all frames or the number of frames to
383*cda5da8dSAndroid Build Coastguard Worker            include.
384*cda5da8dSAndroid Build Coastguard Worker        :param lookup_lines: If True, lookup lines for each frame immediately,
385*cda5da8dSAndroid Build Coastguard Worker            otherwise lookup is deferred until the frame is rendered.
386*cda5da8dSAndroid Build Coastguard Worker        :param capture_locals: If True, the local variables from each frame will
387*cda5da8dSAndroid Build Coastguard Worker            be captured as object representations into the FrameSummary.
388*cda5da8dSAndroid Build Coastguard Worker        """
389*cda5da8dSAndroid Build Coastguard Worker        def extended_frame_gen():
390*cda5da8dSAndroid Build Coastguard Worker            for f, lineno in frame_gen:
391*cda5da8dSAndroid Build Coastguard Worker                yield f, (lineno, None, None, None)
392*cda5da8dSAndroid Build Coastguard Worker
393*cda5da8dSAndroid Build Coastguard Worker        return klass._extract_from_extended_frame_gen(
394*cda5da8dSAndroid Build Coastguard Worker            extended_frame_gen(), limit=limit, lookup_lines=lookup_lines,
395*cda5da8dSAndroid Build Coastguard Worker            capture_locals=capture_locals)
396*cda5da8dSAndroid Build Coastguard Worker
397*cda5da8dSAndroid Build Coastguard Worker    @classmethod
398*cda5da8dSAndroid Build Coastguard Worker    def _extract_from_extended_frame_gen(klass, frame_gen, *, limit=None,
399*cda5da8dSAndroid Build Coastguard Worker            lookup_lines=True, capture_locals=False):
400*cda5da8dSAndroid Build Coastguard Worker        # Same as extract but operates on a frame generator that yields
401*cda5da8dSAndroid Build Coastguard Worker        # (frame, (lineno, end_lineno, colno, end_colno)) in the stack.
402*cda5da8dSAndroid Build Coastguard Worker        # Only lineno is required, the remaining fields can be None if the
403*cda5da8dSAndroid Build Coastguard Worker        # information is not available.
404*cda5da8dSAndroid Build Coastguard Worker        if limit is None:
405*cda5da8dSAndroid Build Coastguard Worker            limit = getattr(sys, 'tracebacklimit', None)
406*cda5da8dSAndroid Build Coastguard Worker            if limit is not None and limit < 0:
407*cda5da8dSAndroid Build Coastguard Worker                limit = 0
408*cda5da8dSAndroid Build Coastguard Worker        if limit is not None:
409*cda5da8dSAndroid Build Coastguard Worker            if limit >= 0:
410*cda5da8dSAndroid Build Coastguard Worker                frame_gen = itertools.islice(frame_gen, limit)
411*cda5da8dSAndroid Build Coastguard Worker            else:
412*cda5da8dSAndroid Build Coastguard Worker                frame_gen = collections.deque(frame_gen, maxlen=-limit)
413*cda5da8dSAndroid Build Coastguard Worker
414*cda5da8dSAndroid Build Coastguard Worker        result = klass()
415*cda5da8dSAndroid Build Coastguard Worker        fnames = set()
416*cda5da8dSAndroid Build Coastguard Worker        for f, (lineno, end_lineno, colno, end_colno) in frame_gen:
417*cda5da8dSAndroid Build Coastguard Worker            co = f.f_code
418*cda5da8dSAndroid Build Coastguard Worker            filename = co.co_filename
419*cda5da8dSAndroid Build Coastguard Worker            name = co.co_name
420*cda5da8dSAndroid Build Coastguard Worker
421*cda5da8dSAndroid Build Coastguard Worker            fnames.add(filename)
422*cda5da8dSAndroid Build Coastguard Worker            linecache.lazycache(filename, f.f_globals)
423*cda5da8dSAndroid Build Coastguard Worker            # Must defer line lookups until we have called checkcache.
424*cda5da8dSAndroid Build Coastguard Worker            if capture_locals:
425*cda5da8dSAndroid Build Coastguard Worker                f_locals = f.f_locals
426*cda5da8dSAndroid Build Coastguard Worker            else:
427*cda5da8dSAndroid Build Coastguard Worker                f_locals = None
428*cda5da8dSAndroid Build Coastguard Worker            result.append(FrameSummary(
429*cda5da8dSAndroid Build Coastguard Worker                filename, lineno, name, lookup_line=False, locals=f_locals,
430*cda5da8dSAndroid Build Coastguard Worker                end_lineno=end_lineno, colno=colno, end_colno=end_colno))
431*cda5da8dSAndroid Build Coastguard Worker        for filename in fnames:
432*cda5da8dSAndroid Build Coastguard Worker            linecache.checkcache(filename)
433*cda5da8dSAndroid Build Coastguard Worker        # If immediate lookup was desired, trigger lookups now.
434*cda5da8dSAndroid Build Coastguard Worker        if lookup_lines:
435*cda5da8dSAndroid Build Coastguard Worker            for f in result:
436*cda5da8dSAndroid Build Coastguard Worker                f.line
437*cda5da8dSAndroid Build Coastguard Worker        return result
438*cda5da8dSAndroid Build Coastguard Worker
439*cda5da8dSAndroid Build Coastguard Worker    @classmethod
440*cda5da8dSAndroid Build Coastguard Worker    def from_list(klass, a_list):
441*cda5da8dSAndroid Build Coastguard Worker        """
442*cda5da8dSAndroid Build Coastguard Worker        Create a StackSummary object from a supplied list of
443*cda5da8dSAndroid Build Coastguard Worker        FrameSummary objects or old-style list of tuples.
444*cda5da8dSAndroid Build Coastguard Worker        """
445*cda5da8dSAndroid Build Coastguard Worker        # While doing a fast-path check for isinstance(a_list, StackSummary) is
446*cda5da8dSAndroid Build Coastguard Worker        # appealing, idlelib.run.cleanup_traceback and other similar code may
447*cda5da8dSAndroid Build Coastguard Worker        # break this by making arbitrary frames plain tuples, so we need to
448*cda5da8dSAndroid Build Coastguard Worker        # check on a frame by frame basis.
449*cda5da8dSAndroid Build Coastguard Worker        result = StackSummary()
450*cda5da8dSAndroid Build Coastguard Worker        for frame in a_list:
451*cda5da8dSAndroid Build Coastguard Worker            if isinstance(frame, FrameSummary):
452*cda5da8dSAndroid Build Coastguard Worker                result.append(frame)
453*cda5da8dSAndroid Build Coastguard Worker            else:
454*cda5da8dSAndroid Build Coastguard Worker                filename, lineno, name, line = frame
455*cda5da8dSAndroid Build Coastguard Worker                result.append(FrameSummary(filename, lineno, name, line=line))
456*cda5da8dSAndroid Build Coastguard Worker        return result
457*cda5da8dSAndroid Build Coastguard Worker
458*cda5da8dSAndroid Build Coastguard Worker    def format_frame_summary(self, frame_summary):
459*cda5da8dSAndroid Build Coastguard Worker        """Format the lines for a single FrameSummary.
460*cda5da8dSAndroid Build Coastguard Worker
461*cda5da8dSAndroid Build Coastguard Worker        Returns a string representing one frame involved in the stack. This
462*cda5da8dSAndroid Build Coastguard Worker        gets called for every frame to be printed in the stack summary.
463*cda5da8dSAndroid Build Coastguard Worker        """
464*cda5da8dSAndroid Build Coastguard Worker        row = []
465*cda5da8dSAndroid Build Coastguard Worker        row.append('  File "{}", line {}, in {}\n'.format(
466*cda5da8dSAndroid Build Coastguard Worker            frame_summary.filename, frame_summary.lineno, frame_summary.name))
467*cda5da8dSAndroid Build Coastguard Worker        if frame_summary.line:
468*cda5da8dSAndroid Build Coastguard Worker            stripped_line = frame_summary.line.strip()
469*cda5da8dSAndroid Build Coastguard Worker            row.append('    {}\n'.format(stripped_line))
470*cda5da8dSAndroid Build Coastguard Worker
471*cda5da8dSAndroid Build Coastguard Worker            orig_line_len = len(frame_summary._original_line)
472*cda5da8dSAndroid Build Coastguard Worker            frame_line_len = len(frame_summary.line.lstrip())
473*cda5da8dSAndroid Build Coastguard Worker            stripped_characters = orig_line_len - frame_line_len
474*cda5da8dSAndroid Build Coastguard Worker            if (
475*cda5da8dSAndroid Build Coastguard Worker                frame_summary.colno is not None
476*cda5da8dSAndroid Build Coastguard Worker                and frame_summary.end_colno is not None
477*cda5da8dSAndroid Build Coastguard Worker            ):
478*cda5da8dSAndroid Build Coastguard Worker                start_offset = _byte_offset_to_character_offset(
479*cda5da8dSAndroid Build Coastguard Worker                    frame_summary._original_line, frame_summary.colno) + 1
480*cda5da8dSAndroid Build Coastguard Worker                end_offset = _byte_offset_to_character_offset(
481*cda5da8dSAndroid Build Coastguard Worker                    frame_summary._original_line, frame_summary.end_colno) + 1
482*cda5da8dSAndroid Build Coastguard Worker
483*cda5da8dSAndroid Build Coastguard Worker                anchors = None
484*cda5da8dSAndroid Build Coastguard Worker                if frame_summary.lineno == frame_summary.end_lineno:
485*cda5da8dSAndroid Build Coastguard Worker                    with suppress(Exception):
486*cda5da8dSAndroid Build Coastguard Worker                        anchors = _extract_caret_anchors_from_line_segment(
487*cda5da8dSAndroid Build Coastguard Worker                            frame_summary._original_line[start_offset - 1:end_offset - 1]
488*cda5da8dSAndroid Build Coastguard Worker                        )
489*cda5da8dSAndroid Build Coastguard Worker                else:
490*cda5da8dSAndroid Build Coastguard Worker                    end_offset = stripped_characters + len(stripped_line)
491*cda5da8dSAndroid Build Coastguard Worker
492*cda5da8dSAndroid Build Coastguard Worker                # show indicators if primary char doesn't span the frame line
493*cda5da8dSAndroid Build Coastguard Worker                if end_offset - start_offset < len(stripped_line) or (
494*cda5da8dSAndroid Build Coastguard Worker                        anchors and anchors.right_start_offset - anchors.left_end_offset > 0):
495*cda5da8dSAndroid Build Coastguard Worker                    row.append('    ')
496*cda5da8dSAndroid Build Coastguard Worker                    row.append(' ' * (start_offset - stripped_characters))
497*cda5da8dSAndroid Build Coastguard Worker
498*cda5da8dSAndroid Build Coastguard Worker                    if anchors:
499*cda5da8dSAndroid Build Coastguard Worker                        row.append(anchors.primary_char * (anchors.left_end_offset))
500*cda5da8dSAndroid Build Coastguard Worker                        row.append(anchors.secondary_char * (anchors.right_start_offset - anchors.left_end_offset))
501*cda5da8dSAndroid Build Coastguard Worker                        row.append(anchors.primary_char * (end_offset - start_offset - anchors.right_start_offset))
502*cda5da8dSAndroid Build Coastguard Worker                    else:
503*cda5da8dSAndroid Build Coastguard Worker                        row.append('^' * (end_offset - start_offset))
504*cda5da8dSAndroid Build Coastguard Worker
505*cda5da8dSAndroid Build Coastguard Worker                    row.append('\n')
506*cda5da8dSAndroid Build Coastguard Worker
507*cda5da8dSAndroid Build Coastguard Worker        if frame_summary.locals:
508*cda5da8dSAndroid Build Coastguard Worker            for name, value in sorted(frame_summary.locals.items()):
509*cda5da8dSAndroid Build Coastguard Worker                row.append('    {name} = {value}\n'.format(name=name, value=value))
510*cda5da8dSAndroid Build Coastguard Worker
511*cda5da8dSAndroid Build Coastguard Worker        return ''.join(row)
512*cda5da8dSAndroid Build Coastguard Worker
513*cda5da8dSAndroid Build Coastguard Worker    def format(self):
514*cda5da8dSAndroid Build Coastguard Worker        """Format the stack ready for printing.
515*cda5da8dSAndroid Build Coastguard Worker
516*cda5da8dSAndroid Build Coastguard Worker        Returns a list of strings ready for printing.  Each string in the
517*cda5da8dSAndroid Build Coastguard Worker        resulting list corresponds to a single frame from the stack.
518*cda5da8dSAndroid Build Coastguard Worker        Each string ends in a newline; the strings may contain internal
519*cda5da8dSAndroid Build Coastguard Worker        newlines as well, for those items with source text lines.
520*cda5da8dSAndroid Build Coastguard Worker
521*cda5da8dSAndroid Build Coastguard Worker        For long sequences of the same frame and line, the first few
522*cda5da8dSAndroid Build Coastguard Worker        repetitions are shown, followed by a summary line stating the exact
523*cda5da8dSAndroid Build Coastguard Worker        number of further repetitions.
524*cda5da8dSAndroid Build Coastguard Worker        """
525*cda5da8dSAndroid Build Coastguard Worker        result = []
526*cda5da8dSAndroid Build Coastguard Worker        last_file = None
527*cda5da8dSAndroid Build Coastguard Worker        last_line = None
528*cda5da8dSAndroid Build Coastguard Worker        last_name = None
529*cda5da8dSAndroid Build Coastguard Worker        count = 0
530*cda5da8dSAndroid Build Coastguard Worker        for frame_summary in self:
531*cda5da8dSAndroid Build Coastguard Worker            formatted_frame = self.format_frame_summary(frame_summary)
532*cda5da8dSAndroid Build Coastguard Worker            if formatted_frame is None:
533*cda5da8dSAndroid Build Coastguard Worker                continue
534*cda5da8dSAndroid Build Coastguard Worker            if (last_file is None or last_file != frame_summary.filename or
535*cda5da8dSAndroid Build Coastguard Worker                last_line is None or last_line != frame_summary.lineno or
536*cda5da8dSAndroid Build Coastguard Worker                last_name is None or last_name != frame_summary.name):
537*cda5da8dSAndroid Build Coastguard Worker                if count > _RECURSIVE_CUTOFF:
538*cda5da8dSAndroid Build Coastguard Worker                    count -= _RECURSIVE_CUTOFF
539*cda5da8dSAndroid Build Coastguard Worker                    result.append(
540*cda5da8dSAndroid Build Coastguard Worker                        f'  [Previous line repeated {count} more '
541*cda5da8dSAndroid Build Coastguard Worker                        f'time{"s" if count > 1 else ""}]\n'
542*cda5da8dSAndroid Build Coastguard Worker                    )
543*cda5da8dSAndroid Build Coastguard Worker                last_file = frame_summary.filename
544*cda5da8dSAndroid Build Coastguard Worker                last_line = frame_summary.lineno
545*cda5da8dSAndroid Build Coastguard Worker                last_name = frame_summary.name
546*cda5da8dSAndroid Build Coastguard Worker                count = 0
547*cda5da8dSAndroid Build Coastguard Worker            count += 1
548*cda5da8dSAndroid Build Coastguard Worker            if count > _RECURSIVE_CUTOFF:
549*cda5da8dSAndroid Build Coastguard Worker                continue
550*cda5da8dSAndroid Build Coastguard Worker            result.append(formatted_frame)
551*cda5da8dSAndroid Build Coastguard Worker
552*cda5da8dSAndroid Build Coastguard Worker        if count > _RECURSIVE_CUTOFF:
553*cda5da8dSAndroid Build Coastguard Worker            count -= _RECURSIVE_CUTOFF
554*cda5da8dSAndroid Build Coastguard Worker            result.append(
555*cda5da8dSAndroid Build Coastguard Worker                f'  [Previous line repeated {count} more '
556*cda5da8dSAndroid Build Coastguard Worker                f'time{"s" if count > 1 else ""}]\n'
557*cda5da8dSAndroid Build Coastguard Worker            )
558*cda5da8dSAndroid Build Coastguard Worker        return result
559*cda5da8dSAndroid Build Coastguard Worker
560*cda5da8dSAndroid Build Coastguard Worker
561*cda5da8dSAndroid Build Coastguard Workerdef _byte_offset_to_character_offset(str, offset):
562*cda5da8dSAndroid Build Coastguard Worker    as_utf8 = str.encode('utf-8')
563*cda5da8dSAndroid Build Coastguard Worker    return len(as_utf8[:offset].decode("utf-8", errors="replace"))
564*cda5da8dSAndroid Build Coastguard Worker
565*cda5da8dSAndroid Build Coastguard Worker
566*cda5da8dSAndroid Build Coastguard Worker_Anchors = collections.namedtuple(
567*cda5da8dSAndroid Build Coastguard Worker    "_Anchors",
568*cda5da8dSAndroid Build Coastguard Worker    [
569*cda5da8dSAndroid Build Coastguard Worker        "left_end_offset",
570*cda5da8dSAndroid Build Coastguard Worker        "right_start_offset",
571*cda5da8dSAndroid Build Coastguard Worker        "primary_char",
572*cda5da8dSAndroid Build Coastguard Worker        "secondary_char",
573*cda5da8dSAndroid Build Coastguard Worker    ],
574*cda5da8dSAndroid Build Coastguard Worker    defaults=["~", "^"]
575*cda5da8dSAndroid Build Coastguard Worker)
576*cda5da8dSAndroid Build Coastguard Worker
577*cda5da8dSAndroid Build Coastguard Workerdef _extract_caret_anchors_from_line_segment(segment):
578*cda5da8dSAndroid Build Coastguard Worker    import ast
579*cda5da8dSAndroid Build Coastguard Worker
580*cda5da8dSAndroid Build Coastguard Worker    try:
581*cda5da8dSAndroid Build Coastguard Worker        tree = ast.parse(segment)
582*cda5da8dSAndroid Build Coastguard Worker    except SyntaxError:
583*cda5da8dSAndroid Build Coastguard Worker        return None
584*cda5da8dSAndroid Build Coastguard Worker
585*cda5da8dSAndroid Build Coastguard Worker    if len(tree.body) != 1:
586*cda5da8dSAndroid Build Coastguard Worker        return None
587*cda5da8dSAndroid Build Coastguard Worker
588*cda5da8dSAndroid Build Coastguard Worker    normalize = lambda offset: _byte_offset_to_character_offset(segment, offset)
589*cda5da8dSAndroid Build Coastguard Worker    statement = tree.body[0]
590*cda5da8dSAndroid Build Coastguard Worker    match statement:
591*cda5da8dSAndroid Build Coastguard Worker        case ast.Expr(expr):
592*cda5da8dSAndroid Build Coastguard Worker            match expr:
593*cda5da8dSAndroid Build Coastguard Worker                case ast.BinOp():
594*cda5da8dSAndroid Build Coastguard Worker                    operator_start = normalize(expr.left.end_col_offset)
595*cda5da8dSAndroid Build Coastguard Worker                    operator_end = normalize(expr.right.col_offset)
596*cda5da8dSAndroid Build Coastguard Worker                    operator_str = segment[operator_start:operator_end]
597*cda5da8dSAndroid Build Coastguard Worker                    operator_offset = len(operator_str) - len(operator_str.lstrip())
598*cda5da8dSAndroid Build Coastguard Worker
599*cda5da8dSAndroid Build Coastguard Worker                    left_anchor = expr.left.end_col_offset + operator_offset
600*cda5da8dSAndroid Build Coastguard Worker                    right_anchor = left_anchor + 1
601*cda5da8dSAndroid Build Coastguard Worker                    if (
602*cda5da8dSAndroid Build Coastguard Worker                        operator_offset + 1 < len(operator_str)
603*cda5da8dSAndroid Build Coastguard Worker                        and not operator_str[operator_offset + 1].isspace()
604*cda5da8dSAndroid Build Coastguard Worker                    ):
605*cda5da8dSAndroid Build Coastguard Worker                        right_anchor += 1
606*cda5da8dSAndroid Build Coastguard Worker                    return _Anchors(normalize(left_anchor), normalize(right_anchor))
607*cda5da8dSAndroid Build Coastguard Worker                case ast.Subscript():
608*cda5da8dSAndroid Build Coastguard Worker                    subscript_start = normalize(expr.value.end_col_offset)
609*cda5da8dSAndroid Build Coastguard Worker                    subscript_end = normalize(expr.slice.end_col_offset + 1)
610*cda5da8dSAndroid Build Coastguard Worker                    return _Anchors(subscript_start, subscript_end)
611*cda5da8dSAndroid Build Coastguard Worker
612*cda5da8dSAndroid Build Coastguard Worker    return None
613*cda5da8dSAndroid Build Coastguard Worker
614*cda5da8dSAndroid Build Coastguard Worker
615*cda5da8dSAndroid Build Coastguard Workerclass _ExceptionPrintContext:
616*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
617*cda5da8dSAndroid Build Coastguard Worker        self.seen = set()
618*cda5da8dSAndroid Build Coastguard Worker        self.exception_group_depth = 0
619*cda5da8dSAndroid Build Coastguard Worker        self.need_close = False
620*cda5da8dSAndroid Build Coastguard Worker
621*cda5da8dSAndroid Build Coastguard Worker    def indent(self):
622*cda5da8dSAndroid Build Coastguard Worker        return ' ' * (2 * self.exception_group_depth)
623*cda5da8dSAndroid Build Coastguard Worker
624*cda5da8dSAndroid Build Coastguard Worker    def emit(self, text_gen, margin_char=None):
625*cda5da8dSAndroid Build Coastguard Worker        if margin_char is None:
626*cda5da8dSAndroid Build Coastguard Worker            margin_char = '|'
627*cda5da8dSAndroid Build Coastguard Worker        indent_str = self.indent()
628*cda5da8dSAndroid Build Coastguard Worker        if self.exception_group_depth:
629*cda5da8dSAndroid Build Coastguard Worker            indent_str += margin_char + ' '
630*cda5da8dSAndroid Build Coastguard Worker
631*cda5da8dSAndroid Build Coastguard Worker        if isinstance(text_gen, str):
632*cda5da8dSAndroid Build Coastguard Worker            yield textwrap.indent(text_gen, indent_str, lambda line: True)
633*cda5da8dSAndroid Build Coastguard Worker        else:
634*cda5da8dSAndroid Build Coastguard Worker            for text in text_gen:
635*cda5da8dSAndroid Build Coastguard Worker                yield textwrap.indent(text, indent_str, lambda line: True)
636*cda5da8dSAndroid Build Coastguard Worker
637*cda5da8dSAndroid Build Coastguard Worker
638*cda5da8dSAndroid Build Coastguard Workerclass TracebackException:
639*cda5da8dSAndroid Build Coastguard Worker    """An exception ready for rendering.
640*cda5da8dSAndroid Build Coastguard Worker
641*cda5da8dSAndroid Build Coastguard Worker    The traceback module captures enough attributes from the original exception
642*cda5da8dSAndroid Build Coastguard Worker    to this intermediary form to ensure that no references are held, while
643*cda5da8dSAndroid Build Coastguard Worker    still being able to fully print or format it.
644*cda5da8dSAndroid Build Coastguard Worker
645*cda5da8dSAndroid Build Coastguard Worker    max_group_width and max_group_depth control the formatting of exception
646*cda5da8dSAndroid Build Coastguard Worker    groups. The depth refers to the nesting level of the group, and the width
647*cda5da8dSAndroid Build Coastguard Worker    refers to the size of a single exception group's exceptions array. The
648*cda5da8dSAndroid Build Coastguard Worker    formatted output is truncated when either limit is exceeded.
649*cda5da8dSAndroid Build Coastguard Worker
650*cda5da8dSAndroid Build Coastguard Worker    Use `from_exception` to create TracebackException instances from exception
651*cda5da8dSAndroid Build Coastguard Worker    objects, or the constructor to create TracebackException instances from
652*cda5da8dSAndroid Build Coastguard Worker    individual components.
653*cda5da8dSAndroid Build Coastguard Worker
654*cda5da8dSAndroid Build Coastguard Worker    - :attr:`__cause__` A TracebackException of the original *__cause__*.
655*cda5da8dSAndroid Build Coastguard Worker    - :attr:`__context__` A TracebackException of the original *__context__*.
656*cda5da8dSAndroid Build Coastguard Worker    - :attr:`exceptions` For exception groups - a list of TracebackException
657*cda5da8dSAndroid Build Coastguard Worker      instances for the nested *exceptions*.  ``None`` for other exceptions.
658*cda5da8dSAndroid Build Coastguard Worker    - :attr:`__suppress_context__` The *__suppress_context__* value from the
659*cda5da8dSAndroid Build Coastguard Worker      original exception.
660*cda5da8dSAndroid Build Coastguard Worker    - :attr:`stack` A `StackSummary` representing the traceback.
661*cda5da8dSAndroid Build Coastguard Worker    - :attr:`exc_type` The class of the original traceback.
662*cda5da8dSAndroid Build Coastguard Worker    - :attr:`filename` For syntax errors - the filename where the error
663*cda5da8dSAndroid Build Coastguard Worker      occurred.
664*cda5da8dSAndroid Build Coastguard Worker    - :attr:`lineno` For syntax errors - the linenumber where the error
665*cda5da8dSAndroid Build Coastguard Worker      occurred.
666*cda5da8dSAndroid Build Coastguard Worker    - :attr:`end_lineno` For syntax errors - the end linenumber where the error
667*cda5da8dSAndroid Build Coastguard Worker      occurred. Can be `None` if not present.
668*cda5da8dSAndroid Build Coastguard Worker    - :attr:`text` For syntax errors - the text where the error
669*cda5da8dSAndroid Build Coastguard Worker      occurred.
670*cda5da8dSAndroid Build Coastguard Worker    - :attr:`offset` For syntax errors - the offset into the text where the
671*cda5da8dSAndroid Build Coastguard Worker      error occurred.
672*cda5da8dSAndroid Build Coastguard Worker    - :attr:`end_offset` For syntax errors - the end offset into the text where
673*cda5da8dSAndroid Build Coastguard Worker      the error occurred. Can be `None` if not present.
674*cda5da8dSAndroid Build Coastguard Worker    - :attr:`msg` For syntax errors - the compiler error message.
675*cda5da8dSAndroid Build Coastguard Worker    """
676*cda5da8dSAndroid Build Coastguard Worker
677*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
678*cda5da8dSAndroid Build Coastguard Worker            lookup_lines=True, capture_locals=False, compact=False,
679*cda5da8dSAndroid Build Coastguard Worker            max_group_width=15, max_group_depth=10, _seen=None):
680*cda5da8dSAndroid Build Coastguard Worker        # NB: we need to accept exc_traceback, exc_value, exc_traceback to
681*cda5da8dSAndroid Build Coastguard Worker        # permit backwards compat with the existing API, otherwise we
682*cda5da8dSAndroid Build Coastguard Worker        # need stub thunk objects just to glue it together.
683*cda5da8dSAndroid Build Coastguard Worker        # Handle loops in __cause__ or __context__.
684*cda5da8dSAndroid Build Coastguard Worker        is_recursive_call = _seen is not None
685*cda5da8dSAndroid Build Coastguard Worker        if _seen is None:
686*cda5da8dSAndroid Build Coastguard Worker            _seen = set()
687*cda5da8dSAndroid Build Coastguard Worker        _seen.add(id(exc_value))
688*cda5da8dSAndroid Build Coastguard Worker
689*cda5da8dSAndroid Build Coastguard Worker        self.max_group_width = max_group_width
690*cda5da8dSAndroid Build Coastguard Worker        self.max_group_depth = max_group_depth
691*cda5da8dSAndroid Build Coastguard Worker
692*cda5da8dSAndroid Build Coastguard Worker        self.stack = StackSummary._extract_from_extended_frame_gen(
693*cda5da8dSAndroid Build Coastguard Worker            _walk_tb_with_full_positions(exc_traceback),
694*cda5da8dSAndroid Build Coastguard Worker            limit=limit, lookup_lines=lookup_lines,
695*cda5da8dSAndroid Build Coastguard Worker            capture_locals=capture_locals)
696*cda5da8dSAndroid Build Coastguard Worker        self.exc_type = exc_type
697*cda5da8dSAndroid Build Coastguard Worker        # Capture now to permit freeing resources: only complication is in the
698*cda5da8dSAndroid Build Coastguard Worker        # unofficial API _format_final_exc_line
699*cda5da8dSAndroid Build Coastguard Worker        self._str = _safe_string(exc_value, 'exception')
700*cda5da8dSAndroid Build Coastguard Worker        self.__notes__ = getattr(exc_value, '__notes__', None)
701*cda5da8dSAndroid Build Coastguard Worker
702*cda5da8dSAndroid Build Coastguard Worker        if exc_type and issubclass(exc_type, SyntaxError):
703*cda5da8dSAndroid Build Coastguard Worker            # Handle SyntaxError's specially
704*cda5da8dSAndroid Build Coastguard Worker            self.filename = exc_value.filename
705*cda5da8dSAndroid Build Coastguard Worker            lno = exc_value.lineno
706*cda5da8dSAndroid Build Coastguard Worker            self.lineno = str(lno) if lno is not None else None
707*cda5da8dSAndroid Build Coastguard Worker            end_lno = exc_value.end_lineno
708*cda5da8dSAndroid Build Coastguard Worker            self.end_lineno = str(end_lno) if end_lno is not None else None
709*cda5da8dSAndroid Build Coastguard Worker            self.text = exc_value.text
710*cda5da8dSAndroid Build Coastguard Worker            self.offset = exc_value.offset
711*cda5da8dSAndroid Build Coastguard Worker            self.end_offset = exc_value.end_offset
712*cda5da8dSAndroid Build Coastguard Worker            self.msg = exc_value.msg
713*cda5da8dSAndroid Build Coastguard Worker        if lookup_lines:
714*cda5da8dSAndroid Build Coastguard Worker            self._load_lines()
715*cda5da8dSAndroid Build Coastguard Worker        self.__suppress_context__ = \
716*cda5da8dSAndroid Build Coastguard Worker            exc_value.__suppress_context__ if exc_value is not None else False
717*cda5da8dSAndroid Build Coastguard Worker
718*cda5da8dSAndroid Build Coastguard Worker        # Convert __cause__ and __context__ to `TracebackExceptions`s, use a
719*cda5da8dSAndroid Build Coastguard Worker        # queue to avoid recursion (only the top-level call gets _seen == None)
720*cda5da8dSAndroid Build Coastguard Worker        if not is_recursive_call:
721*cda5da8dSAndroid Build Coastguard Worker            queue = [(self, exc_value)]
722*cda5da8dSAndroid Build Coastguard Worker            while queue:
723*cda5da8dSAndroid Build Coastguard Worker                te, e = queue.pop()
724*cda5da8dSAndroid Build Coastguard Worker                if (e and e.__cause__ is not None
725*cda5da8dSAndroid Build Coastguard Worker                    and id(e.__cause__) not in _seen):
726*cda5da8dSAndroid Build Coastguard Worker                    cause = TracebackException(
727*cda5da8dSAndroid Build Coastguard Worker                        type(e.__cause__),
728*cda5da8dSAndroid Build Coastguard Worker                        e.__cause__,
729*cda5da8dSAndroid Build Coastguard Worker                        e.__cause__.__traceback__,
730*cda5da8dSAndroid Build Coastguard Worker                        limit=limit,
731*cda5da8dSAndroid Build Coastguard Worker                        lookup_lines=lookup_lines,
732*cda5da8dSAndroid Build Coastguard Worker                        capture_locals=capture_locals,
733*cda5da8dSAndroid Build Coastguard Worker                        max_group_width=max_group_width,
734*cda5da8dSAndroid Build Coastguard Worker                        max_group_depth=max_group_depth,
735*cda5da8dSAndroid Build Coastguard Worker                        _seen=_seen)
736*cda5da8dSAndroid Build Coastguard Worker                else:
737*cda5da8dSAndroid Build Coastguard Worker                    cause = None
738*cda5da8dSAndroid Build Coastguard Worker
739*cda5da8dSAndroid Build Coastguard Worker                if compact:
740*cda5da8dSAndroid Build Coastguard Worker                    need_context = (cause is None and
741*cda5da8dSAndroid Build Coastguard Worker                                    e is not None and
742*cda5da8dSAndroid Build Coastguard Worker                                    not e.__suppress_context__)
743*cda5da8dSAndroid Build Coastguard Worker                else:
744*cda5da8dSAndroid Build Coastguard Worker                    need_context = True
745*cda5da8dSAndroid Build Coastguard Worker                if (e and e.__context__ is not None
746*cda5da8dSAndroid Build Coastguard Worker                    and need_context and id(e.__context__) not in _seen):
747*cda5da8dSAndroid Build Coastguard Worker                    context = TracebackException(
748*cda5da8dSAndroid Build Coastguard Worker                        type(e.__context__),
749*cda5da8dSAndroid Build Coastguard Worker                        e.__context__,
750*cda5da8dSAndroid Build Coastguard Worker                        e.__context__.__traceback__,
751*cda5da8dSAndroid Build Coastguard Worker                        limit=limit,
752*cda5da8dSAndroid Build Coastguard Worker                        lookup_lines=lookup_lines,
753*cda5da8dSAndroid Build Coastguard Worker                        capture_locals=capture_locals,
754*cda5da8dSAndroid Build Coastguard Worker                        max_group_width=max_group_width,
755*cda5da8dSAndroid Build Coastguard Worker                        max_group_depth=max_group_depth,
756*cda5da8dSAndroid Build Coastguard Worker                        _seen=_seen)
757*cda5da8dSAndroid Build Coastguard Worker                else:
758*cda5da8dSAndroid Build Coastguard Worker                    context = None
759*cda5da8dSAndroid Build Coastguard Worker
760*cda5da8dSAndroid Build Coastguard Worker                if e and isinstance(e, BaseExceptionGroup):
761*cda5da8dSAndroid Build Coastguard Worker                    exceptions = []
762*cda5da8dSAndroid Build Coastguard Worker                    for exc in e.exceptions:
763*cda5da8dSAndroid Build Coastguard Worker                        texc = TracebackException(
764*cda5da8dSAndroid Build Coastguard Worker                            type(exc),
765*cda5da8dSAndroid Build Coastguard Worker                            exc,
766*cda5da8dSAndroid Build Coastguard Worker                            exc.__traceback__,
767*cda5da8dSAndroid Build Coastguard Worker                            limit=limit,
768*cda5da8dSAndroid Build Coastguard Worker                            lookup_lines=lookup_lines,
769*cda5da8dSAndroid Build Coastguard Worker                            capture_locals=capture_locals,
770*cda5da8dSAndroid Build Coastguard Worker                            max_group_width=max_group_width,
771*cda5da8dSAndroid Build Coastguard Worker                            max_group_depth=max_group_depth,
772*cda5da8dSAndroid Build Coastguard Worker                            _seen=_seen)
773*cda5da8dSAndroid Build Coastguard Worker                        exceptions.append(texc)
774*cda5da8dSAndroid Build Coastguard Worker                else:
775*cda5da8dSAndroid Build Coastguard Worker                    exceptions = None
776*cda5da8dSAndroid Build Coastguard Worker
777*cda5da8dSAndroid Build Coastguard Worker                te.__cause__ = cause
778*cda5da8dSAndroid Build Coastguard Worker                te.__context__ = context
779*cda5da8dSAndroid Build Coastguard Worker                te.exceptions = exceptions
780*cda5da8dSAndroid Build Coastguard Worker                if cause:
781*cda5da8dSAndroid Build Coastguard Worker                    queue.append((te.__cause__, e.__cause__))
782*cda5da8dSAndroid Build Coastguard Worker                if context:
783*cda5da8dSAndroid Build Coastguard Worker                    queue.append((te.__context__, e.__context__))
784*cda5da8dSAndroid Build Coastguard Worker                if exceptions:
785*cda5da8dSAndroid Build Coastguard Worker                    queue.extend(zip(te.exceptions, e.exceptions))
786*cda5da8dSAndroid Build Coastguard Worker
787*cda5da8dSAndroid Build Coastguard Worker    @classmethod
788*cda5da8dSAndroid Build Coastguard Worker    def from_exception(cls, exc, *args, **kwargs):
789*cda5da8dSAndroid Build Coastguard Worker        """Create a TracebackException from an exception."""
790*cda5da8dSAndroid Build Coastguard Worker        return cls(type(exc), exc, exc.__traceback__, *args, **kwargs)
791*cda5da8dSAndroid Build Coastguard Worker
792*cda5da8dSAndroid Build Coastguard Worker    def _load_lines(self):
793*cda5da8dSAndroid Build Coastguard Worker        """Private API. force all lines in the stack to be loaded."""
794*cda5da8dSAndroid Build Coastguard Worker        for frame in self.stack:
795*cda5da8dSAndroid Build Coastguard Worker            frame.line
796*cda5da8dSAndroid Build Coastguard Worker
797*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
798*cda5da8dSAndroid Build Coastguard Worker        if isinstance(other, TracebackException):
799*cda5da8dSAndroid Build Coastguard Worker            return self.__dict__ == other.__dict__
800*cda5da8dSAndroid Build Coastguard Worker        return NotImplemented
801*cda5da8dSAndroid Build Coastguard Worker
802*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
803*cda5da8dSAndroid Build Coastguard Worker        return self._str
804*cda5da8dSAndroid Build Coastguard Worker
805*cda5da8dSAndroid Build Coastguard Worker    def format_exception_only(self):
806*cda5da8dSAndroid Build Coastguard Worker        """Format the exception part of the traceback.
807*cda5da8dSAndroid Build Coastguard Worker
808*cda5da8dSAndroid Build Coastguard Worker        The return value is a generator of strings, each ending in a newline.
809*cda5da8dSAndroid Build Coastguard Worker
810*cda5da8dSAndroid Build Coastguard Worker        Normally, the generator emits a single string; however, for
811*cda5da8dSAndroid Build Coastguard Worker        SyntaxError exceptions, it emits several lines that (when
812*cda5da8dSAndroid Build Coastguard Worker        printed) display detailed information about where the syntax
813*cda5da8dSAndroid Build Coastguard Worker        error occurred.
814*cda5da8dSAndroid Build Coastguard Worker
815*cda5da8dSAndroid Build Coastguard Worker        The message indicating which exception occurred is always the last
816*cda5da8dSAndroid Build Coastguard Worker        string in the output.
817*cda5da8dSAndroid Build Coastguard Worker        """
818*cda5da8dSAndroid Build Coastguard Worker        if self.exc_type is None:
819*cda5da8dSAndroid Build Coastguard Worker            yield _format_final_exc_line(None, self._str)
820*cda5da8dSAndroid Build Coastguard Worker            return
821*cda5da8dSAndroid Build Coastguard Worker
822*cda5da8dSAndroid Build Coastguard Worker        stype = self.exc_type.__qualname__
823*cda5da8dSAndroid Build Coastguard Worker        smod = self.exc_type.__module__
824*cda5da8dSAndroid Build Coastguard Worker        if smod not in ("__main__", "builtins"):
825*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(smod, str):
826*cda5da8dSAndroid Build Coastguard Worker                smod = "<unknown>"
827*cda5da8dSAndroid Build Coastguard Worker            stype = smod + '.' + stype
828*cda5da8dSAndroid Build Coastguard Worker
829*cda5da8dSAndroid Build Coastguard Worker        if not issubclass(self.exc_type, SyntaxError):
830*cda5da8dSAndroid Build Coastguard Worker            yield _format_final_exc_line(stype, self._str)
831*cda5da8dSAndroid Build Coastguard Worker        else:
832*cda5da8dSAndroid Build Coastguard Worker            yield from self._format_syntax_error(stype)
833*cda5da8dSAndroid Build Coastguard Worker        if isinstance(self.__notes__, collections.abc.Sequence):
834*cda5da8dSAndroid Build Coastguard Worker            for note in self.__notes__:
835*cda5da8dSAndroid Build Coastguard Worker                note = _safe_string(note, 'note')
836*cda5da8dSAndroid Build Coastguard Worker                yield from [l + '\n' for l in note.split('\n')]
837*cda5da8dSAndroid Build Coastguard Worker        elif self.__notes__ is not None:
838*cda5da8dSAndroid Build Coastguard Worker            yield _safe_string(self.__notes__, '__notes__', func=repr)
839*cda5da8dSAndroid Build Coastguard Worker
840*cda5da8dSAndroid Build Coastguard Worker    def _format_syntax_error(self, stype):
841*cda5da8dSAndroid Build Coastguard Worker        """Format SyntaxError exceptions (internal helper)."""
842*cda5da8dSAndroid Build Coastguard Worker        # Show exactly where the problem was found.
843*cda5da8dSAndroid Build Coastguard Worker        filename_suffix = ''
844*cda5da8dSAndroid Build Coastguard Worker        if self.lineno is not None:
845*cda5da8dSAndroid Build Coastguard Worker            yield '  File "{}", line {}\n'.format(
846*cda5da8dSAndroid Build Coastguard Worker                self.filename or "<string>", self.lineno)
847*cda5da8dSAndroid Build Coastguard Worker        elif self.filename is not None:
848*cda5da8dSAndroid Build Coastguard Worker            filename_suffix = ' ({})'.format(self.filename)
849*cda5da8dSAndroid Build Coastguard Worker
850*cda5da8dSAndroid Build Coastguard Worker        text = self.text
851*cda5da8dSAndroid Build Coastguard Worker        if text is not None:
852*cda5da8dSAndroid Build Coastguard Worker            # text  = "   foo\n"
853*cda5da8dSAndroid Build Coastguard Worker            # rtext = "   foo"
854*cda5da8dSAndroid Build Coastguard Worker            # ltext =    "foo"
855*cda5da8dSAndroid Build Coastguard Worker            rtext = text.rstrip('\n')
856*cda5da8dSAndroid Build Coastguard Worker            ltext = rtext.lstrip(' \n\f')
857*cda5da8dSAndroid Build Coastguard Worker            spaces = len(rtext) - len(ltext)
858*cda5da8dSAndroid Build Coastguard Worker            yield '    {}\n'.format(ltext)
859*cda5da8dSAndroid Build Coastguard Worker
860*cda5da8dSAndroid Build Coastguard Worker            if self.offset is not None:
861*cda5da8dSAndroid Build Coastguard Worker                offset = self.offset
862*cda5da8dSAndroid Build Coastguard Worker                end_offset = self.end_offset if self.end_offset not in {None, 0} else offset
863*cda5da8dSAndroid Build Coastguard Worker                if offset == end_offset or end_offset == -1:
864*cda5da8dSAndroid Build Coastguard Worker                    end_offset = offset + 1
865*cda5da8dSAndroid Build Coastguard Worker
866*cda5da8dSAndroid Build Coastguard Worker                # Convert 1-based column offset to 0-based index into stripped text
867*cda5da8dSAndroid Build Coastguard Worker                colno = offset - 1 - spaces
868*cda5da8dSAndroid Build Coastguard Worker                end_colno = end_offset - 1 - spaces
869*cda5da8dSAndroid Build Coastguard Worker                if colno >= 0:
870*cda5da8dSAndroid Build Coastguard Worker                    # non-space whitespace (likes tabs) must be kept for alignment
871*cda5da8dSAndroid Build Coastguard Worker                    caretspace = ((c if c.isspace() else ' ') for c in ltext[:colno])
872*cda5da8dSAndroid Build Coastguard Worker                    yield '    {}{}'.format("".join(caretspace), ('^' * (end_colno - colno) + "\n"))
873*cda5da8dSAndroid Build Coastguard Worker        msg = self.msg or "<no detail available>"
874*cda5da8dSAndroid Build Coastguard Worker        yield "{}: {}{}\n".format(stype, msg, filename_suffix)
875*cda5da8dSAndroid Build Coastguard Worker
876*cda5da8dSAndroid Build Coastguard Worker    def format(self, *, chain=True, _ctx=None):
877*cda5da8dSAndroid Build Coastguard Worker        """Format the exception.
878*cda5da8dSAndroid Build Coastguard Worker
879*cda5da8dSAndroid Build Coastguard Worker        If chain is not *True*, *__cause__* and *__context__* will not be formatted.
880*cda5da8dSAndroid Build Coastguard Worker
881*cda5da8dSAndroid Build Coastguard Worker        The return value is a generator of strings, each ending in a newline and
882*cda5da8dSAndroid Build Coastguard Worker        some containing internal newlines. `print_exception` is a wrapper around
883*cda5da8dSAndroid Build Coastguard Worker        this method which just prints the lines to a file.
884*cda5da8dSAndroid Build Coastguard Worker
885*cda5da8dSAndroid Build Coastguard Worker        The message indicating which exception occurred is always the last
886*cda5da8dSAndroid Build Coastguard Worker        string in the output.
887*cda5da8dSAndroid Build Coastguard Worker        """
888*cda5da8dSAndroid Build Coastguard Worker
889*cda5da8dSAndroid Build Coastguard Worker        if _ctx is None:
890*cda5da8dSAndroid Build Coastguard Worker            _ctx = _ExceptionPrintContext()
891*cda5da8dSAndroid Build Coastguard Worker
892*cda5da8dSAndroid Build Coastguard Worker        output = []
893*cda5da8dSAndroid Build Coastguard Worker        exc = self
894*cda5da8dSAndroid Build Coastguard Worker        if chain:
895*cda5da8dSAndroid Build Coastguard Worker            while exc:
896*cda5da8dSAndroid Build Coastguard Worker                if exc.__cause__ is not None:
897*cda5da8dSAndroid Build Coastguard Worker                    chained_msg = _cause_message
898*cda5da8dSAndroid Build Coastguard Worker                    chained_exc = exc.__cause__
899*cda5da8dSAndroid Build Coastguard Worker                elif (exc.__context__  is not None and
900*cda5da8dSAndroid Build Coastguard Worker                      not exc.__suppress_context__):
901*cda5da8dSAndroid Build Coastguard Worker                    chained_msg = _context_message
902*cda5da8dSAndroid Build Coastguard Worker                    chained_exc = exc.__context__
903*cda5da8dSAndroid Build Coastguard Worker                else:
904*cda5da8dSAndroid Build Coastguard Worker                    chained_msg = None
905*cda5da8dSAndroid Build Coastguard Worker                    chained_exc = None
906*cda5da8dSAndroid Build Coastguard Worker
907*cda5da8dSAndroid Build Coastguard Worker                output.append((chained_msg, exc))
908*cda5da8dSAndroid Build Coastguard Worker                exc = chained_exc
909*cda5da8dSAndroid Build Coastguard Worker        else:
910*cda5da8dSAndroid Build Coastguard Worker            output.append((None, exc))
911*cda5da8dSAndroid Build Coastguard Worker
912*cda5da8dSAndroid Build Coastguard Worker        for msg, exc in reversed(output):
913*cda5da8dSAndroid Build Coastguard Worker            if msg is not None:
914*cda5da8dSAndroid Build Coastguard Worker                yield from _ctx.emit(msg)
915*cda5da8dSAndroid Build Coastguard Worker            if exc.exceptions is None:
916*cda5da8dSAndroid Build Coastguard Worker                if exc.stack:
917*cda5da8dSAndroid Build Coastguard Worker                    yield from _ctx.emit('Traceback (most recent call last):\n')
918*cda5da8dSAndroid Build Coastguard Worker                    yield from _ctx.emit(exc.stack.format())
919*cda5da8dSAndroid Build Coastguard Worker                yield from _ctx.emit(exc.format_exception_only())
920*cda5da8dSAndroid Build Coastguard Worker            elif _ctx.exception_group_depth > self.max_group_depth:
921*cda5da8dSAndroid Build Coastguard Worker                # exception group, but depth exceeds limit
922*cda5da8dSAndroid Build Coastguard Worker                yield from _ctx.emit(
923*cda5da8dSAndroid Build Coastguard Worker                    f"... (max_group_depth is {self.max_group_depth})\n")
924*cda5da8dSAndroid Build Coastguard Worker            else:
925*cda5da8dSAndroid Build Coastguard Worker                # format exception group
926*cda5da8dSAndroid Build Coastguard Worker                is_toplevel = (_ctx.exception_group_depth == 0)
927*cda5da8dSAndroid Build Coastguard Worker                if is_toplevel:
928*cda5da8dSAndroid Build Coastguard Worker                    _ctx.exception_group_depth += 1
929*cda5da8dSAndroid Build Coastguard Worker
930*cda5da8dSAndroid Build Coastguard Worker                if exc.stack:
931*cda5da8dSAndroid Build Coastguard Worker                    yield from _ctx.emit(
932*cda5da8dSAndroid Build Coastguard Worker                        'Exception Group Traceback (most recent call last):\n',
933*cda5da8dSAndroid Build Coastguard Worker                        margin_char = '+' if is_toplevel else None)
934*cda5da8dSAndroid Build Coastguard Worker                    yield from _ctx.emit(exc.stack.format())
935*cda5da8dSAndroid Build Coastguard Worker
936*cda5da8dSAndroid Build Coastguard Worker                yield from _ctx.emit(exc.format_exception_only())
937*cda5da8dSAndroid Build Coastguard Worker                num_excs = len(exc.exceptions)
938*cda5da8dSAndroid Build Coastguard Worker                if num_excs <= self.max_group_width:
939*cda5da8dSAndroid Build Coastguard Worker                    n = num_excs
940*cda5da8dSAndroid Build Coastguard Worker                else:
941*cda5da8dSAndroid Build Coastguard Worker                    n = self.max_group_width + 1
942*cda5da8dSAndroid Build Coastguard Worker                _ctx.need_close = False
943*cda5da8dSAndroid Build Coastguard Worker                for i in range(n):
944*cda5da8dSAndroid Build Coastguard Worker                    last_exc = (i == n-1)
945*cda5da8dSAndroid Build Coastguard Worker                    if last_exc:
946*cda5da8dSAndroid Build Coastguard Worker                        # The closing frame may be added by a recursive call
947*cda5da8dSAndroid Build Coastguard Worker                        _ctx.need_close = True
948*cda5da8dSAndroid Build Coastguard Worker
949*cda5da8dSAndroid Build Coastguard Worker                    if self.max_group_width is not None:
950*cda5da8dSAndroid Build Coastguard Worker                        truncated = (i >= self.max_group_width)
951*cda5da8dSAndroid Build Coastguard Worker                    else:
952*cda5da8dSAndroid Build Coastguard Worker                        truncated = False
953*cda5da8dSAndroid Build Coastguard Worker                    title = f'{i+1}' if not truncated else '...'
954*cda5da8dSAndroid Build Coastguard Worker                    yield (_ctx.indent() +
955*cda5da8dSAndroid Build Coastguard Worker                           ('+-' if i==0 else '  ') +
956*cda5da8dSAndroid Build Coastguard Worker                           f'+---------------- {title} ----------------\n')
957*cda5da8dSAndroid Build Coastguard Worker                    _ctx.exception_group_depth += 1
958*cda5da8dSAndroid Build Coastguard Worker                    if not truncated:
959*cda5da8dSAndroid Build Coastguard Worker                        yield from exc.exceptions[i].format(chain=chain, _ctx=_ctx)
960*cda5da8dSAndroid Build Coastguard Worker                    else:
961*cda5da8dSAndroid Build Coastguard Worker                        remaining = num_excs - self.max_group_width
962*cda5da8dSAndroid Build Coastguard Worker                        plural = 's' if remaining > 1 else ''
963*cda5da8dSAndroid Build Coastguard Worker                        yield from _ctx.emit(
964*cda5da8dSAndroid Build Coastguard Worker                            f"and {remaining} more exception{plural}\n")
965*cda5da8dSAndroid Build Coastguard Worker
966*cda5da8dSAndroid Build Coastguard Worker                    if last_exc and _ctx.need_close:
967*cda5da8dSAndroid Build Coastguard Worker                        yield (_ctx.indent() +
968*cda5da8dSAndroid Build Coastguard Worker                               "+------------------------------------\n")
969*cda5da8dSAndroid Build Coastguard Worker                        _ctx.need_close = False
970*cda5da8dSAndroid Build Coastguard Worker                    _ctx.exception_group_depth -= 1
971*cda5da8dSAndroid Build Coastguard Worker
972*cda5da8dSAndroid Build Coastguard Worker                if is_toplevel:
973*cda5da8dSAndroid Build Coastguard Worker                    assert _ctx.exception_group_depth == 1
974*cda5da8dSAndroid Build Coastguard Worker                    _ctx.exception_group_depth = 0
975*cda5da8dSAndroid Build Coastguard Worker
976*cda5da8dSAndroid Build Coastguard Worker
977*cda5da8dSAndroid Build Coastguard Worker    def print(self, *, file=None, chain=True):
978*cda5da8dSAndroid Build Coastguard Worker        """Print the result of self.format(chain=chain) to 'file'."""
979*cda5da8dSAndroid Build Coastguard Worker        if file is None:
980*cda5da8dSAndroid Build Coastguard Worker            file = sys.stderr
981*cda5da8dSAndroid Build Coastguard Worker        for line in self.format(chain=chain):
982*cda5da8dSAndroid Build Coastguard Worker            print(line, file=file, end="")
983