How Rich's Traceback Handler Captures and Displays Exception Information
Rich's traceback handler intercepts uncaught exceptions by replacing Python's sys.excepthook with a custom callback that walks the raw traceback to extract frame data, then renders syntax-highlighted source code and optional local variables through a console-aware pipeline.
The Textualize/rich library transforms standard Python error reports into detailed, colorized terminal output. Understanding how Rich's traceback handler captures and displays exception information reveals a sophisticated pipeline that bridges low-level CPython internals with high-level terminal rendering.
Hook Installation and Interception
Rich begins by replacing the default exception display mechanism with its own renderer.
Replacing the Global Exception Hook
The entry point is the install() function. When called, it stores the existing sys.excepthook and substitutes a custom handler. This new hook receives the exception type, value, and raw traceback object whenever an uncaught error bubbles to the top of the stack.
In IPython or Jupyter environments, Rich detects the shell and patches IPython._showtraceback instead (see the integration logic at lines 1010‑1025). This ensures the rich output appears even in interactive notebooks that bypass the standard CPython hook.
Capturing and Structuring Exception Data
Once the hook triggers, Rich converts the raw traceback into a structured, renderable object.
Building the Traceback Object
The handler immediately delegates to Traceback.from_exception(). This class method accepts the exception triple (type_, value, traceback) and optional configuration flags such as show_locals, width, and suppress.
Walking the Stack
Inside from_exception, the extract() method (lines [332‑424]) performs the heavy lifting. It uses walk_tb() to iterate over every frame in the traceback chain, building a list of Frame objects. Each Frame captures:
- Filename, line number, and function name
- The source line text retrieved via
linecache - A boolean indicating whether the frame is part of the user's code or a library
- Optional local variables (when enabled)
The extractor respects frame-local markers such as _rich_traceback_omit and _rich_traceback_guard, allowing libraries to hide internal frames or reset the traceback guard. It also filters out any paths matching the suppress list passed to install().
Enriching the Display
After structuring the data, Rich enhances each frame with visual context.
Syntax Highlighting
For every frame, Rich reads the source file and creates a Syntax object with guessed lexer via _guess_lexer. This happens in the rendering phase (see the Syntax creation around line 842), which produces colorized code with line numbers aligned to the error location.
Local Variable Inspection
When show_locals=True, Rich traverses the frame's f_locals using pretty.traverse (lines 588‑595). The render_scope helper formats these variables into a compact, syntax-highlighted panel, respecting options like locals_max_length and locals_max_string to prevent massive objects from flooding the terminal.
Rendering to the Terminal
The final stage converts the structured data into printable renderables.
The Console Protocol
The Traceback class implements __rich_console__, starting at line 626. This method constructs a hierarchy of Panel and Group objects, wrapping each stack frame in a visual container. It temporarily applies a custom Theme that maps Pygments tokens to Rich style names (defined at lines 633‑653), ensuring consistent coloring for keywords, strings, and comments across different syntax themes.
Final Output
The installed exception hook obtains a dedicated console instance and invokes print(exception_traceback) (line 164), streaming the fully rendered panels to stderr or the configured output stream.
Configuration and Usage Examples
Enable Rich tracebacks with a single call at the start of your application.
from rich.traceback import install
install()
raise ValueError("Demonstration error")
To include local variables and suppress specific library paths:
from rich.traceback import install
install(
show_locals=True,
suppress=["/usr/local/lib/python3.11/site-packages"]
)
def failing_function():
data = {"key": "value"}
raise RuntimeError("Error with context")
failing_function()
For manual control outside the global hook:
import sys
from rich.console import Console
from rich.traceback import Traceback
try:
1 / 0
except ZeroDivisionError:
console = Console()
tb = Traceback.from_exception(*sys.exc_info(), show_locals=True)
console.print(tb)
Summary
- Hook Replacement: Rich's
install()function replacessys.excepthook(and IPython's equivalent) to intercept all uncaught exceptions. - Data Extraction: The handler walks the raw traceback using
walk_tb()and builds Frame and Stack objects inextract(), respecting filters like_rich_traceback_omit. - Visual Enrichment: Source code is highlighted via the
Syntaxclass, and locals are rendered usingpretty.traversewhen enabled. - Console Rendering: The Traceback object implements
__rich_console__to generate themed panels, printed to the terminal via the console protocol.
Frequently Asked Questions
How do I suppress specific library frames from the traceback?
Pass a list of path strings or modules to the suppress argument in install(). For example, install(suppress=[click, "path/to/library"]) hides frames originating from those locations, decluttering the output to show only your application code.
Does Rich's handler work in Jupyter notebooks?
Yes. Rich detects IPython environments and patches IPython._showtraceback automatically (see lines 1010‑1025). This allows the same colorized, local-variable-aware tracebacks to appear in notebook cells without any additional configuration.
Can I display local variables for every frame?
Set show_locals=True when calling install(). This triggers the local variable renderer for every frame, using pretty.traverse to format objects. You can limit output size with locals_max_length and locals_max_string parameters to avoid overwhelming the terminal with large data structures.
How do I revert to the default Python traceback handler?
Store the return value of install() (the original hook) and reassign it to sys.excepthook when needed. The install() function returns the previous exception hook, allowing you to restore standard behavior by running sys.excepthook = original_hook.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →