Claude Code for cgitb: Python CGI Traceback Handler — Claude Skills 360 Blog
Blog / AI / Claude Code for cgitb: Python CGI Traceback Handler
AI

Claude Code for cgitb: Python CGI Traceback Handler

Published: November 6, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s cgitb module generates rich, annotated tracebacks showing source code lines, local variable values, and full exception chains — originally for CGI scripts, but useful for any application error reporting. import cgitb. Enable globally: cgitb.enable() — installs as sys.excepthook; unhandled exceptions produce an HTML report. cgitb.enable(display=True, logdir=None, context=5, format="html")display=False suppresses browser output; logdir writes to .txt files in that directory; context controls lines of source shown. Text format: cgitb.enable(format="text") — plain text instead of HTML. Manual use: cgitb.html(exc_info_tuple, context=5) → HTML string; cgitb.text(exc_info_tuple, context=5) → plain-text string. Exception hook: cgitb.handler(exc_info_tuple) — same as the installed excepthook. cgitb.reset() — outputs a closing </div> tag after partial HTML output. cgitb.Hook — the class installed by enable(). Note: deprecated 3.11, removed 3.13 — include compatibility guard. Claude Code generates rich exception reporters, development debug pages, structured traceback loggers, and CI failure annotators.

CLAUDE.md for cgitb

## cgitb Stack
- Stdlib: import cgitb  (deprecated 3.11, removed 3.13 — guard with try/except)
- Global: cgitb.enable()                    # HTML to browser on exception
-         cgitb.enable(format="text")       # plain text
-         cgitb.enable(logdir="/var/log")   # write to .txt files + stdout
- Manual: html = cgitb.html(sys.exc_info(), context=5)
-         text = cgitb.text(sys.exc_info(), context=5)
- Note:   Great for development; do NOT use in production — exposes source code

cgitb Rich Traceback Reporter Pipeline

# app/cgitbutil.py — enable, manual text/HTML, logging, structured reporter
from __future__ import annotations

import io
import logging
import os
import sys
import traceback
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

# Guard for Python 3.13+ where cgitb is removed
try:
    import cgitb as _cgitb
    _CGITB_AVAILABLE = True
except ImportError:
    _CGITB_AVAILABLE = False


# ─────────────────────────────────────────────────────────────────────────────
# 1. Enable globally
# ─────────────────────────────────────────────────────────────────────────────

def enable_html_tracebacks(
    context: int = 5,
    logdir: str | None = None,
    display: bool = True,
) -> None:
    """
    Install cgitb as sys.excepthook to generate HTML tracebacks on exception.
    Only useful in HTTP-serving CGI scripts or development servers.

    Example:
        enable_html_tracebacks(logdir="/var/log/myapp")
    """
    if not _CGITB_AVAILABLE:
        logger.warning("cgitb not available; using default traceback handler")
        return
    _cgitb.enable(display=display, logdir=logdir,
                 context=context, format="html")


def enable_text_tracebacks(
    context: int = 5,
    logdir: str | None = None,
) -> None:
    """
    Install cgitb as sys.excepthook to generate annotated plain-text tracebacks.

    Example:
        enable_text_tracebacks()
        raise ValueError("something exploded")
    """
    if not _CGITB_AVAILABLE:
        logger.warning("cgitb not available; using default traceback handler")
        return
    _cgitb.enable(display=True, logdir=logdir, context=context, format="text")


# ─────────────────────────────────────────────────────────────────────────────
# 2. Manual traceback generation
# ─────────────────────────────────────────────────────────────────────────────

def get_text_traceback(
    exc_info: tuple | None = None,
    context: int = 5,
) -> str:
    """
    Generate a detailed plain-text traceback string from an exc_info tuple.
    If exc_info is None, uses sys.exc_info() (call from inside an except block).
    Falls back to stdlib traceback.format_exc() if cgitb unavailable.

    Example:
        try:
            risky_operation()
        except Exception:
            text = get_text_traceback()
            logger.error("Operation failed:\n%s", text)
    """
    if exc_info is None:
        exc_info = sys.exc_info()

    if not _CGITB_AVAILABLE:
        return traceback.format_exc()

    try:
        return _cgitb.text(exc_info, context=context)
    except Exception:
        return traceback.format_exc()


def get_html_traceback(
    exc_info: tuple | None = None,
    context: int = 5,
) -> str:
    """
    Generate a complete HTML traceback page from an exc_info tuple.
    Falls back to plain-text wrapped in <pre> if cgitb unavailable.

    Example:
        try:
            risky_operation()
        except Exception:
            html = get_html_traceback()
            send_debug_email(html)
    """
    if exc_info is None:
        exc_info = sys.exc_info()

    if not _CGITB_AVAILABLE:
        text = traceback.format_exc()
        return f"<pre>{text}</pre>"

    try:
        return _cgitb.html(exc_info, context=context)
    except Exception:
        text = traceback.format_exc()
        return f"<pre>{text}</pre>"


# ─────────────────────────────────────────────────────────────────────────────
# 3. File-based traceback logger
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class TracebackLogEntry:
    timestamp:    str
    exc_type:     str
    exc_message:  str
    log_file:     Path | None
    text_report:  str

    def __str__(self) -> str:
        return (f"[{self.timestamp}] {self.exc_type}: "
                f"{self.exc_message[:80]}")


class TracebackFileLogger:
    """
    Writes annotated traceback reports to a log directory.
    Works with or without cgitb.

    Example:
        tbl = TracebackFileLogger("/var/log/myapp/tracebacks")
        try:
            risky_operation()
        except Exception:
            entry = tbl.log()
            print(f"Traceback written to {entry.log_file}")
    """

    def __init__(
        self,
        logdir: str | Path,
        context: int = 5,
        max_files: int = 100,
    ) -> None:
        self.logdir = Path(logdir)
        self.logdir.mkdir(parents=True, exist_ok=True)
        self.context = context
        self.max_files = max_files

    def log(self, exc_info: tuple | None = None) -> TracebackLogEntry:
        """
        Log the current exception to a file. Call from inside an except block.

        Example:
            try:
                risky()
            except Exception:
                entry = tbl.log()
        """
        if exc_info is None:
            exc_info = sys.exc_info()

        exc_type, exc_value, _ = exc_info
        ts = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
        exc_name = exc_type.__name__ if exc_type else "UnknownError"
        fname = f"{ts}_{exc_name}.txt"
        log_path = self.logdir / fname

        text_report = get_text_traceback(exc_info, context=self.context)
        log_path.write_text(text_report, encoding="utf-8")

        self._cleanup_old_files()

        return TracebackLogEntry(
            timestamp=ts,
            exc_type=exc_name,
            exc_message=str(exc_value) if exc_value else "",
            log_file=log_path,
            text_report=text_report,
        )

    def _cleanup_old_files(self) -> None:
        """Remove the oldest log files when max_files is exceeded."""
        files = sorted(self.logdir.glob("*.txt"), key=lambda p: p.stat().st_mtime)
        while len(files) > self.max_files:
            files.pop(0).unlink(missing_ok=True)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Structured exception reporter (extracts locals for inspection)
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class FrameInfo:
    filename: str
    lineno:   int
    function: str
    locals_:  dict[str, str]   # repr() of local variables

    def __str__(self) -> str:
        locals_str = "  ".join(f"{k}={v}" for k, v in list(self.locals_.items())[:5])
        return (f"  File {self.filename!r}, line {self.lineno}, in {self.function}\n"
                f"    locals: {locals_str}")


@dataclass
class StructuredTraceback:
    exc_type:    str
    exc_message: str
    frames:      list[FrameInfo]
    chained:     "StructuredTraceback | None" = None

    def summary(self) -> str:
        return (f"{self.exc_type}: {self.exc_message}\n"
                + "\n".join(str(f) for f in self.frames[-3:]))


def structured_traceback(exc_info: tuple | None = None) -> StructuredTraceback:
    """
    Extract a StructuredTraceback with local variable values from an exc_info.
    Works without cgitb by using the inspect module directly.

    Example:
        try:
            risky()
        except Exception:
            st = structured_traceback()
            for frame in st.frames:
                print(frame)
    """
    import inspect

    if exc_info is None:
        exc_info = sys.exc_info()

    exc_type, exc_value, exc_tb = exc_info
    if exc_type is None:
        return StructuredTraceback("", "", [])

    frames: list[FrameInfo] = []
    tb = exc_tb
    while tb is not None:
        frame = tb.tb_frame
        locals_repr = {
            k: repr(v)[:120]
            for k, v in frame.f_locals.items()
            if not k.startswith("__")
        }
        frames.append(FrameInfo(
            filename=frame.f_code.co_filename,
            lineno=tb.tb_lineno,
            function=frame.f_code.co_name,
            locals_=locals_repr,
        ))
        tb = tb.tb_next

    chained = None
    if exc_value and exc_value.__cause__:
        try:
            chained = structured_traceback(
                (type(exc_value.__cause__), exc_value.__cause__,
                 exc_value.__cause__.__traceback__)
            )
        except Exception:
            pass

    return StructuredTraceback(
        exc_type=exc_type.__name__,
        exc_message=str(exc_value),
        frames=frames,
        chained=chained,
    )


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    import tempfile
    print("=== cgitb demo ===")

    if not _CGITB_AVAILABLE:
        print("  cgitb not available (Python 3.13+); using fallback implementations")

    # ── synthesize an exception with local variables ───────────────────────────
    def level3(data: dict) -> None:
        result = data["missing_key"]   # KeyError

    def level2(items: list) -> None:
        config = {"timeout": 30, "retries": 3}
        level3(config)

    def level1() -> None:
        items = [1, 2, 3]
        level2(items)

    # ── get_text_traceback ─────────────────────────────────────────────────────
    print("\n--- get_text_traceback ---")
    try:
        level1()
    except Exception:
        text = get_text_traceback()
        # Print first 30 lines
        lines = text.splitlines()
        for line in lines[:30]:
            print(f"  {line}")
        if len(lines) > 30:
            print(f"  ... ({len(lines) - 30} more lines)")

    # ── get_html_traceback ────────────────────────────────────────────────────
    print("\n--- get_html_traceback ---")
    try:
        level1()
    except Exception:
        html = get_html_traceback()
        print(f"  HTML length: {len(html)} chars")
        print(f"  starts with: {html[:60]!r}")

    # ── TracebackFileLogger ────────────────────────────────────────────────────
    print("\n--- TracebackFileLogger ---")
    with tempfile.TemporaryDirectory() as tmp:
        tbl = TracebackFileLogger(tmp, context=3)
        for i in range(3):
            try:
                x = 1 / (i - 1)   # ZeroDivisionError on i=1
            except Exception:
                entry = tbl.log()
                print(f"  [{i}] {entry}")
                print(f"      file: {entry.log_file.name}")
        files = list(Path(tmp).glob("*.txt"))
        print(f"  {len(files)} traceback file(s) written")

    # ── structured_traceback ──────────────────────────────────────────────────
    print("\n--- structured_traceback ---")
    try:
        try:
            1 / 0
        except ZeroDivisionError as e:
            raise ValueError("bad input") from e
    except Exception:
        st = structured_traceback()
        print(f"  exc_type: {st.exc_type}")
        print(f"  message:  {st.exc_message}")
        for frame in st.frames:
            print(f"  {frame.filename}:{frame.lineno}  {frame.function}")
        if st.chained:
            print(f"  caused by: {st.chained.exc_type}: {st.chained.exc_message}")

    print("\n=== done ===")

For the rich (PyPI) alternative — from rich.traceback import install; install() produces beautifully colored, syntax-highlighted tracebacks with local variable values in any terminal, and rich.console.Console().print_exception() can generate rich reports without modifying sys.excepthook — use rich for modern terminal applications and CLI tools where you want syntax highlighting and clean variable display without HTML; use cgitb (or the fallback implementations above) when generating HTML exception reports for development web servers or when you want file-based traceback logging in a stdlib-only environment. For the traceback alternative — stdlib traceback.format_exc(), traceback.print_exc(), traceback.TracebackException.from_exception(), and traceback.format_exception() provide structured access to the full exception chain with chained exception support — use traceback when you need structured programmatic access to exception metadata, or when cgitb is unavailable (Python 3.13+); the structured_traceback() function above uses inspect directly to add local variable extraction that plain traceback does not expose. The Claude Skills 360 bundle includes cgitb skill sets covering enable_html_tracebacks()/enable_text_tracebacks() global hooks, get_text_traceback()/get_html_traceback() manual reporters, TracebackFileLogger with timestamped file output, and StructuredTraceback / structured_traceback() for programmatic exception inspection. Start with the free tier to try rich traceback patterns and cgitb pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free