Claude Code for pprint: Pretty-Printing in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for pprint: Pretty-Printing in Python
AI

Claude Code for pprint: Pretty-Printing in Python

Published: July 31, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s pprint module formats data structures for human-readable output. import pprint. pprint: pprint.pprint(obj, width=80, depth=None, indent=1, compact=False, sort_dicts=True) — prints formatted to stdout. pformat: pprint.pformat(obj, ...) → formatted string. pp: pprint.pp(obj) — shorthand for pprint(obj, sort_dicts=False) (Python 3.8+). PrettyPrinter: pp_obj = pprint.PrettyPrinter(indent=2, width=120, depth=3) — reusable printer; pp_obj.pprint(obj), pp_obj.pformat(obj). width: max line width before wrapping (default 80). depth: max nesting depth; deeper structures show .... indent: spaces per level (default 1). compact: pack short items onto one line within width. sort_dicts: sort dict keys (default True in pprint, False in pp). isreadable: pprint.isreadable(obj) — True if repr() output is readable. isrecursive: pprint.isrecursive(obj) — True if object contains circular refs. Custom repr: subclass PrettyPrinter, override _repr(object, context, level). Common uses: debug logging of API responses, config inspection, test failure output, nested JSON display. pprint.pformat in logging.debug()logger.debug("response:\n%s", pformat(data)). Claude Code generates debug formatters, config dumpers, test assertion printers, and structured log helpers.

CLAUDE.md for pprint

## pprint Stack
- Stdlib: import pprint
- Quick:   pprint.pp(obj)                         # sort_dicts=False, cleaner
- String:  pprint.pformat(obj, indent=2, width=120)
- Deep:    pprint.pprint(obj, depth=3)            # truncate deep nesting
- Reuse:   pp = pprint.PrettyPrinter(indent=2, width=100)
- Logging: logger.debug("data:\n%s", pprint.pformat(data))

pprint Structured Output Pipeline

# app/pputil.py — pretty-print helpers, log formatter, diff, custom repr
from __future__ import annotations

import json
import logging
import pprint
import textwrap
from dataclasses import asdict, dataclass, field, fields, is_dataclass
from typing import Any


# ─────────────────────────────────────────────────────────────────────────────
# 1. Core formatting helpers
# ─────────────────────────────────────────────────────────────────────────────

# Shared printer: 2-space indent, 120 wide, dicts unsorted (insertion order)
_PP = pprint.PrettyPrinter(indent=2, width=120, sort_dicts=False)


def pp(obj: Any, *, width: int = 120, depth: int | None = None, indent: int = 2) -> None:
    """
    Pretty-print obj to stdout using consistent defaults.

    Example:
        pp(response.json())
        pp(config, depth=3)
    """
    pprint.pprint(obj, width=width, depth=depth, indent=indent, sort_dicts=False)


def pf(obj: Any, *, width: int = 120, depth: int | None = None, indent: int = 2) -> str:
    """
    Return a pretty-formatted string of obj.

    Example:
        msg = f"Config loaded:\n{pf(config)}"
        logger.debug(msg)
    """
    return pprint.pformat(obj, width=width, depth=depth, indent=indent, sort_dicts=False)


def pp_truncated(obj: Any, max_depth: int = 3, width: int = 120) -> None:
    """
    Pretty-print obj truncated at max_depth to avoid overwhelming output.
    Deeper structures appear as ... ellipsis.

    Example:
        pp_truncated(deeply_nested_response, max_depth=2)
    """
    pprint.pprint(obj, depth=max_depth, width=width, sort_dicts=False)


def pp_compact(obj: Any, width: int = 80) -> None:
    """
    Pretty-print with compact mode: short items on same line within width.

    Example:
        pp_compact({"flags": [1, 2, 3, 4, 5, 6, 7, 8]})
    """
    pprint.pprint(obj, compact=True, width=width, sort_dicts=False)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Logging integration
# ─────────────────────────────────────────────────────────────────────────────

def log_data(
    logger: logging.Logger,
    label: str,
    data: Any,
    level: int = logging.DEBUG,
    *,
    depth: int | None = 4,
    width: int = 120,
) -> None:
    """
    Log a pretty-formatted data structure at the given level.

    Example:
        log_data(logger, "API response", response_json)
        log_data(logger, "config", config_dict, level=logging.INFO)
    """
    formatted = pprint.pformat(data, depth=depth, width=width, sort_dicts=False)
    logger.log(level, "%s:\n%s", label, formatted)


def pp_header(label: str, obj: Any, width: int = 60, depth: int | None = None) -> None:
    """
    Print a labeled header bar then pretty-print obj.

    Example:
        pp_header("Request headers", dict(request.headers))
        pp_header("Users", users, depth=2)
    """
    bar = "─" * width
    print(f"\n{bar}")
    print(f"  {label}")
    print(bar)
    pprint.pprint(obj, width=width, depth=depth, sort_dicts=False)


# ─────────────────────────────────────────────────────────────────────────────
# 3. Dataclass printing
# ─────────────────────────────────────────────────────────────────────────────

def pp_dataclass(obj: Any, *, indent: int = 2, width: int = 120, depth: int | None = None) -> None:
    """
    Pretty-print a dataclass instance as its dict representation.

    Example:
        @dataclass
        class Config:
            host: str = "localhost"
            port: int = 8080

        pp_dataclass(Config())
    """
    if is_dataclass(obj) and not isinstance(obj, type):
        pprint.pprint(asdict(obj), indent=indent, width=width, depth=depth, sort_dicts=False)
    else:
        pprint.pprint(obj, indent=indent, width=width, depth=depth, sort_dicts=False)


def dataclass_summary(obj: Any) -> str:
    """
    Return a compact one-line summary of a dataclass's fields and values.

    Example:
        print(dataclass_summary(user))  # "User(id=1, name='Alice', active=True)"
    """
    if not (is_dataclass(obj) and not isinstance(obj, type)):
        return repr(obj)
    cls_name = type(obj).__name__
    parts = [f"{f.name}={getattr(obj, f.name)!r}" for f in fields(obj)]
    return f"{cls_name}({', '.join(parts)})"


# ─────────────────────────────────────────────────────────────────────────────
# 4. JSON and dict comparison
# ─────────────────────────────────────────────────────────────────────────────

def pp_json(data: Any, indent: int = 2) -> None:
    """
    Pretty-print data as formatted JSON (handles non-JSON types via str fallback).
    Better than pprint for JSON-serializable data since it shows plain strings,
    not Python repr.

    Example:
        pp_json(api_response)
    """
    print(json.dumps(data, indent=indent, default=str, ensure_ascii=False))


def pf_json(data: Any, indent: int = 2) -> str:
    """
    Return data as a formatted JSON string.

    Example:
        logger.debug("payload:\n%s", pf_json(payload))
    """
    return json.dumps(data, indent=indent, default=str, ensure_ascii=False)


def dict_diff(a: dict, b: dict) -> dict[str, Any]:
    """
    Return a dict showing keys added, removed, or changed between a and b.

    Example:
        diff = dict_diff(old_config, new_config)
        pp(diff)
    """
    all_keys = set(a) | set(b)
    added   = {k: b[k] for k in all_keys if k not in a}
    removed = {k: a[k] for k in all_keys if k not in b}
    changed = {k: {"old": a[k], "new": b[k]} for k in all_keys if k in a and k in b and a[k] != b[k]}
    return {"added": added, "removed": removed, "changed": changed}


def pp_diff(a: dict, b: dict, label_a: str = "before", label_b: str = "after") -> None:
    """
    Print a labeled diff of two dicts.

    Example:
        pp_diff(old_settings, new_settings)
    """
    diff = dict_diff(a, b)
    pp_header(f"Diff: {label_a}{label_b}", diff)


# ─────────────────────────────────────────────────────────────────────────────
# 5. Table and list display
# ─────────────────────────────────────────────────────────────────────────────

def pp_table(
    rows: list[dict],
    columns: list[str] | None = None,
    max_col_width: int = 30,
) -> None:
    """
    Print a simple ASCII table from a list of dicts.

    Example:
        pp_table(users, columns=["id", "name", "email"])
    """
    if not rows:
        print("  (empty)")
        return
    cols = columns or list(rows[0].keys())
    # compute column widths
    widths = {col: max(len(col), max(len(str(row.get(col, ""))) for row in rows)) for col in cols}
    widths = {col: min(w, max_col_width) for col, w in widths.items()}

    def _cell(val: Any, width: int) -> str:
        s = str(val)
        if len(s) > width:
            s = s[:width - 1] + "…"
        return s.ljust(width)

    header = "  " + "  ".join(_cell(col, widths[col]) for col in cols)
    sep    = "  " + "  ".join("─" * widths[col] for col in cols)
    print(header)
    print(sep)
    for row in rows:
        print("  " + "  ".join(_cell(row.get(col, ""), widths[col]) for col in cols))


def pp_counts(counter: dict[Any, int], top_n: int = 20, label: str = "counts") -> None:
    """
    Print a sorted count table (descending by count).

    Example:
        from collections import Counter
        words = ["the", "cat", "sat", "on", "the", "mat", "the"]
        pp_counts(Counter(words))
    """
    print(f"\n  {label} (top {top_n}):")
    print(f"  {'value':30s}  count")
    print(f"  {'─'*30}  {'─'*8}")
    for val, cnt in sorted(counter.items(), key=lambda x: x[1], reverse=True)[:top_n]:
        print(f"  {str(val):30s}  {cnt:,}")


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

if __name__ == "__main__":
    print("=== pprint demo ===")

    data = {
        "status": "ok",
        "users": [
            {"id": 1, "name": "Alice", "email": "[email protected]", "roles": ["admin", "user"], "score": 98.5},
            {"id": 2, "name": "Bob",   "email": "[email protected]",   "roles": ["user"],           "score": 72.0},
            {"id": 3, "name": "Carol", "email": "[email protected]", "roles": ["user", "mod"],    "score": 88.3},
        ],
        "metadata": {"page": 1, "total": 3, "filters": {"active": True, "dept": "eng"}},
    }

    print("\n--- pp (unsorted, 2-space indent) ---")
    pp(data)

    print("\n--- pp_truncated depth=2 ---")
    pp_truncated(data, max_depth=2)

    print("\n--- pp_compact ---")
    pp_compact({"flags": list(range(1, 16)), "tags": ["python", "stdlib", "dev"]})

    print("\n--- pf (formatted string) ---")
    snippet = pf(data["metadata"])
    print(f"  formatted metadata:\n{textwrap.indent(snippet, '    ')}")

    print("\n--- pp_json ---")
    pp_json(data["users"][0])

    print("\n--- dict_diff ---")
    old = {"host": "localhost", "port": 5432, "debug": False, "timeout": 30}
    new = {"host": "db.prod.com", "port": 5432, "debug": False, "max_retries": 3}
    pp_diff(old, new)

    print("\n--- pp_table ---")
    pp_table(data["users"], columns=["id", "name", "email", "score"])

    print("\n--- pp_counts ---")
    from collections import Counter
    text = "the quick brown fox jumps over the lazy dog the fox"
    word_counts = Counter(text.split())
    pp_counts(word_counts, top_n=5, label="word frequencies")

    print("\n--- pp_dataclass ---")
    @dataclass
    class AppConfig:
        host:    str   = "localhost"
        port:    int   = 8080
        debug:   bool  = False
        tags:    list  = field(default_factory=list)

    cfg = AppConfig(port=9000, debug=True, tags=["web", "api"])
    print(f"  summary: {dataclass_summary(cfg)}")
    print("  pp_dataclass:")
    pp_dataclass(cfg)

    print("\n--- isreadable / isrecursive ---")
    circular: list = []
    circular.append(circular)
    print(f"  isreadable([1,2,3]):  {pprint.isreadable([1,2,3])}")
    print(f"  isrecursive(circular): {pprint.isrecursive(circular)}")

    print("\n--- logging integration ---")
    logging.basicConfig(level=logging.DEBUG, format="%(levelname)s %(message)s")
    log = logging.getLogger("demo")
    log_data(log, "config", cfg.__dict__, level=logging.DEBUG)

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

For the rich alternative — the rich library (PyPI) renders data structures with syntax highlighting, colored tables, progress bars, panels, and trees in the terminal using rich.print(), rich.pretty.install(), and rich.inspect(obj); pprint outputs plain monochrome text — use rich in CLI tools, REPL sessions, and development scripts where visual distinction and color improve readability; use pprint in production log pipelines, test output, and any context where plain-text stdout is expected or color codes would pollute logs. For the json.dumps alternative — json.dumps(obj, indent=2) produces clean human-readable JSON output without Python repr quoting (strings appear as "hello" not 'hello'), but requires all objects to be JSON-serializable (use default=str as a fallback); pprint handles any Python object including sets, tuples, custom classes, and dataclasses — use json.dumps(indent=2) for JSON-serializable API responses, configuration objects, and data that will be parsed by other tools; use pprint for arbitrary runtime Python objects during debugging where JSON compatibility is not guaranteed. The Claude Skills 360 bundle includes pprint skill sets covering pp()/pf()/pp_truncated()/pp_compact() core formatting helpers, log_data()/pp_header() logging integration, pp_dataclass()/dataclass_summary() dataclass printing, pp_json()/pf_json()/dict_diff()/pp_diff() JSON and comparison utilities, and pp_table()/pp_counts() tabular display. Start with the free tier to try structured data inspection and pprint 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