Python’s traceback module formats and extracts exception information. import traceback. format_exc: traceback.format_exc() → current exception as string (like what Python prints); returns "None\n" if no active exception. print_exc: traceback.print_exc(file=sys.stderr) — print current traceback. format_exception: traceback.format_exception(exc_type, exc_value, exc_tb) → list of strings. format_exception (3-arg or 1-arg in 3.10+): traceback.format_exception(exc) → list of strings. format_tb: traceback.format_tb(tb) → list of frame strings. extract_tb: traceback.extract_tb(tb) → StackSummary. format_stack: traceback.format_stack() → current stack as list of strings. print_stack: traceback.print_stack() — print current stack. TracebackException: te = traceback.TracebackException.from_exception(exc) — rich structured object; te.format() → iterator; te.__cause__, te.__context__, te.__suppress_context__. StackSummary: list of FrameSummary(filename, lineno, name, line). walk_tb: traceback.walk_tb(tb) → (frame, lineno) iterator. walk_stack: traceback.walk_stack(frame) → current call stack iterator. limit: format_tb(tb, limit=10) — show at most 10 frames. Claude Code generates structured error reporters, exception loggers, Sentry payload builders, and error summary utilities.
CLAUDE.md for traceback
## traceback Stack
- Stdlib: import traceback
- String: traceback.format_exc() # current exception → str
- Struct: traceback.TracebackException.from_exception(exc)
- Frames: traceback.extract_tb(exc.__traceback__) → StackSummary
- Log: logger.error("err", exc_info=True) (preferred for prod)
- Compact: traceback.format_exception_only(type(exc), exc) → one-liner list
traceback Structured Error Pipeline
# app/tbutil.py — structured capture, error reporter, chain formatter, log
from __future__ import annotations
import logging
import sys
import traceback
from dataclasses import dataclass, field
from typing import Any
# ─────────────────────────────────────────────────────────────────────────────
# 1. Basic formatting helpers
# ─────────────────────────────────────────────────────────────────────────────
def exc_string(exc: BaseException | None = None, limit: int | None = None) -> str:
"""
Return a formatted traceback string for exc (or current exception if None).
Example:
try:
risky()
except Exception as e:
msg = exc_string(e)
logger.error(msg)
"""
if exc is None:
return traceback.format_exc()
lines = traceback.format_exception(type(exc), exc, exc.__traceback__, limit=limit)
return "".join(lines)
def exc_oneliner(exc: BaseException) -> str:
"""
Return a single-line exception summary: 'ExcType: message'.
Example:
label = exc_oneliner(e) # "ValueError: invalid literal for int()"
"""
parts = traceback.format_exception_only(type(exc), exc)
return "".join(parts).strip()
def current_stack(limit: int = 10, skip: int = 1) -> str:
"""
Return the current call stack as a formatted string (no exception needed).
Example:
print(current_stack())
"""
lines = traceback.format_stack(limit=limit + skip)
return "".join(lines[skip:])
# ─────────────────────────────────────────────────────────────────────────────
# 2. Structured exception capture
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class FrameInfo:
filename: str
lineno: int
name: str
line: str
@dataclass
class ErrorReport:
exc_type: str
message: str
frames: list[FrameInfo]
cause: "ErrorReport | None" = None
context: "ErrorReport | None" = None
full_text: str = ""
@classmethod
def from_exception(cls, exc: BaseException, limit: int = 20) -> "ErrorReport":
"""
Build a structured ErrorReport from an exception.
Example:
try:
load_data()
except Exception as e:
report = ErrorReport.from_exception(e)
send_to_alerting(report.to_dict())
"""
te = traceback.TracebackException.from_exception(exc, limit=limit)
frames = [
FrameInfo(
filename=fs.filename or "<unknown>",
lineno=fs.lineno or 0,
name=fs.name or "<unknown>",
line=(fs.line or "").strip(),
)
for fs in te.stack
]
cause = cls.from_exception(exc.__cause__) if exc.__cause__ else None
context = (
cls.from_exception(exc.__context__)
if exc.__context__ and not exc.__suppress_context__
else None
)
return cls(
exc_type=te.exc_type.__name__ if te.exc_type else "Exception",
message="".join(te.format_exception_only()).strip(),
frames=frames,
cause=cause,
context=context,
full_text="".join(te.format()),
)
@property
def location(self) -> str:
"""'filename:lineno in function_name' of the innermost frame."""
if self.frames:
f = self.frames[-1]
return f"{f.filename}:{f.lineno} in {f.name}"
return "<unknown>"
def to_dict(self) -> dict[str, Any]:
return {
"exc_type": self.exc_type,
"message": self.message,
"location": self.location,
"frames": [{"file": f.filename, "line": f.lineno, "fn": f.name, "src": f.line} for f in self.frames],
"cause": self.cause.to_dict() if self.cause else None,
"context": self.context.to_dict() if self.context else None,
}
def summary(self) -> str:
parts = [f"{self.exc_type} at {self.location}"]
if self.cause:
parts.append(f" caused by: {self.cause.exc_type} at {self.cause.location}")
if self.context:
parts.append(f" during: {self.context.exc_type} at {self.context.location}")
return "\n".join(parts)
# ─────────────────────────────────────────────────────────────────────────────
# 3. Logging integration
# ─────────────────────────────────────────────────────────────────────────────
def log_exception(
logger: logging.Logger,
exc: BaseException | None = None,
*,
message: str = "Unhandled exception",
level: int = logging.ERROR,
limit: int | None = None,
) -> None:
"""
Log an exception with its full traceback at the given level.
Uses exc_info=True for proper integration with log handlers.
Example:
try:
db.query(sql)
except Exception as e:
log_exception(logger, e, message="DB query failed")
"""
if exc is not None:
logger.log(level, message, exc_info=(type(exc), exc, exc.__traceback__))
else:
logger.log(level, message, exc_info=True)
def log_and_reraise(
logger: logging.Logger,
exc: BaseException,
message: str = "Exception",
) -> None:
"""
Log the exception then re-raise it.
Example:
try:
compute()
except Exception as e:
log_and_reraise(logger, e, "compute() failed")
"""
log_exception(logger, exc, message=message)
raise exc
def safe_call(
fn: Any,
*args: Any,
logger: logging.Logger | None = None,
default: Any = None,
**kwargs: Any,
) -> Any:
"""
Call fn(*args, **kwargs); log and return default on any exception.
Example:
result = safe_call(fetch_data, url, logger=logger, default=[])
"""
try:
return fn(*args, **kwargs)
except Exception as exc:
if logger:
log_exception(logger, exc, message=f"{fn.__name__}() failed")
return default
# ─────────────────────────────────────────────────────────────────────────────
# 4. Exception chain formatting
# ─────────────────────────────────────────────────────────────────────────────
def format_chain(exc: BaseException, indent: int = 0) -> str:
"""
Recursively format an exception chain (cause / context) as a tree.
Example:
try:
...
except Exception as e:
print(format_chain(e))
"""
lines: list[str] = []
pad = " " * indent
if exc.__cause__ is not None:
lines.append(format_chain(exc.__cause__, indent + 1))
lines.append(f"{pad}↳ [caused by above]")
elif exc.__context__ is not None and not exc.__suppress_context__:
lines.append(format_chain(exc.__context__, indent + 1))
lines.append(f"{pad}↳ [during handling of above]")
report = ErrorReport.from_exception(exc)
lines.append(f"{pad}{report.exc_type}: {exc}")
if report.frames:
last = report.frames[-1]
lines.append(f"{pad} at {last.filename}:{last.lineno} in {last.name}()")
if last.line:
lines.append(f"{pad} → {last.line}")
return "\n".join(lines)
# ─────────────────────────────────────────────────────────────────────────────
# 5. Exception hook / global handler
# ─────────────────────────────────────────────────────────────────────────────
def install_global_handler(
logger: logging.Logger | None = None,
reporter: Any = None,
) -> None:
"""
Install a sys.excepthook that logs unhandled exceptions and optionally
calls an external reporter (e.g., Sentry capture_exception).
Example:
install_global_handler(logger=app_logger, reporter=sentry_sdk.capture_exception)
"""
_prev_hook = sys.excepthook
def _hook(exc_type: type, exc_value: BaseException, exc_tb: Any) -> None:
if issubclass(exc_type, KeyboardInterrupt):
_prev_hook(exc_type, exc_value, exc_tb)
return
if logger:
logger.critical(
"Unhandled exception",
exc_info=(exc_type, exc_value, exc_tb),
)
if reporter is not None:
try:
reporter(exc_value)
except Exception:
pass
_prev_hook(exc_type, exc_value, exc_tb)
sys.excepthook = _hook
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG, format="%(levelname)s %(name)s — %(message)s")
log = logging.getLogger("demo")
print("=== traceback demo ===")
print("\n--- exc_string ---")
try:
1 / 0
except ZeroDivisionError as e:
s = exc_string(e)
print(s.strip())
print("\n--- exc_oneliner ---")
try:
int("bad")
except ValueError as e:
print(f" oneliner: {exc_oneliner(e)!r}")
print("\n--- ErrorReport ---")
def inner():
raise KeyError("missing_field")
def outer():
try:
inner()
except KeyError as exc:
raise RuntimeError("processing failed") from exc
try:
outer()
except RuntimeError as e:
report = ErrorReport.from_exception(e)
print(f" type: {report.exc_type}")
print(f" location: {report.location}")
print(f" cause: {report.cause.exc_type if report.cause else None}")
print(f" summary:\n{report.summary()}")
print("\n--- format_chain ---")
try:
outer()
except RuntimeError as e:
chain = format_chain(e)
print(chain)
print("\n--- log_exception ---")
try:
{}["nope"]
except KeyError as e:
log_exception(log, e, message="lookup failed", level=logging.WARNING)
print("\n--- safe_call ---")
result = safe_call(int, "not_a_number", logger=log, default=-1)
print(f" safe_call result: {result}")
print("\n--- current_stack ---")
def show_stack():
return current_stack(limit=4)
print(show_stack())
print("\n=== done ===")
For the sys.exc_info() alternative — sys.exc_info() returns (exc_type, exc_value, exc_traceback) inside an except block; it is the raw low-level accessor; traceback module functions accept these values and produce formatted strings — use sys.exc_info() when you need direct access to the traceback object for introspection or passing to frameworks; use traceback.format_exception() / TracebackException.from_exception() for formatting and structured analysis. For the logging.exception / logger.error(exc_info=True) alternative — logger.error("msg", exc_info=True) or logger.exception("msg") captures the full traceback and attaches it to the log record, letting log handlers (file, Sentry, Datadog) process it appropriately; traceback.format_exc() produces a plain string that can be passed to any logger but loses structured metadata like log level and logger name — prefer logger.error(exc_info=True) in production code; use traceback.format_exc() or TracebackException when you need the string for non-logging outputs (JSON payloads, API responses, terminal UIs). The Claude Skills 360 bundle includes traceback skill sets covering exc_string()/exc_oneliner()/current_stack() format helpers, ErrorReport/FrameInfo structured exception capture with cause and context chaining, log_exception()/log_and_reraise()/safe_call() logging integration, format_chain() recursive chain tree formatter, and install_global_handler() sys.excepthook installer. Start with the free tier to try structured error reporting and traceback pipeline code generation.