Lib/traceback.py
The traceback module is the public face of CPython's exception-printing pipeline.
Every unhandled exception you see in a terminal passes through this file.
In 3.14 the module grew colorization support via _colorize, extended position
metadata (end-line, column, end-column), and a show_group path for
BaseExceptionGroup trees.
Map
| Lines | Symbol | Role |
|---|---|---|
| 29-35 | print_list | Writes a formatted stack list to a file, delegates to StackSummary.from_list |
| 37-49 | format_list | Returns a list of formatted strings from a StackSummary or legacy tuples |
| 55-63 | print_tb | Prints up to limit frames from a traceback object |
| 65-67 | format_tb | One-liner wrapper: format_list(extract_tb(...)) |
| 69-83 | extract_tb | Builds a StackSummary from _walk_tb_with_full_positions |
| 119-134 | print_exception | Top-level print entry point, constructs TracebackException and calls .print() |
| 146-159 | format_exception | Same as print_exception but returns a list of strings |
| 208-214 | print_exc / format_exc | Convenience wrappers around sys.exception() |
| 278-379 | FrameSummary | Holds per-frame metadata: filename, lineno, end_lineno, colno, end_colno, name, locals |
| 382-396 | walk_stack | Generator that walks f.f_back upward, yields (frame, lineno) |
| 399-407 | walk_tb | Generator that walks tb.tb_next, yields (frame, lineno) |
| 410-421 | _walk_tb_with_full_positions | Internal variant of walk_tb that yields 4-tuple positions from co_positions() |
| 434-791 | StackSummary | list subclass; extract, from_list, format, format_frame_summary |
| 459-507 | StackSummary._extract_from_extended_frame_gen | Core extractor, applies tracebacklimit, defers line lookups |
| 528-710 | StackSummary.format_frame_summary | Renders one frame with caret underlining, colorization, and wide-char support |
| 799-810 | _Anchors | Named tuple recording binary-op / bracket anchor positions for caret rendering |
| 812-956 | _extract_caret_anchors_from_line_segment | Parses a code segment with ast to find op/subscript/call bracket positions |
| 1004-1576 | TracebackException | Snapshot class: captures exc type, str, notes, syntax-error fields, chain, and groups |
| 1044-1189 | TracebackException.__init__ | Walks __cause__ / __context__ iteratively to avoid recursion, captures StackSummary |
| 1226-1278 | TracebackException.format_exception_only | Yields the final "ExcType: msg" line plus __notes__ and group children |
| 1468-1566 | TracebackException.format | Main generator: resolves chain order, delegates to stack.format and format_exception_only |
| 1602-1684 | _compute_suggestion_error | Levenshtein-based "Did you mean?" hint for NameError / AttributeError / ImportError |
| 1687-1745 | _levenshtein_distance | Pure-Python port of Python/suggestions.c:levenshtein_distance |
Reading
FrameSummary: capturing position metadata
FrameSummary stores everything needed to reconstruct one line of the "File ..., line N, in func" output.
In 3.14 it gained end_lineno, colno, and end_colno from co_positions() so that
multi-line expressions can be underlined precisely.
# CPython: Lib/traceback.py:292 FrameSummary.__slots__
__slots__ = ('filename', 'lineno', 'end_lineno', 'colno', 'end_colno',
'name', '_lines', '_lines_dedented', 'locals', '_code')
# CPython: Lib/traceback.py:359 FrameSummary._original_lines (property)
@property
def _original_lines(self):
self._set_lines()
return self._lines
# CPython: Lib/traceback.py:373 FrameSummary.line (property)
@property
def line(self):
self._set_lines()
if self._lines is None:
return None
return self._lines.partition("\n")[0].strip()
StackSummary.extract and the extended frame generator
extract is the public API; it wraps the frame generator to add None position fields and
forwards to _extract_from_extended_frame_gen, which is also called directly by
extract_tb with the full 4-tuple positions.
# CPython: Lib/traceback.py:438 StackSummary.extract
@classmethod
def extract(klass, frame_gen, *, limit=None, lookup_lines=True,
capture_locals=False):
def extended_frame_gen():
for f, lineno in frame_gen:
yield f, (lineno, None, None, None)
return klass._extract_from_extended_frame_gen(
extended_frame_gen(), limit=limit, lookup_lines=lookup_lines,
capture_locals=capture_locals)
# CPython: Lib/traceback.py:466 StackSummary._extract_from_extended_frame_gen (limit handling)
builtin_limit = limit is BUILTIN_EXCEPTION_LIMIT
if limit is None or builtin_limit:
limit = getattr(sys, 'tracebacklimit', None)
if limit is not None and limit < 0:
limit = 0
TracebackException: iterative chain walk
The constructor avoids Python recursion on deeply-chained exceptions by using an explicit
queue. The _seen set (keyed by id(exc_value)) breaks __cause__ / __context__ cycles.
# CPython: Lib/traceback.py:1123 TracebackException.__init__ (queue loop)
if not is_recursive_call:
queue = [(self, exc_value)]
while queue:
te, e = queue.pop()
if (e is not None and e.__cause__ is not None
and id(e.__cause__) not in _seen):
cause = TracebackException(
type(e.__cause__), e.__cause__,
e.__cause__.__traceback__, ...)
else:
cause = None
...
te.__cause__ = cause
te.__context__ = context
te.exceptions = exceptions
if cause:
queue.append((te.__cause__, e.__cause__))
format_exc and print_exc convenience wrappers
Both wrappers call sys.exception() to retrieve the current exception from the interpreter
thread state, which replaced the old three-argument sys.exc_info() pattern in 3.12.
# CPython: Lib/traceback.py:208 print_exc
def print_exc(limit=None, file=None, chain=True):
print_exception(sys.exception(), limit=limit, file=file, chain=chain)
# CPython: Lib/traceback.py:212 format_exc
def format_exc(limit=None, chain=True):
return "".join(format_exception(sys.exception(), limit=limit, chain=chain))
gopy notes
FrameSummary maps cleanly to a Go struct with pointer-nullable int fields for the
column positions. The lazy _set_lines / line property pattern should become
a sync.Once-guarded call to linecache.GetLine. StackSummary.format can stay
as a method on []FrameSummary that returns []string.
TracebackException is the harder target: the iterative __cause__ / __context__
queue in __init__ must be reproduced exactly to avoid Go stack overflows on deep chains.
The _Anchors AST walk (_extract_caret_anchors_from_line_segment) depends on a
Python ast parse, which gopy does not yet expose; caret rendering can be stubbed to
plain underlines until that gate is cleared.
_levenshtein_distance at lines 1687-1745 is a self-contained algorithm with no
CPython-internal dependencies and is a straightforward port.