How Python pprint Formats Nested Data Structures

Python’s built-in pprint (pretty-print) module formats complex nested data structures into clean, human-readable representations by intelligently managing line breaks, indentation, and depth. Unlike standard print(), which dumps data into a single continuous stream, pprint calculates character widths and nesting levels to output organized, visually structured text. This article explains the core mechanics behind how pprint analyzes structure, wraps lines, enforces depth limits, and safely handles recursive references.

Line Width and Dynamic Wrapping

The primary mechanism behind pprint is its width-calculation algorithm. By default, pprint targets an 80-character line width, controlled by the width parameter.

When evaluating a data structure, the module first checks if the entire object fits within the remaining horizontal space on the current line. If it fits, pprint formats it inline:

import pprint

data = {"status": "ok", "codes": [200, 201]}
pprint.pprint(data, width=40)

If an object exceeds the allowed width, the formatter recursively splits child elements onto separate lines, placing each item on its own row with an opening and closing delimiter.

Indentation and Hierarchy

To reflect hierarchy in nested dictionaries, lists, and tuples, pprint adjusts indentation at each nesting level.

nested_data = {
    "user": {
        "profile": {
            "id": 42,
            "roles": ["admin", "editor", "moderator"]
        }
    }
}
pprint.pprint(nested_data, indent=2, width=30)

In this case, each nested level shifts right by two spaces, clearly distinguishing parents from children.

Limiting Output Depth

When dealing with deeply nested JSON payloads or tree structures, formatting every single layer can clutter output. The depth parameter restricts how far down pprint will traverse:

pprint.pprint(nested_data, depth=2)

Any container located deeper than the specified threshold is replaced with an ellipsis (...), showing the high-level layout without rendering every terminal node.

Deterministic Dictionary Ordering

In debugging, consistent output is critical. By default, pprint sorts dictionary keys alphabetically (controlled via sort_dicts=True). Even though standard Python dictionaries preserve insertion order, sorting ensures that identical data structures always produce the exact same visual representation, regardless of how keys were inserted. To preserve insertion order, set sort_dicts=False.

Packing Sequences with the Compact Parameter

For collections containing many small elements, standard vertical expansion can lead to excessive scrolling. The compact=True parameter changes the line-wrapping strategy:

numbers = list(range(30))
pprint.pprint(numbers, width=40, compact=True)

Instead of printing each number on a new line, pprint packs as many items as possible onto a single line before wrapping to the next, maintaining the 40-character boundary while minimizing vertical space.

Detecting Recursive References

Standard recursive printing functions can enter infinite loops when processing self-referential structures (where an object references itself). The pprint module keeps an internal registry of visited object IDs (id()). If an object is encountered a second time within the same traversal chain, pprint stops recursion and prints <Recursion on type with id=...>, preventing stack overflow errors.