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.