Python’s reprlib module provides size-limited repr() strings and protection against infinite recursion. import reprlib. reprlib.repr: reprlib.repr(obj) → abbreviated string; long containers are truncated with .... Repr class: r = reprlib.Repr() — configure per-type limits; r.repr(obj). Limits: maxstring=30, maxlist=6, maxdict=4, maxset=6, maxfrozenset=6, maxdeque=6, maxarray=5, maxlong=40, maxother=30, maxlevel=6 (recursion depth). Custom type: subclass Repr and define repr_typename(self, obj, level) -> str. repr1: r.repr1(obj, level) — single dispatch step used internally. recursive_repr: @reprlib.recursive_repr(fillvalue="...") decorator — returns fillvalue instead of recursing when __repr__ is called on an already-active object (prevents RecursionError in linked lists and circular refs). aRepr: reprlib.aRepr — module-level Repr singleton used by reprlib.repr(). Claude Code generates safe debug loggers, structured log formatters, REPL display helpers, and circular-reference-safe repr methods.
CLAUDE.md for reprlib
## reprlib Stack
- Stdlib: import reprlib
- Quick: reprlib.repr(big_obj) # auto-abbreviated, safe
- Config: r = reprlib.Repr(); r.maxlist = 3; r.repr(obj)
- Safe: @reprlib.recursive_repr() # guards __repr__ from recursion
- Log: logger.debug("state=%s", reprlib.repr(state))
reprlib Safe Repr Pipeline
# app/reprutil.py — abbreviated repr, custom Repr, safe logging, circular-safe
from __future__ import annotations
import reprlib
import logging
from dataclasses import dataclass, fields
from typing import Any
# ─────────────────────────────────────────────────────────────────────────────
# 1. Quick-use abbreviation helpers
# ─────────────────────────────────────────────────────────────────────────────
def short_repr(obj: Any, max_len: int = 60) -> str:
"""
Return an abbreviated repr of obj, truncated to max_len characters.
Example:
short_repr(list(range(1000))) # "[0, 1, 2, 3, 4, 5, ...]"
short_repr("a" * 200) # "'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...'"
"""
r = reprlib.repr(obj)
if len(r) > max_len:
return r[:max_len - 3] + "..."
return r
def safe_repr(obj: Any) -> str:
"""
Return reprlib.repr(obj), falling back to type-name on any exception.
Example:
safe_repr(object_with_broken_repr) # "<BrokenClass instance>"
"""
try:
return reprlib.repr(obj)
except Exception as exc:
return f"<{type(obj).__name__} repr-error: {exc}>"
def type_and_repr(obj: Any) -> str:
"""
Return 'TypeName: <abbreviated repr>' for use in debug logs.
Example:
type_and_repr([1, 2, 3]) # "list: [1, 2, 3]"
type_and_repr({"a": 1}) # "dict: {'a': 1}"
"""
return f"{type(obj).__name__}: {safe_repr(obj)}"
def repr_kwargs(**kwargs: Any) -> str:
"""
Format keyword args as 'key=short_repr(val), ...' for log messages.
Example:
repr_kwargs(data=[1]*100, name="Alice")
# "data=[0, 1, 2, 3, 4, 5, ...], name='Alice'"
"""
return ", ".join(f"{k}={safe_repr(v)}" for k, v in kwargs.items())
# ─────────────────────────────────────────────────────────────────────────────
# 2. Configurable Repr instances
# ─────────────────────────────────────────────────────────────────────────────
def make_repr(
maxlist: int = 6,
maxdict: int = 4,
maxstring: int = 40,
maxset: int = 6,
maxlevel: int = 4,
maxother: int = 50,
) -> reprlib.Repr:
"""
Build a configured Repr instance.
Example:
r = make_repr(maxlist=3, maxstring=20)
r.repr([1, 2, 3, 4, 5]) # '[1, 2, 3, ...]'
"""
r = reprlib.Repr()
r.maxlist = maxlist
r.maxdict = maxdict
r.maxstring = maxstring
r.maxset = maxset
r.maxfrozenset = maxset
r.maxlevel = maxlevel
r.maxother = maxother
return r
# Preconfigured instances for common use cases
COMPACT_REPR = make_repr(maxlist=3, maxdict=2, maxstring=20, maxlevel=2)
VERBOSE_REPR = make_repr(maxlist=20, maxdict=10, maxstring=120, maxlevel=8)
LOG_REPR = make_repr(maxlist=5, maxdict=3, maxstring=60, maxlevel=3)
# ─────────────────────────────────────────────────────────────────────────────
# 3. Recursive-repr guard for custom classes
# ─────────────────────────────────────────────────────────────────────────────
class Node:
"""
A linked-list node that uses @recursive_repr to handle circular references.
Example:
a = Node(1)
b = Node(2)
a.next = b
b.next = a # circular!
repr(a) # "Node(1, next=Node(2, next=...))" — no RecursionError
"""
def __init__(self, value: Any, next_node: "Node | None" = None) -> None:
self.value = value
self.next = next_node
@reprlib.recursive_repr(fillvalue="...")
def __repr__(self) -> str:
if self.next is None:
return f"Node({self.value!r})"
return f"Node({self.value!r}, next={self.next!r})"
class CircularList:
"""
A list-like container that uses @recursive_repr to stay safe.
Example:
cl = CircularList([1, 2, 3])
cl.items.append(cl) # self-reference
repr(cl) # "CircularList([1, 2, 3, ...])"
"""
def __init__(self, items: list[Any] | None = None) -> None:
self.items: list[Any] = list(items or [])
@reprlib.recursive_repr(fillvalue="CircularList(...)")
def __repr__(self) -> str:
inner = reprlib.repr(self.items)
return f"CircularList({inner})"
# ─────────────────────────────────────────────────────────────────────────────
# 4. Safe logging helpers
# ─────────────────────────────────────────────────────────────────────────────
class SafeReprLogger:
"""
A logger wrapper that abbreviates all positional log arguments with reprlib.
Example:
logger = SafeReprLogger("myapp")
logger.debug("Processing: %s", large_object)
# large_object is passed through LOG_REPR before formatting
"""
def __init__(self, name: str, repr_instance: reprlib.Repr | None = None) -> None:
self._log = logging.getLogger(name)
self._repr = repr_instance or LOG_REPR
def _safe_args(self, args: tuple[Any, ...]) -> tuple[str, ...]:
return tuple(self._repr.repr(a) if not isinstance(a, str) else a for a in args)
def debug(self, msg: str, *args: Any) -> None:
if self._log.isEnabledFor(logging.DEBUG):
self._log.debug(msg, *self._safe_args(args))
def info(self, msg: str, *args: Any) -> None:
self._log.info(msg, *self._safe_args(args))
def warning(self, msg: str, *args: Any) -> None:
self._log.warning(msg, *self._safe_args(args))
def error(self, msg: str, *args: Any) -> None:
self._log.error(msg, *self._safe_args(args))
# ─────────────────────────────────────────────────────────────────────────────
# 5. Dataclass and dict summary helpers
# ─────────────────────────────────────────────────────────────────────────────
def dataclass_repr(obj: Any, r: reprlib.Repr | None = None) -> str:
"""
Generate an abbreviated repr for a dataclass instance.
Uses LOG_REPR by default to abbreviate large field values.
Example:
@dataclass
class Job:
name: str
data: list[int]
job = Job("process", list(range(1000)))
dataclass_repr(job) # "Job(name='process', data=[0, 1, 2, 3, 4, 5, ...])"
"""
representer = r or LOG_REPR
try:
fs = fields(obj) # type: ignore[arg-type]
except TypeError:
return repr(obj)
parts = ", ".join(f"{f.name}={representer.repr(getattr(obj, f.name))}" for f in fs)
return f"{type(obj).__name__}({parts})"
def summarize_dict(d: dict[str, Any], max_keys: int = 8, r: reprlib.Repr | None = None) -> str:
"""
Summarize a dict, showing up to max_keys entries with abbreviated values.
Example:
summarize_dict({"a": list(range(100)), "b": "hello " * 50}, max_keys=3)
"""
representer = r or LOG_REPR
keys = list(d)[:max_keys]
truncated = len(d) > max_keys
parts = [f"{k!r}: {representer.repr(d[k])}" for k in keys]
if truncated:
parts.append(f"... ({len(d) - max_keys} more)")
return "{" + ", ".join(parts) + "}"
def format_call(fn_name: str, *args: Any, **kwargs: Any) -> str:
"""
Format a function call signature with abbreviated arguments for logging.
Example:
format_call("process_data", [1]*1000, limit=50, mode="fast")
# "process_data([0, 1, 2, 3, 4, 5, ...], limit=50, mode='fast')"
"""
r = LOG_REPR
arg_parts = [r.repr(a) for a in args]
kwarg_parts = [f"{k}={r.repr(v)}" for k, v in kwargs.items()]
return f"{fn_name}({', '.join(arg_parts + kwarg_parts)})"
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import dataclasses
print("=== reprlib demo ===")
print("\n--- reprlib.repr on large objects ---")
print(f" list(range(100)): {reprlib.repr(list(range(100)))}")
print(f" set(range(20)): {reprlib.repr(set(range(20)))}")
d100 = {i: chr(65 + i % 26) for i in range(100)}
print(f" dict(100 items): {reprlib.repr(d100)}")
print(f" long string: {reprlib.repr('x' * 200)}")
print("\n--- short_repr / safe_repr ---")
print(f" short_repr(list(range(1000))): {short_repr(list(range(1000)))}")
print(f" type_and_repr({{1: 'a', 2: 'b'}}): {type_and_repr({1: 'a', 2: 'b'})}")
print("\n--- configured Repr ---")
r_compact = make_repr(maxlist=2, maxdict=2, maxstring=15)
print(f" compact list(range(10)): {r_compact.repr(list(range(10)))}")
print(f" verbose list(range(10)): {VERBOSE_REPR.repr(list(range(10)))}")
print("\n--- recursive_repr on Node ---")
a = Node(1)
b = Node(2)
c = Node(3)
a.next = b
b.next = c
c.next = a # circular!
print(f" circular node list: {a!r}")
print("\n--- CircularList ---")
cl = CircularList([10, 20, 30])
cl.items.append(cl) # self-reference
print(f" self-referencing list: {cl!r}")
print("\n--- repr_kwargs ---")
big_data = {"records": list(range(500)), "errors": []}
print(f" {repr_kwargs(data=big_data, limit=10, mode='strict')}")
print("\n--- dataclass_repr ---")
@dataclasses.dataclass
class Job:
name: str
payload: list[int]
tags: set[str]
job = Job("compress", list(range(1000)), {"urgent", "batch", "retry", "nightly"})
print(f" {dataclass_repr(job)}")
print("\n--- summarize_dict ---")
big = {f"key_{i}": list(range(i * 10)) for i in range(20)}
print(f" {summarize_dict(big, max_keys=3)}")
print("\n--- format_call ---")
print(f" {format_call('process', list(range(500)), limit=50, mode='fast')}")
print("\n=== done ===")
For the pprint alternative — pprint.pformat() produces fully expanded, indented representations of Python objects without truncation, making it ideal for human inspection of small-to-medium structures; reprlib deliberately truncates to prevent log floods and RecursionError in circular structures — use pprint when you want to see the complete structure in development and debugging; use reprlib in production logging, __repr__ methods for data containers, and anywhere that giant objects could swamp log files or cause runaway recursion. For the rich alternative — rich (PyPI) renders syntax-highlighted, colored, and paginated repr output in the terminal with rich.inspect() and rich.pretty.pprint(); it also provides progress bars, tables, and panels; reprlib is stdlib-only with zero dependencies and focuses on safe size-limited strings — use rich for interactive development, REPL sessions, and CLI tools where colorized output adds value; use reprlib for production code where you need dependency-free abbreviation in log statements and __repr__ methods. The Claude Skills 360 bundle includes reprlib skill sets covering short_repr()/safe_repr()/type_and_repr()/repr_kwargs() quick helpers, make_repr()/COMPACT_REPR/LOG_REPR/VERBOSE_REPR configured instances, Node and CircularList classes with @recursive_repr guard, SafeReprLogger wrapper for abbreviating log arguments, and dataclass_repr()/summarize_dict()/format_call() structured summary tools. Start with the free tier to try safe repr patterns and reprlib pipeline code generation.