|
| 1 | +PEP: 813 |
| 2 | +Title: The Pretty Print Protocol |
| 3 | +Author: Barry Warsaw <barry@python.org>, |
| 4 | + Eric V. Smith <eric at trueblade.com> |
| 5 | +Discussions-To: Pending |
| 6 | +Status: Draft |
| 7 | +Type: Standards Track |
| 8 | +Created: 07-Nov-2025 |
| 9 | +Python-Version: 3.15 |
| 10 | +Post-History: Pending |
| 11 | + |
| 12 | + |
| 13 | +Abstract |
| 14 | +======== |
| 15 | + |
| 16 | +This PEP describes the "pretty print protocol", a collection of changes proposed to make pretty printing more |
| 17 | +customizable and convenient. |
| 18 | + |
| 19 | + |
| 20 | +Motivation |
| 21 | +========== |
| 22 | + |
| 23 | +"Pretty printing" is a feature which provides a capability to format object representations for better |
| 24 | +readability. The core functionality is implemented by the standard library :mod:`pprint`. ``pprint`` |
| 25 | +includes a class and APIs which users can invoke to format and print more readable representations of objects. |
| 26 | +Important use cases include pretty printing large dictionaries and other complicated objects. |
| 27 | + |
| 28 | +The ``pprint`` module is great as far as it goes. This PEP builds on the features of this module to provide |
| 29 | +more customization and convenience. |
| 30 | + |
| 31 | + |
| 32 | +Rationale |
| 33 | +========= |
| 34 | + |
| 35 | +Pretty printing is very useful for displaying complex data structures, like dictionaries read from JSON |
| 36 | +content. By providing a way for classes to customize how their instances participate in pretty printing, |
| 37 | +users have more options for visually improving the display and debugging of their complex data. |
| 38 | + |
| 39 | +By extending the built-in :py:func:`print` function to automatically pretty print its output, this feature is |
| 40 | +made even more convenient, since no extra imports are required, and users can easily just piggyback on |
| 41 | +well-worn "print debugging" patterns, at least for the most common use cases. |
| 42 | + |
| 43 | +These two extensions work independently, but hand-in-hand can provide a powerful and convenient new feature. |
| 44 | + |
| 45 | + |
| 46 | +Specification |
| 47 | +============= |
| 48 | + |
| 49 | +There are two parts to this proposal. |
| 50 | + |
| 51 | + |
| 52 | +``__pretty__()`` methods |
| 53 | +------------------------ |
| 54 | + |
| 55 | +Classes can implement a new dunder method, ``__pretty__()`` which if present, generates the pretty printed |
| 56 | +representation of their instances. This augments ``__repr__()`` which, prior to this proposal, was the only |
| 57 | +method used to generate a pretty representation of the object. Since object reprs provide functionality |
| 58 | +distinct from pretty printing, some classes may want more control over their pretty display. |
| 59 | + |
| 60 | +``__pretty__()`` is optional; if missing, the standard pretty printers fall back to ``__repr__()`` for full |
| 61 | +backward compatibility (technically speaking, :meth:`pprint.saferepr` is used). However, if defined on a |
| 62 | +class, ``__pretty__()`` has the same argument signature as :py:func:`PrettyPrinter.format`, taking four |
| 63 | +arguments: |
| 64 | + |
| 65 | +* ``object`` - the object to print, which is effectively always ``self`` |
| 66 | +* ``context`` - a dictionary mapping the ``id()`` of objects which are part of the current presentation |
| 67 | + context |
| 68 | +* ``maxlevels`` - the requested limit to recursion |
| 69 | +* ``levels`` - the current recursion level |
| 70 | + |
| 71 | +Similarly, ``__pretty__()`` returns three values, the string to be used as the pretty printed representation, |
| 72 | +a boolean indicating whether the returned value is "readable", and a boolean indicating whether recursion has |
| 73 | +been detected. In this context, "readable" means the same as :meth:`PrettyPrinter.isreadable`, i.e. that the |
| 74 | +returned value can be used to reconstruct the original object using ``eval()``. |
| 75 | + |
| 76 | +See :py:func:`PrettyPrinter.format` for details. |
| 77 | + |
| 78 | + |
| 79 | +A new argument to built-in ``print`` |
| 80 | +------------------------------------ |
| 81 | + |
| 82 | +Built-in :py:func:`print` takes a new optional argument, appended to the end of the argument list, called |
| 83 | +``pretty``, which can take one of the following values: |
| 84 | + |
| 85 | +* ``None`` - the default; fully backward compatible |
| 86 | +* ``True`` - use a temporary instance of the :py:class:`PrettyPrinter` class to get a pretty representation of |
| 87 | + the object. |
| 88 | +* An instance with a ``pformat()`` method, which has the same signature as |
| 89 | + :meth:`PrettyPrinter.pformat`. When given, this will usually be an instance of a subclass of |
| 90 | + `PrettyPrinter` with its `pformat()` method overridden. Note that this form requires **an |
| 91 | + instance** of a pretty printer, not a class, as only ``print(..., pretty=True)`` performs implicit |
| 92 | + instantiation. |
| 93 | + |
| 94 | + |
| 95 | +Examples |
| 96 | +======== |
| 97 | + |
| 98 | +A custom ``__pprint__()`` method can be used to customize the representation of the object: |
| 99 | + |
| 100 | +.. _code-block: |
| 101 | + |
| 102 | + >>> class Custom: |
| 103 | + ... def __str__(self): return 'my str' |
| 104 | + ... def __repr__(self): return 'my repr' |
| 105 | + ... def __pprint__(self, context, maxlevels, level): return 'my pprint' |
| 106 | + |
| 107 | + >>> pprint.pp(Custom()) |
| 108 | + my pprint |
| 109 | + |
| 110 | +Using the ``pretty`` argument to ``print()``: |
| 111 | + |
| 112 | +.. _code-block: |
| 113 | + |
| 114 | + >>> import os |
| 115 | + >>> print(os.pathconf_names) |
| 116 | + {'PC_ASYNC_IO': 17, 'PC_CHOWN_RESTRICTED': 7, 'PC_FILESIZEBITS': 18, 'PC_LINK_MAX': 1, 'PC_MAX_CANON': 2, 'PC_MAX_INPUT': 3, 'PC_NAME_MAX': 4, 'PC_NO_TRUNC': 8, 'PC_PATH_MAX': 5, 'PC_PIPE_BUF': 6, 'PC_PRIO_IO': 19, 'PC_SYNC_IO': 25, 'PC_VDISABLE': 9, 'PC_MIN_HOLE_SIZE': 27, 'PC_ALLOC_SIZE_MIN': 16, 'PC_REC_INCR_XFER_SIZE': 20, 'PC_REC_MAX_XFER_SIZE': 21, 'PC_REC_MIN_XFER_SIZE': 22, 'PC_REC_XFER_ALIGN': 23, 'PC_SYMLINK_MAX': 24} |
| 117 | + >>> print(os.pathconf_names, pretty=True) |
| 118 | + {'PC_ALLOC_SIZE_MIN': 16, |
| 119 | + 'PC_ASYNC_IO': 17, |
| 120 | + 'PC_CHOWN_RESTRICTED': 7, |
| 121 | + 'PC_FILESIZEBITS': 18, |
| 122 | + 'PC_LINK_MAX': 1, |
| 123 | + 'PC_MAX_CANON': 2, |
| 124 | + 'PC_MAX_INPUT': 3, |
| 125 | + 'PC_MIN_HOLE_SIZE': 27, |
| 126 | + 'PC_NAME_MAX': 4, |
| 127 | + 'PC_NO_TRUNC': 8, |
| 128 | + 'PC_PATH_MAX': 5, |
| 129 | + 'PC_PIPE_BUF': 6, |
| 130 | + 'PC_PRIO_IO': 19, |
| 131 | + 'PC_REC_INCR_XFER_SIZE': 20, |
| 132 | + 'PC_REC_MAX_XFER_SIZE': 21, |
| 133 | + 'PC_REC_MIN_XFER_SIZE': 22, |
| 134 | + 'PC_REC_XFER_ALIGN': 23, |
| 135 | + 'PC_SYMLINK_MAX': 24, |
| 136 | + 'PC_SYNC_IO': 25, |
| 137 | + 'PC_VDISABLE': 9} |
| 138 | + |
| 139 | + |
| 140 | +Backwards Compatibility |
| 141 | +======================= |
| 142 | + |
| 143 | +When none of the new features are used, this PEP is fully backward compatible, both for built-in |
| 144 | +``print()`` and the ``pprint`` module. |
| 145 | + |
| 146 | + |
| 147 | +Security Implications |
| 148 | +===================== |
| 149 | + |
| 150 | +There are no known security implications for this proposal. |
| 151 | + |
| 152 | + |
| 153 | +How to Teach This |
| 154 | +================= |
| 155 | + |
| 156 | +Documentation and examples are added to the ``pprint`` module and the ``print()`` function. |
| 157 | +Beginners don't need to be taught these new features until they want prettier representations of |
| 158 | +their objects. |
| 159 | + |
| 160 | + |
| 161 | +Reference Implementation |
| 162 | +======================== |
| 163 | + |
| 164 | +The reference implementation is currently available as a `PEP author branch of the CPython main |
| 165 | +branch <https://github.com/warsaw/cpython/tree/pprint>`__. |
| 166 | + |
| 167 | + |
| 168 | +Rejected Ideas |
| 169 | +============== |
| 170 | + |
| 171 | +None at this time. |
| 172 | + |
| 173 | + |
| 174 | +Open Issues |
| 175 | +=========== |
| 176 | + |
| 177 | +TBD |
| 178 | + |
| 179 | +Acknowledgements |
| 180 | +================ |
| 181 | + |
| 182 | +TBD |
| 183 | + |
| 184 | + |
| 185 | +Footnotes |
| 186 | +========= |
| 187 | + |
| 188 | +TBD |
| 189 | + |
| 190 | + |
| 191 | +Copyright |
| 192 | +========= |
| 193 | + |
| 194 | +This document is placed in the public domain or under the |
| 195 | +CC0-1.0-Universal license, whichever is more permissive. |
0 commit comments