Skip to content

Commit 16ba0a4

Browse files
authored
PEP 813: Address feedback so far in the DPO thread (python#4827)
* Address feedback so far in the DPO thread * Rich treats any false-y name in the 2-tuple form as a positional argument * Describe the new PyObject_Pretty() function * Clarify `name` must be a `str` * For f-strings only (not ``str.format()``) the ``!p`` conversion field takes an optional "format spec". * The PEP no longer proposes a ``pretty`` argument to the ``print()`` built-in function. With the addition of ``!p:callable`` syntax for f-strings, the new argument is unnecessary. * Specify that to pretty print tuples as positional arguments, use the 2-tuple value format, passing a "false-y" value as the argument name. * Clarify that a truth-y ``name`` must be a ``str``. * Specify that the ``!p`` conversion in f-strings and ``str.format()`` implicitly perform an import of the ``pprint`` module. * Describe the new Limited C API function ``PyObject_Pretty()``, and add the optional argument. * Clean up the format-specs-for-f-strings-only language and add a cross reference
1 parent d74fcd3 commit 16ba0a4

1 file changed

Lines changed: 123 additions & 53 deletions

File tree

peps/pep-0813.rst

Lines changed: 123 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ content. However, the existing :mod:`pprint` module can only format builtin obj
3737
By providing a way for classes to customize how their instances participate in pretty printing,
3838
users have more options for visually improving the display of their complex data, especially for debugging.
3939

40-
By extending the built-in :func:`print` function to automatically pretty print its output, debugging with
41-
user-friendly display is made even more convenient. Since no extra imports are required, users can easily
42-
just piggyback on well-worn "print debugging" patterns, at least for the most common use cases.
40+
By adding a ``!p`` conversion specifier to f-strings and ``str.format()``, debugging with user-friendly
41+
display is made even more convenient. Since no extra imports are required, users can easily just piggyback
42+
on well-worn "print debugging" patterns, at least for the most common use cases.
4343

4444
These extensions work both independently and complimentary, to provide powerful new use cases.
4545

46+
.. _specification:
4647

4748
Specification
4849
=============
@@ -70,41 +71,56 @@ class name. The printed representation will usually look like a class construct
7071
keyword, and default arguments. The values can be any of the following formats:
7172

7273
* A single value, representing a positional argument. The value itself is used.
73-
* A 2-tuple of ``(name, value)`` representing a keyword argument. A representation of
74-
``name=value`` is used.
74+
* A 2-tuple of ``(name, value)`` representing a keyword argument. A
75+
representation of ``name=value`` is used. If ``name`` is "false-y", then
76+
``value`` is treated as a positional argument. This is how you would print
77+
a positional argument with a tuple value. See :ref:`examples`. Otherwise,
78+
``name`` **MUST** exactly be a ``str``.
7579
* A 3-tuple of ``(name, value, default_value)`` representing a keyword argument with a default
7680
value. If ``value`` equals ``default_value``, then this tuple is skipped, otherwise
77-
``name=value`` is used.
81+
``name=value`` is used. ``name`` **MUST** exactly be a ``str``.
7882

7983
.. note::
8084

8185
This protocol is compatible with the `Rich library's pretty printing protocol
8286
<https://rich.readthedocs.io/en/latest/pretty.html#rich-repr-protocol>`_.
8387

8488

85-
A new argument to built-in ``print``
86-
------------------------------------
89+
Additions to ``f-strings`` and ``str.format()``
90+
-----------------------------------------------
8791

88-
Built-in :func:`print` takes a new optional argument, appended to the end of the argument list, called
89-
``pretty``, which can take one of the following values:
92+
In addition to the existing ``!s``, ``!r``, and ``!a`` conversion specifiers, a new ``!p``
93+
conversion specifier will be added. The effect of this specifier with an expression ``value`` will
94+
be to call :py:func:`python:pprint.pformat` (importing the ``pprint`` module as needed), passing
95+
``value`` as the only argument.
9096

91-
* ``None`` - the default. No pretty printing is invoked. Fully backward compatible.
92-
* ``True`` - use a temporary instance of the :py:class:`python:pprint.PrettyPrinter` class to get a
93-
pretty representation of the object.
94-
* An instance with a ``pformat()`` method, which has the same signature as
95-
:py:meth:`python:pprint.PrettyPrinter.pformat`. When given, this will usually be an instance of a
96-
subclass of ``PrettyPrinter`` with its ``pformat()`` method overridden. Note that this form
97-
requires **an instance** of a pretty printer, not a class, as only ``print(..., pretty=True)``
98-
performs implicit instantiation.
97+
For f-strings only, the ``!p`` conversion specifier accepts an optional "format spec" expression, after
98+
the normal separating ``:``, for example: ``f'{obj!p:expression}'``. Formally, the expression can
99+
be anything that evaluates to a callable accepting a single argument (the object to format), and
100+
returns a string which is used as the f-string substitution value. Also for f-strings, the ``!p``
101+
specifier is fully compatible with the ``obj=`` form, e.g. ``f'{obj=!p:expression}'``. If no format
102+
spec is given, as above :py:func:`python:pprint.pformat` will be used.
99103

104+
Note that format specs are *not* allowed in ``str.format()`` calls, at least for the :ref:`initial
105+
implementation <deferred>` of this PEP.
100106

101-
Additions to ``f-strings`` and ``str.format()``
102-
-----------------------------------------------
103107

104-
In addition to the existing ``!s``, ``!r``, and ``!a`` conversion specifiers, an additional ``!p`` conversion
105-
will be added. The effect of this specifier with an expression ``value`` will be to call
106-
:py:func:`python:pprint.pformat`, passing ``value`` as the only argument. In this initial specification, it
107-
will be an error to provide any format specifier if ``!p`` is used.
108+
Additions to the C-API
109+
----------------------
110+
111+
To support ``!p``, a new function, ``PyObject_Pretty()`` is added to the
112+
`Limited C API <https://docs.python.org/3/c-api/stable.html#limited-c-api>`_.
113+
This function takes two arguments: a ``PyObject *`` for the object to pretty
114+
print, and an optional ``PyObject *`` for the formatter callable (which may be
115+
``NULL``). When the formatter is ``NULL``, this function imports the ``pprint``
116+
module and calls :func:`pprint.pformat` with the object as its argument,
117+
returning the results. When the formatter is not ``NULL``, it must be a
118+
callable that accepts the object as its single argument and returns a string;
119+
this is used to support the already-evaluated ``:expression`` in
120+
``f'{obj!p:expression}'``.
121+
122+
123+
.. _examples:
108124

109125
Examples
110126
========
@@ -125,7 +141,7 @@ class:
125141
yield 'pickups', self._pickups
126142
yield 'active', self._active, False
127143
128-
Now let's create a couple of instances, and pretty print them:
144+
Now let's create a couple of instances and pretty print them:
129145

130146
.. code-block:: pycon
131147
@@ -137,16 +153,28 @@ Now let's create a couple of instances, and pretty print them:
137153
>>> pprint.pprint(stingray)
138154
Bass(5, pickups='humbucker', active=True)
139155
140-
Here's an example of using the ``pretty`` argument to built-in ``print()``:
156+
The ``!p`` conversion specifier can be used in f-strings and ``str.format()`` to pretty print values:
141157

142158
.. code-block:: pycon
159+
:force:
160+
161+
>>> print(f'{precision=!p}')
162+
precision=Bass(4, pickups='split coil P')
163+
164+
>>> print('{!p}'.format(precision))
165+
Bass(4, pickups='split coil P')
166+
167+
For more complex objects, ``!p`` can help make debugging output more readable:
168+
169+
.. code-block:: pycon
170+
:force:
143171
144172
>>> import os
145173
>>> print(os.pathconf_names)
146174
{'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}
147175
148-
>>> print(os.pathconf_names, pretty=True)
149-
{'PC_ALLOC_SIZE_MIN': 16,
176+
>>> print(f'{os.pathconf_names = !p}')
177+
os.pathconf_names = {'PC_ALLOC_SIZE_MIN': 16,
150178
'PC_ASYNC_IO': 17,
151179
'PC_CHOWN_RESTRICTED': 7,
152180
'PC_FILESIZEBITS': 18,
@@ -167,12 +195,38 @@ Here's an example of using the ``pretty`` argument to built-in ``print()``:
167195
'PC_SYNC_IO': 25,
168196
'PC_VDISABLE': 9}
169197
198+
For f-strings only, the ``!p`` conversion specifier also accepts a format spec expression, which must
199+
evaluate to a callable taking a single argument and returning a string:
200+
201+
.. code-block:: pycon
202+
:force:
203+
204+
>>> def slappa(da: Bass) -> str:
205+
... return 'All about that bass'
206+
207+
>>> print(f'{precision=!p:slappa}')
208+
precision=All about that bass
209+
210+
Here's an example where a positional argument has a tuple value. In this case, you use the 2-tuple format,
211+
with the ``name`` being "false-y".
212+
213+
.. code-block:: pycon
214+
215+
>>> class Things:
216+
... def __pprint__(self):
217+
... yield (None, (1, 2))
218+
... yield ('', (3, 4))
219+
... yield ('arg', (5, 6))
220+
...
221+
>>> from rich.pretty import pprint
222+
>>> pprint(Things())
223+
Things((1, 2), (3, 4), arg=(5, 6))
224+
170225
171226
Backwards Compatibility
172227
=======================
173228

174-
When none of the new features are used, this PEP is fully backward compatible, both for built-in
175-
``print()`` and the ``pprint`` module.
229+
When none of the new features are used, this PEP is fully backward compatible.
176230

177231

178232
Security Implications
@@ -184,7 +238,7 @@ There are no known security implications for this proposal.
184238
How to Teach This
185239
=================
186240

187-
Documentation and examples are added to the ``pprint`` module and the ``print()`` function.
241+
Documentation and examples are added to the ``pprint`` module, f-strings, and ``str.format()``.
188242
Beginners don't need to be taught these new features until they want prettier representations of
189243
their objects.
190244

@@ -199,16 +253,30 @@ branch <https://github.com/warsaw/cpython/tree/pprint>`__.
199253
Rejected Ideas
200254
==============
201255

202-
None at this time.
256+
We considered an alternative :ref:`specification <specification>` of the ``__pprint__()`` return
257+
values, where either :func:`~collections.namedtuple`\s, :mod:`dataclasses`, or a duck-typed instance
258+
were used as the return types. Ultimately we rejected this because we don't want to force folks to
259+
define a new class or add any imports just to return values from this function.
260+
261+
262+
.. _deferred:
263+
264+
Deferred Ideas
265+
==============
266+
267+
In the future, we could add support for ``!p`` conversions to t-strings. Addition of the ``:expression``
268+
format for ``!p`` conversions on ``str.format()`` is also deferred.
203269

204270

205271
Open Issues
206272
===========
207273

208-
The output format and APIs are heavily inspired by `Rich
209-
<rich-repr-protocol_>`_. The idea is that Rich could
210-
implement an API compatible with ``print(..., pretty=RichPrinter)`` fairly easily. Rich's API is designed to
211-
print constructor-like representations of instances, which means that it's not possible to control much of the
274+
Rich compatibility
275+
------------------
276+
277+
The output format and APIs are heavily inspired by `Rich <rich-repr-protocol_>`_. The idea is that Rich could
278+
implement a callable compatible with ``!p:callable`` fairly easily. Rich's API is designed to print
279+
constructor-like representations of instances, which means that it's not possible to control much of the
212280
"class chrome" around the arguments. Rich does support using angle brackets (i.e. ``<...>``) instead of
213281
parentheses by setting the attribute ``.angular=True`` on the rich repr method. This PEP does not support
214282
that feature, although it likely could in the future.
@@ -217,32 +285,34 @@ This also means that there's no way to control the pretty printed format of buil
217285
dicts, lists, etc. This seems fine as ``pprint`` is not intended to be as feature-rich (pun intended!) as
218286
Rich. This PEP purposefully deems such fancy features as out-of-scope.
219287

220-
One consequence of ``print(..., pretty=True)`` is that it can be more less obvious if you wanted to print
221-
multiple objects with, say a newline between the object representations. Compare these two outputs:
222-
223-
.. code-block:: pycon
224-
225-
>>> print(precision, '\n', stingray, pretty=True)
226-
Bass(4, pickups='split coil P') '\n' Bass(5, pickups='humbucker', active=True)
227-
228-
>>> print(precision, stingray, sep='\n', pretty=True)
229-
Bass(4, pickups='split coil P')
230-
Bass(5, pickups='humbucker', active=True)
231-
232-
It's likely you'll want the second output, but more complicated multi-object displays could get even less
233-
convenient and/or more verbose.
234-
235288

236289
Acknowledgments
237290
===============
238291

239-
TBD
292+
Pablo Galindo Salgado for helping the PEP authors prototype the use of and prove the feasibility of
293+
``!p:callable`` for f-strings.
240294

241295

242296
Footnotes
243297
=========
244298

245-
TBD
299+
None at this time.
300+
301+
302+
Change History
303+
==============
304+
305+
* `TBD <TBD>`__
306+
307+
* For f-strings only (not ``str.format()``) the ``!p`` conversion specifier takes an optional "format spec".
308+
* The PEP no longer proposes a ``pretty`` argument to the ``print()`` built-in function. With the
309+
addition of ``!p:callable`` syntax for f-strings, the new argument is unnecessary.
310+
* Specify that to pretty print tuples as positional arguments, use the 2-tuple value format, passing
311+
a "false-y" value as the argument name.
312+
* Clarify that a truth-y ``name`` must be a ``str``.
313+
* Specify that the ``!p`` conversion in f-strings and ``str.format()`` implicitly perform an
314+
import of the ``pprint`` module.
315+
* Describe the new Limited C API function ``PyObject_Pretty()``, and add the optional argument.
246316

247317

248318
Copyright

0 commit comments

Comments
 (0)