1"""Extract, format and print information about Python stack traces."""
2
3import collections
4import itertools
5import linecache
6import sys
7
8__all__ = ['extract_stack', 'extract_tb', 'format_exception',
9           'format_exception_only', 'format_list', 'format_stack',
10           'format_tb', 'print_exc', 'format_exc', 'print_exception',
11           'print_last', 'print_stack', 'print_tb', 'clear_frames',
12           'FrameSummary', 'StackSummary', 'TracebackException',
13           'walk_stack', 'walk_tb']
14
15#
16# Formatting and printing lists of traceback lines.
17#
18
19def print_list(extracted_list, file=None):
20    """Print the list of tuples as returned by extract_tb() or
21    extract_stack() as a formatted stack trace to the given file."""
22    if file is None:
23        file = sys.stderr
24    for item in StackSummary.from_list(extracted_list).format():
25        print(item, file=file, end="")
26
27def format_list(extracted_list):
28    """Format a list of tuples or FrameSummary objects for printing.
29
30    Given a list of tuples or FrameSummary objects as returned by
31    extract_tb() or extract_stack(), return a list of strings ready
32    for printing.
33
34    Each string in the resulting list corresponds to the item with the
35    same index in the argument list.  Each string ends in a newline;
36    the strings may contain internal newlines as well, for those items
37    whose source text line is not None.
38    """
39    return StackSummary.from_list(extracted_list).format()
40
41#
42# Printing and Extracting Tracebacks.
43#
44
45def print_tb(tb, limit=None, file=None):
46    """Print up to 'limit' stack trace entries from the traceback 'tb'.
47
48    If 'limit' is omitted or None, all entries are printed.  If 'file'
49    is omitted or None, the output goes to sys.stderr; otherwise
50    'file' should be an open file or file-like object with a write()
51    method.
52    """
53    print_list(extract_tb(tb, limit=limit), file=file)
54
55def format_tb(tb, limit=None):
56    """A shorthand for 'format_list(extract_tb(tb, limit))'."""
57    return extract_tb(tb, limit=limit).format()
58
59def extract_tb(tb, limit=None):
60    """
61    Return a StackSummary object representing a list of
62    pre-processed entries from traceback.
63
64    This is useful for alternate formatting of stack traces.  If
65    'limit' is omitted or None, all entries are extracted.  A
66    pre-processed stack trace entry is a FrameSummary object
67    containing attributes filename, lineno, name, and line
68    representing the information that is usually printed for a stack
69    trace.  The line is a string with leading and trailing
70    whitespace stripped; if the source is not available it is None.
71    """
72    return StackSummary.extract(walk_tb(tb), limit=limit)
73
74#
75# Exception formatting and output.
76#
77
78_cause_message = (
79    "\nThe above exception was the direct cause "
80    "of the following exception:\n\n")
81
82_context_message = (
83    "\nDuring handling of the above exception, "
84    "another exception occurred:\n\n")
85
86
87class _Sentinel:
88    def __repr__(self):
89        return "<implicit>"
90
91_sentinel = _Sentinel()
92
93def _parse_value_tb(exc, value, tb):
94    if (value is _sentinel) != (tb is _sentinel):
95        raise ValueError("Both or neither of value and tb must be given")
96    if value is tb is _sentinel:
97        if exc is not None:
98            return exc, exc.__traceback__
99        else:
100            return None, None
101    return value, tb
102
103
104def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
105                    file=None, chain=True):
106    """Print exception up to 'limit' stack trace entries from 'tb' to 'file'.
107
108    This differs from print_tb() in the following ways: (1) if
109    traceback is not None, it prints a header "Traceback (most recent
110    call last):"; (2) it prints the exception type and value after the
111    stack trace; (3) if type is SyntaxError and value has the
112    appropriate format, it prints the line where the syntax error
113    occurred with a caret on the next line indicating the approximate
114    position of the error.
115    """
116    value, tb = _parse_value_tb(exc, value, tb)
117    if file is None:
118        file = sys.stderr
119    te = TracebackException(type(value), value, tb, limit=limit, compact=True)
120    for line in te.format(chain=chain):
121        print(line, file=file, end="")
122
123
124def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
125                     chain=True):
126    """Format a stack trace and the exception information.
127
128    The arguments have the same meaning as the corresponding arguments
129    to print_exception().  The return value is a list of strings, each
130    ending in a newline and some containing internal newlines.  When
131    these lines are concatenated and printed, exactly the same text is
132    printed as does print_exception().
133    """
134    value, tb = _parse_value_tb(exc, value, tb)
135    te = TracebackException(type(value), value, tb, limit=limit, compact=True)
136    return list(te.format(chain=chain))
137
138
139def format_exception_only(exc, /, value=_sentinel):
140    """Format the exception part of a traceback.
141
142    The return value is a list of strings, each ending in a newline.
143
144    Normally, the list contains a single string; however, for
145    SyntaxError exceptions, it contains several lines that (when
146    printed) display detailed information about where the syntax
147    error occurred.
148
149    The message indicating which exception occurred is always the last
150    string in the list.
151
152    """
153    if value is _sentinel:
154        value = exc
155    te = TracebackException(type(value), value, None, compact=True)
156    return list(te.format_exception_only())
157
158
159# -- not official API but folk probably use these two functions.
160
161def _format_final_exc_line(etype, value):
162    valuestr = _some_str(value)
163    if value is None or not valuestr:
164        line = "%s\n" % etype
165    else:
166        line = "%s: %s\n" % (etype, valuestr)
167    return line
168
169def _some_str(value):
170    try:
171        return str(value)
172    except:
173        return '<unprintable %s object>' % type(value).__name__
174
175# --
176
177def print_exc(limit=None, file=None, chain=True):
178    """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'."""
179    print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
180
181def format_exc(limit=None, chain=True):
182    """Like print_exc() but return a string."""
183    return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain))
184
185def print_last(limit=None, file=None, chain=True):
186    """This is a shorthand for 'print_exception(sys.last_type,
187    sys.last_value, sys.last_traceback, limit, file)'."""
188    if not hasattr(sys, "last_type"):
189        raise ValueError("no last exception")
190    print_exception(sys.last_type, sys.last_value, sys.last_traceback,
191                    limit, file, chain)
192
193#
194# Printing and Extracting Stacks.
195#
196
197def print_stack(f=None, limit=None, file=None):
198    """Print a stack trace from its invocation point.
199
200    The optional 'f' argument can be used to specify an alternate
201    stack frame at which to start. The optional 'limit' and 'file'
202    arguments have the same meaning as for print_exception().
203    """
204    if f is None:
205        f = sys._getframe().f_back
206    print_list(extract_stack(f, limit=limit), file=file)
207
208
209def format_stack(f=None, limit=None):
210    """Shorthand for 'format_list(extract_stack(f, limit))'."""
211    if f is None:
212        f = sys._getframe().f_back
213    return format_list(extract_stack(f, limit=limit))
214
215
216def extract_stack(f=None, limit=None):
217    """Extract the raw traceback from the current stack frame.
218
219    The return value has the same format as for extract_tb().  The
220    optional 'f' and 'limit' arguments have the same meaning as for
221    print_stack().  Each item in the list is a quadruple (filename,
222    line number, function name, text), and the entries are in order
223    from oldest to newest stack frame.
224    """
225    if f is None:
226        f = sys._getframe().f_back
227    stack = StackSummary.extract(walk_stack(f), limit=limit)
228    stack.reverse()
229    return stack
230
231
232def clear_frames(tb):
233    "Clear all references to local variables in the frames of a traceback."
234    while tb is not None:
235        try:
236            tb.tb_frame.clear()
237        except RuntimeError:
238            # Ignore the exception raised if the frame is still executing.
239            pass
240        tb = tb.tb_next
241
242
243class FrameSummary:
244    """A single frame from a traceback.
245
246    - :attr:`filename` The filename for the frame.
247    - :attr:`lineno` The line within filename for the frame that was
248      active when the frame was captured.
249    - :attr:`name` The name of the function or method that was executing
250      when the frame was captured.
251    - :attr:`line` The text from the linecache module for the
252      of code that was running when the frame was captured.
253    - :attr:`locals` Either None if locals were not supplied, or a dict
254      mapping the name to the repr() of the variable.
255    """
256
257    __slots__ = ('filename', 'lineno', 'name', '_line', 'locals')
258
259    def __init__(self, filename, lineno, name, *, lookup_line=True,
260            locals=None, line=None):
261        """Construct a FrameSummary.
262
263        :param lookup_line: If True, `linecache` is consulted for the source
264            code line. Otherwise, the line will be looked up when first needed.
265        :param locals: If supplied the frame locals, which will be captured as
266            object representations.
267        :param line: If provided, use this instead of looking up the line in
268            the linecache.
269        """
270        self.filename = filename
271        self.lineno = lineno
272        self.name = name
273        self._line = line
274        if lookup_line:
275            self.line
276        self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
277
278    def __eq__(self, other):
279        if isinstance(other, FrameSummary):
280            return (self.filename == other.filename and
281                    self.lineno == other.lineno and
282                    self.name == other.name and
283                    self.locals == other.locals)
284        if isinstance(other, tuple):
285            return (self.filename, self.lineno, self.name, self.line) == other
286        return NotImplemented
287
288    def __getitem__(self, pos):
289        return (self.filename, self.lineno, self.name, self.line)[pos]
290
291    def __iter__(self):
292        return iter([self.filename, self.lineno, self.name, self.line])
293
294    def __repr__(self):
295        return "<FrameSummary file {filename}, line {lineno} in {name}>".format(
296            filename=self.filename, lineno=self.lineno, name=self.name)
297
298    def __len__(self):
299        return 4
300
301    @property
302    def line(self):
303        if self._line is None:
304            if self.lineno is None:
305                return None
306            self._line = linecache.getline(self.filename, self.lineno)
307        return self._line.strip()
308
309def walk_stack(f):
310    """Walk a stack yielding the frame and line number for each frame.
311
312    This will follow f.f_back from the given frame. If no frame is given, the
313    current stack is used. Usually used with StackSummary.extract.
314    """
315    if f is None:
316        f = sys._getframe().f_back.f_back
317    while f is not None:
318        yield f, f.f_lineno
319        f = f.f_back
320
321
322def walk_tb(tb):
323    """Walk a traceback yielding the frame and line number for each frame.
324
325    This will follow tb.tb_next (and thus is in the opposite order to
326    walk_stack). Usually used with StackSummary.extract.
327    """
328    while tb is not None:
329        yield tb.tb_frame, tb.tb_lineno
330        tb = tb.tb_next
331
332
333_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c.
334
335class StackSummary(list):
336    """A stack of frames."""
337
338    @classmethod
339    def extract(klass, frame_gen, *, limit=None, lookup_lines=True,
340            capture_locals=False):
341        """Create a StackSummary from a traceback or stack object.
342
343        :param frame_gen: A generator that yields (frame, lineno) tuples to
344            include in the stack.
345        :param limit: None to include all frames or the number of frames to
346            include.
347        :param lookup_lines: If True, lookup lines for each frame immediately,
348            otherwise lookup is deferred until the frame is rendered.
349        :param capture_locals: If True, the local variables from each frame will
350            be captured as object representations into the FrameSummary.
351        """
352        if limit is None:
353            limit = getattr(sys, 'tracebacklimit', None)
354            if limit is not None and limit < 0:
355                limit = 0
356        if limit is not None:
357            if limit >= 0:
358                frame_gen = itertools.islice(frame_gen, limit)
359            else:
360                frame_gen = collections.deque(frame_gen, maxlen=-limit)
361
362        result = klass()
363        fnames = set()
364        for f, lineno in frame_gen:
365            co = f.f_code
366            filename = co.co_filename
367            name = co.co_name
368
369            fnames.add(filename)
370            linecache.lazycache(filename, f.f_globals)
371            # Must defer line lookups until we have called checkcache.
372            if capture_locals:
373                f_locals = f.f_locals
374            else:
375                f_locals = None
376            result.append(FrameSummary(
377                filename, lineno, name, lookup_line=False, locals=f_locals))
378        for filename in fnames:
379            linecache.checkcache(filename)
380        # If immediate lookup was desired, trigger lookups now.
381        if lookup_lines:
382            for f in result:
383                f.line
384        return result
385
386    @classmethod
387    def from_list(klass, a_list):
388        """
389        Create a StackSummary object from a supplied list of
390        FrameSummary objects or old-style list of tuples.
391        """
392        # While doing a fast-path check for isinstance(a_list, StackSummary) is
393        # appealing, idlelib.run.cleanup_traceback and other similar code may
394        # break this by making arbitrary frames plain tuples, so we need to
395        # check on a frame by frame basis.
396        result = StackSummary()
397        for frame in a_list:
398            if isinstance(frame, FrameSummary):
399                result.append(frame)
400            else:
401                filename, lineno, name, line = frame
402                result.append(FrameSummary(filename, lineno, name, line=line))
403        return result
404
405    def format(self):
406        """Format the stack ready for printing.
407
408        Returns a list of strings ready for printing.  Each string in the
409        resulting list corresponds to a single frame from the stack.
410        Each string ends in a newline; the strings may contain internal
411        newlines as well, for those items with source text lines.
412
413        For long sequences of the same frame and line, the first few
414        repetitions are shown, followed by a summary line stating the exact
415        number of further repetitions.
416        """
417        result = []
418        last_file = None
419        last_line = None
420        last_name = None
421        count = 0
422        for frame in self:
423            if (last_file is None or last_file != frame.filename or
424                last_line is None or last_line != frame.lineno or
425                last_name is None or last_name != frame.name):
426                if count > _RECURSIVE_CUTOFF:
427                    count -= _RECURSIVE_CUTOFF
428                    result.append(
429                        f'  [Previous line repeated {count} more '
430                        f'time{"s" if count > 1 else ""}]\n'
431                    )
432                last_file = frame.filename
433                last_line = frame.lineno
434                last_name = frame.name
435                count = 0
436            count += 1
437            if count > _RECURSIVE_CUTOFF:
438                continue
439            row = []
440            row.append('  File "{}", line {}, in {}\n'.format(
441                frame.filename, frame.lineno, frame.name))
442            if frame.line:
443                row.append('    {}\n'.format(frame.line.strip()))
444            if frame.locals:
445                for name, value in sorted(frame.locals.items()):
446                    row.append('    {name} = {value}\n'.format(name=name, value=value))
447            result.append(''.join(row))
448        if count > _RECURSIVE_CUTOFF:
449            count -= _RECURSIVE_CUTOFF
450            result.append(
451                f'  [Previous line repeated {count} more '
452                f'time{"s" if count > 1 else ""}]\n'
453            )
454        return result
455
456
457class TracebackException:
458    """An exception ready for rendering.
459
460    The traceback module captures enough attributes from the original exception
461    to this intermediary form to ensure that no references are held, while
462    still being able to fully print or format it.
463
464    Use `from_exception` to create TracebackException instances from exception
465    objects, or the constructor to create TracebackException instances from
466    individual components.
467
468    - :attr:`__cause__` A TracebackException of the original *__cause__*.
469    - :attr:`__context__` A TracebackException of the original *__context__*.
470    - :attr:`__suppress_context__` The *__suppress_context__* value from the
471      original exception.
472    - :attr:`stack` A `StackSummary` representing the traceback.
473    - :attr:`exc_type` The class of the original traceback.
474    - :attr:`filename` For syntax errors - the filename where the error
475      occurred.
476    - :attr:`lineno` For syntax errors - the linenumber where the error
477      occurred.
478    - :attr:`end_lineno` For syntax errors - the end linenumber where the error
479      occurred. Can be `None` if not present.
480    - :attr:`text` For syntax errors - the text where the error
481      occurred.
482    - :attr:`offset` For syntax errors - the offset into the text where the
483      error occurred.
484    - :attr:`end_offset` For syntax errors - the offset into the text where the
485      error occurred. Can be `None` if not present.
486    - :attr:`msg` For syntax errors - the compiler error message.
487    """
488
489    def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
490            lookup_lines=True, capture_locals=False, compact=False,
491            _seen=None):
492        # NB: we need to accept exc_traceback, exc_value, exc_traceback to
493        # permit backwards compat with the existing API, otherwise we
494        # need stub thunk objects just to glue it together.
495        # Handle loops in __cause__ or __context__.
496        is_recursive_call = _seen is not None
497        if _seen is None:
498            _seen = set()
499        _seen.add(id(exc_value))
500
501        # TODO: locals.
502        self.stack = StackSummary.extract(
503            walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines,
504            capture_locals=capture_locals)
505        self.exc_type = exc_type
506        # Capture now to permit freeing resources: only complication is in the
507        # unofficial API _format_final_exc_line
508        self._str = _some_str(exc_value)
509        if exc_type and issubclass(exc_type, SyntaxError):
510            # Handle SyntaxError's specially
511            self.filename = exc_value.filename
512            lno = exc_value.lineno
513            self.lineno = str(lno) if lno is not None else None
514            end_lno = exc_value.end_lineno
515            self.end_lineno = str(end_lno) if end_lno is not None else None
516            self.text = exc_value.text
517            self.offset = exc_value.offset
518            self.end_offset = exc_value.end_offset
519            self.msg = exc_value.msg
520        if lookup_lines:
521            self._load_lines()
522        self.__suppress_context__ = \
523            exc_value.__suppress_context__ if exc_value is not None else False
524
525        # Convert __cause__ and __context__ to `TracebackExceptions`s, use a
526        # queue to avoid recursion (only the top-level call gets _seen == None)
527        if not is_recursive_call:
528            queue = [(self, exc_value)]
529            while queue:
530                te, e = queue.pop()
531                if (e and e.__cause__ is not None
532                    and id(e.__cause__) not in _seen):
533                    cause = TracebackException(
534                        type(e.__cause__),
535                        e.__cause__,
536                        e.__cause__.__traceback__,
537                        limit=limit,
538                        lookup_lines=lookup_lines,
539                        capture_locals=capture_locals,
540                        _seen=_seen)
541                else:
542                    cause = None
543
544                if compact:
545                    need_context = (cause is None and
546                                    e is not None and
547                                    not e.__suppress_context__)
548                else:
549                    need_context = True
550                if (e and e.__context__ is not None
551                    and need_context and id(e.__context__) not in _seen):
552                    context = TracebackException(
553                        type(e.__context__),
554                        e.__context__,
555                        e.__context__.__traceback__,
556                        limit=limit,
557                        lookup_lines=lookup_lines,
558                        capture_locals=capture_locals,
559                        _seen=_seen)
560                else:
561                    context = None
562                te.__cause__ = cause
563                te.__context__ = context
564                if cause:
565                    queue.append((te.__cause__, e.__cause__))
566                if context:
567                    queue.append((te.__context__, e.__context__))
568
569    @classmethod
570    def from_exception(cls, exc, *args, **kwargs):
571        """Create a TracebackException from an exception."""
572        return cls(type(exc), exc, exc.__traceback__, *args, **kwargs)
573
574    def _load_lines(self):
575        """Private API. force all lines in the stack to be loaded."""
576        for frame in self.stack:
577            frame.line
578
579    def __eq__(self, other):
580        if isinstance(other, TracebackException):
581            return self.__dict__ == other.__dict__
582        return NotImplemented
583
584    def __str__(self):
585        return self._str
586
587    def format_exception_only(self):
588        """Format the exception part of the traceback.
589
590        The return value is a generator of strings, each ending in a newline.
591
592        Normally, the generator emits a single string; however, for
593        SyntaxError exceptions, it emits several lines that (when
594        printed) display detailed information about where the syntax
595        error occurred.
596
597        The message indicating which exception occurred is always the last
598        string in the output.
599        """
600        if self.exc_type is None:
601            yield _format_final_exc_line(None, self._str)
602            return
603
604        stype = self.exc_type.__qualname__
605        smod = self.exc_type.__module__
606        if smod not in ("__main__", "builtins"):
607            if not isinstance(smod, str):
608                smod = "<unknown>"
609            stype = smod + '.' + stype
610
611        if not issubclass(self.exc_type, SyntaxError):
612            yield _format_final_exc_line(stype, self._str)
613        else:
614            yield from self._format_syntax_error(stype)
615
616    def _format_syntax_error(self, stype):
617        """Format SyntaxError exceptions (internal helper)."""
618        # Show exactly where the problem was found.
619        filename_suffix = ''
620        if self.lineno is not None:
621            yield '  File "{}", line {}\n'.format(
622                self.filename or "<string>", self.lineno)
623        elif self.filename is not None:
624            filename_suffix = ' ({})'.format(self.filename)
625
626        text = self.text
627        if text is not None:
628            # text  = "   foo\n"
629            # rtext = "   foo"
630            # ltext =    "foo"
631            rtext = text.rstrip('\n')
632            ltext = rtext.lstrip(' \n\f')
633            spaces = len(rtext) - len(ltext)
634            yield '    {}\n'.format(ltext)
635
636            if self.offset is not None:
637                offset = self.offset
638                end_offset = self.end_offset if self.end_offset not in {None, 0} else offset
639                if offset == end_offset or end_offset == -1:
640                    end_offset = offset + 1
641
642                # Convert 1-based column offset to 0-based index into stripped text
643                colno = offset - 1 - spaces
644                end_colno = end_offset - 1 - spaces
645                if colno >= 0:
646                    # non-space whitespace (likes tabs) must be kept for alignment
647                    caretspace = ((c if c.isspace() else ' ') for c in ltext[:colno])
648                    yield '    {}{}'.format("".join(caretspace), ('^' * (end_colno - colno) + "\n"))
649        msg = self.msg or "<no detail available>"
650        yield "{}: {}{}\n".format(stype, msg, filename_suffix)
651
652    def format(self, *, chain=True):
653        """Format the exception.
654
655        If chain is not *True*, *__cause__* and *__context__* will not be formatted.
656
657        The return value is a generator of strings, each ending in a newline and
658        some containing internal newlines. `print_exception` is a wrapper around
659        this method which just prints the lines to a file.
660
661        The message indicating which exception occurred is always the last
662        string in the output.
663        """
664
665        output = []
666        exc = self
667        while exc:
668            if chain:
669                if exc.__cause__ is not None:
670                    chained_msg = _cause_message
671                    chained_exc = exc.__cause__
672                elif (exc.__context__  is not None and
673                      not exc.__suppress_context__):
674                    chained_msg = _context_message
675                    chained_exc = exc.__context__
676                else:
677                    chained_msg = None
678                    chained_exc = None
679
680                output.append((chained_msg, exc))
681                exc = chained_exc
682            else:
683                output.append((None, exc))
684                exc = None
685
686        for msg, exc in reversed(output):
687            if msg is not None:
688                yield msg
689            if exc.stack:
690                yield 'Traceback (most recent call last):\n'
691                yield from exc.stack.format()
692            yield from exc.format_exception_only()
693