Skip to main content

Lib/traceback.py

cpython 3.14 @ ab2d84fe1023/Lib/traceback.py

traceback.py is a pure-Python module that converts live exception objects and frame chains into serializable data structures, then formats them as human-readable text. It is the backend for the default exception hook (sys.excepthook), for unittest failure output, and for any code that needs to capture or pretty-print tracebacks without re-raising.

The module is structured in three layers:

  1. Low-level walkers: walk_stack and walk_tb are generators that produce (frame, lineno) pairs by following f_back or tb_next chains.

  2. Capture structures: FrameSummary stores a single frame's filename, line number, function name, and the source text. StackSummary is a list of FrameSummary objects with a class method extract that calls one of the walkers.

  3. Formatting: TracebackException recursively captures __cause__, __context__, and __suppress_context__ at construction time and provides format() / format_exc() generators. Top-level helpers format_exception, format_tb, print_exception, and print_tb delegate to these structures.

PEP 657 (fine-grained error locations) added _ExceptionPrintContext in 3.11 to carry the caret annotation state across the recursive formatting calls.

Map

LinesSymbolRolegopy
1-60Module header, __all__, importsPublic surface and sentinel constants.module/traceback/module.go
61-120print_tb, format_tb, extract_tbTop-level helpers that format or print a tb chain.module/traceback/module.go
121-180print_exception, format_exception, format_exc, print_lastFormat a complete exception (chain + traceback + message).module/traceback/module.go
181-250walk_stack, walk_tbGenerators that yield (frame, lineno) by walking f_back / tb_next.module/traceback/module.go
251-360FrameSummary, StackSummaryFrameSummary stores one frame; StackSummary.extract captures a walk into a list.module/traceback/module.go
361-420_ExceptionPrintContextCarries indentation depth and caret-annotation state (PEP 657) through recursive TracebackException.format calls.module/traceback/module.go
421-520TracebackException.__init__Recursively captures exception chain (__cause__, __context__, __suppress_context__) and extracts StackSummary.module/traceback/module.go
521-600TracebackException.format, format_stack, format_exception_onlyYield formatted lines for the full chain, a single stack, or only the exception line.module/traceback/module.go

Reading

TracebackException.__init__ — exception chaining capture (lines 421 to 520)

cpython 3.14 @ ab2d84fe1023/Lib/traceback.py#L421-520

class TracebackException:
def __init__(self, exc_type, exc_value, exc_tb, *,
limit=None, lookup_lines=True,
capture_locals=False, compact=False,
max_group_width=15, max_group_depth=10,
_seen=None):
if _seen is None:
_seen = set()
_seen.add(id(exc_value))

if exc_value and exc_value.__cause__ is not None:
cause = exc_value.__cause__
if id(cause) not in _seen:
cause = TracebackException(
type(cause), cause, cause.__traceback__,
..., _seen=_seen)
else:
cause = None
else:
cause = None

if exc_value and exc_value.__context__ is not None:
context = exc_value.__context__
if id(context) not in _seen:
context = TracebackException(
type(context), context, context.__traceback__,
..., _seen=_seen)
else:
context = None
else:
context = None

self.__cause__ = cause
self.__context__ = context
self.__suppress_context__ = \
exc_value.__suppress_context__ if exc_value else False
...
self.stack = StackSummary.extract(
walk_tb(exc_tb), limit=limit, lookup_lines=lookup_lines,
capture_locals=capture_locals)

The constructor captures both __cause__ and __context__ recursively, passing a _seen set to break cycles (an exception can appear multiple times in a chain if it was raised, caught, and re-raised). Cycle detection uses id(exc_value) rather than identity so that even replaced exception objects are recognized.

StackSummary.extract(walk_tb(exc_tb)) drives walk_tb to convert the live tb_next chain into a StackSummary at construction time. This means the frames are snapshotted immediately: source lines are read while the files are still accessible, and local variable values (if capture_locals=True) are repr-ed before they go out of scope.

format_exception chain walk (lines 521 to 600)

cpython 3.14 @ ab2d84fe1023/Lib/traceback.py#L521-600

def format(self, *, chain=True, _ctx=None):
if _ctx is None:
_ctx = _ExceptionPrintContext()

output = []
exc = self
while True:
if chain:
if exc.__cause__ is not None:
yield from exc.__cause__.format(chain=chain, _ctx=_ctx)
yield _cause_message
elif (exc.__context__ is not None
and not exc.__suppress_context__):
yield from exc.__context__.format(chain=chain, _ctx=_ctx)
yield _context_message
if exc.exc_traceback is not None:
yield 'Traceback (most recent call last):\n'
yield from exc.stack.format()
yield from exc.format_exception_only()
break

format walks the chain from the innermost __cause__ or __context__ outward, yielding the chaining header ("The above exception was the direct cause..." or "During handling of the above exception...") between each pair. __suppress_context__ is checked so that raise B from None (which sets __suppress_context__ = True) suppresses the implicit context chain. The _ExceptionPrintContext object tracks how deeply nested the format call is (for exception groups) and whether PEP 657 caret annotations have been emitted for each frame.

StackSummary.extract (lines 251 to 360)

cpython 3.14 @ ab2d84fe1023/Lib/traceback.py#L251-360

class StackSummary(list):
@classmethod
def extract(cls, frame_gen, *, limit=None, lookup_lines=True,
capture_locals=False):
def extended_frame_gen():
for f, lineno in frame_gen:
yield f, (f.f_lineno, f.f_lasti)

result = cls()
fnames = {}
for f, (lineno, lasti) in itertools.islice(
extended_frame_gen(), limit):
co = f.f_code
filename = co.co_filename
name = co.co_qualname
...
if lookup_lines:
linecache.checkcache(filename)
locals_ = f.f_locals if capture_locals else None
result.append(FrameSummary(
filename, lineno, name,
lookup_line=lookup_lines,
locals=locals_,
lasti=lasti,
))
return result

extract drives any (frame, lineno) generator (usually walk_tb or walk_stack) and converts each frame into a FrameSummary. It records f_lasti (the last bytecode offset) alongside the line number so that PEP 657 fine-grained error annotations can later compute column offsets. linecache.checkcache is called per unique filename to ensure the source cache is current. When capture_locals is true, f.f_locals is taken at this moment; the values are repr-ed lazily inside FrameSummary to defer expensive repr calls until the traceback is actually formatted.