Skip to content

Commit 96831c7

Browse files
Rémi Lapeyrerhettinger
Rémi Lapeyre
authored andcommitted
bpo-30670: Add pp function to the pprint module (GH-11769)
1 parent c5c6cda commit 96831c7

File tree

5 files changed

+84
-35
lines changed

5 files changed

+84
-35
lines changed

Doc/library/pprint.rst

+32-8
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ The :mod:`pprint` module defines one class:
3333
.. index:: single: ...; placeholder
3434

3535
.. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \
36-
compact=False)
36+
compact=False, sort_dicts=True)
3737

3838
Construct a :class:`PrettyPrinter` instance. This constructor understands
3939
several keyword parameters. An output stream may be set using the *stream*
@@ -50,11 +50,17 @@ The :mod:`pprint` module defines one class:
5050
structure cannot be formatted within the constrained width, a best effort will
5151
be made. If *compact* is false (the default) each item of a long sequence
5252
will be formatted on a separate line. If *compact* is true, as many items
53-
as will fit within the *width* will be formatted on each output line.
53+
as will fit within the *width* will be formatted on each output line. If
54+
*sort_dicts* is true (the default), dictionaries will be formatted with their
55+
keys sorted, otherwise they will display in insertion order.
5456

5557
.. versionchanged:: 3.4
5658
Added the *compact* parameter.
5759

60+
.. versionchanged:: 3.8
61+
Added the *sort_dicts* parameter.
62+
63+
5864
>>> import pprint
5965
>>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni']
6066
>>> stuff.insert(0, stuff[:])
@@ -81,29 +87,47 @@ The :mod:`pprint` module defines one class:
8187

8288
The :mod:`pprint` module also provides several shortcut functions:
8389

84-
.. function:: pformat(object, indent=1, width=80, depth=None, *, compact=False)
90+
.. function:: pformat(object, indent=1, width=80, depth=None, *, \
91+
compact=False, sort_dicts=True)
8592

8693
Return the formatted representation of *object* as a string. *indent*,
87-
*width*, *depth* and *compact* will be passed to the :class:`PrettyPrinter`
88-
constructor as formatting parameters.
94+
*width*, *depth*, *compact* and *sort_dicts* will be passed to the
95+
:class:`PrettyPrinter` constructor as formatting parameters.
8996

9097
.. versionchanged:: 3.4
9198
Added the *compact* parameter.
9299

100+
.. versionchanged:: 3.8
101+
Added the *sort_dicts* parameter.
102+
103+
104+
.. function:: pp(object, *args, sort_dicts=False, **kwargs)
105+
106+
Prints the formatted representation of *object* followed by a newline.
107+
If *sort_dicts* is false (the default), dictionaries will be displayed with
108+
their keys in insertion order, otherwise the dict keys will be sorted.
109+
*args* an *kwargs* will be passed to :func:`pprint` as formatting
110+
parameters.
111+
112+
.. versionadded:: 3.8
113+
93114

94115
.. function:: pprint(object, stream=None, indent=1, width=80, depth=None, *, \
95-
compact=False)
116+
compact=False, sort_dicts=True)
96117

97118
Prints the formatted representation of *object* on *stream*, followed by a
98119
newline. If *stream* is ``None``, ``sys.stdout`` is used. This may be used
99120
in the interactive interpreter instead of the :func:`print` function for
100121
inspecting values (you can even reassign ``print = pprint.pprint`` for use
101-
within a scope). *indent*, *width*, *depth* and *compact* will be passed
102-
to the :class:`PrettyPrinter` constructor as formatting parameters.
122+
within a scope). *indent*, *width*, *depth*, *compact* and *sort_dicts* will
123+
be passed to the :class:`PrettyPrinter` constructor as formatting parameters.
103124

104125
.. versionchanged:: 3.4
105126
Added the *compact* parameter.
106127

128+
.. versionchanged:: 3.8
129+
Added the *sort_dicts* parameter.
130+
107131
>>> import pprint
108132
>>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni']
109133
>>> stuff.insert(0, stuff)

Doc/tools/susp-ignored.csv

+9-9
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,15 @@ library/pickle,,:memory,"conn = sqlite3.connect("":memory:"")"
180180
library/posix,,`,"CFLAGS=""`getconf LFS_CFLAGS`"" OPT=""-g -O2 $CFLAGS"""
181181
library/pprint,,::,"'Programming Language :: Python :: 2.6',"
182182
library/pprint,,::,"'Programming Language :: Python :: 2.7',"
183-
library/pprint,225,::,"'classifiers': ['Development Status :: 3 - Alpha',"
184-
library/pprint,225,::,"'Intended Audience :: Developers',"
185-
library/pprint,225,::,"'License :: OSI Approved :: MIT License',"
186-
library/pprint,225,::,"'Programming Language :: Python :: 2',"
187-
library/pprint,225,::,"'Programming Language :: Python :: 3',"
188-
library/pprint,225,::,"'Programming Language :: Python :: 3.2',"
189-
library/pprint,225,::,"'Programming Language :: Python :: 3.3',"
190-
library/pprint,225,::,"'Programming Language :: Python :: 3.4',"
191-
library/pprint,225,::,"'Topic :: Software Development :: Build Tools'],"
183+
library/pprint,,::,"'classifiers': ['Development Status :: 3 - Alpha',"
184+
library/pprint,,::,"'Intended Audience :: Developers',"
185+
library/pprint,,::,"'License :: OSI Approved :: MIT License',"
186+
library/pprint,,::,"'Programming Language :: Python :: 2',"
187+
library/pprint,,::,"'Programming Language :: Python :: 3',"
188+
library/pprint,,::,"'Programming Language :: Python :: 3.2',"
189+
library/pprint,,::,"'Programming Language :: Python :: 3.3',"
190+
library/pprint,,::,"'Programming Language :: Python :: 3.4',"
191+
library/pprint,,::,"'Topic :: Software Development :: Build Tools'],"
192192
library/profile,,:lineno,filename:lineno(function)
193193
library/pyexpat,,:elem1,<py:elem1 />
194194
library/pyexpat,,:py,"xmlns:py = ""http://www.python.org/ns/"">"

Lib/pprint.py

+32-18
Original file line numberDiff line numberDiff line change
@@ -41,33 +41,38 @@
4141
from io import StringIO as _StringIO
4242

4343
__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
44-
"PrettyPrinter"]
44+
"PrettyPrinter", "pp"]
4545

4646

4747
def pprint(object, stream=None, indent=1, width=80, depth=None, *,
48-
compact=False):
48+
compact=False, sort_dicts=True):
4949
"""Pretty-print a Python object to a stream [default is sys.stdout]."""
5050
printer = PrettyPrinter(
5151
stream=stream, indent=indent, width=width, depth=depth,
52-
compact=compact)
52+
compact=compact, sort_dicts=sort_dicts)
5353
printer.pprint(object)
5454

55-
def pformat(object, indent=1, width=80, depth=None, *, compact=False):
55+
def pformat(object, indent=1, width=80, depth=None, *,
56+
compact=False, sort_dicts=True):
5657
"""Format a Python object into a pretty-printed representation."""
5758
return PrettyPrinter(indent=indent, width=width, depth=depth,
58-
compact=compact).pformat(object)
59+
compact=compact, sort_dicts=sort_dicts).pformat(object)
60+
61+
def pp(object, *args, sort_dicts=False, **kwargs):
62+
"""Pretty-print a Python object"""
63+
pprint(object, *args, sort_dicts=sort_dicts, **kwargs)
5964

6065
def saferepr(object):
6166
"""Version of repr() which can handle recursive data structures."""
62-
return _safe_repr(object, {}, None, 0)[0]
67+
return _safe_repr(object, {}, None, 0, True)[0]
6368

6469
def isreadable(object):
6570
"""Determine if saferepr(object) is readable by eval()."""
66-
return _safe_repr(object, {}, None, 0)[1]
71+
return _safe_repr(object, {}, None, 0, True)[1]
6772

6873
def isrecursive(object):
6974
"""Determine if object requires a recursive representation."""
70-
return _safe_repr(object, {}, None, 0)[2]
75+
return _safe_repr(object, {}, None, 0, True)[2]
7176

7277
class _safe_key:
7378
"""Helper function for key functions when sorting unorderable objects.
@@ -97,7 +102,7 @@ def _safe_tuple(t):
97102

98103
class PrettyPrinter:
99104
def __init__(self, indent=1, width=80, depth=None, stream=None, *,
100-
compact=False):
105+
compact=False, sort_dicts=True):
101106
"""Handle pretty printing operations onto a stream using a set of
102107
configured parameters.
103108
@@ -117,6 +122,9 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *,
117122
compact
118123
If true, several items will be combined in one line.
119124
125+
sort_dicts
126+
If true, dict keys are sorted.
127+
120128
"""
121129
indent = int(indent)
122130
width = int(width)
@@ -134,6 +142,7 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *,
134142
else:
135143
self._stream = _sys.stdout
136144
self._compact = bool(compact)
145+
self._sort_dicts = sort_dicts
137146

138147
def pprint(self, object):
139148
self._format(object, self._stream, 0, 0, {}, 0)
@@ -184,7 +193,10 @@ def _pprint_dict(self, object, stream, indent, allowance, context, level):
184193
write((self._indent_per_level - 1) * ' ')
185194
length = len(object)
186195
if length:
187-
items = sorted(object.items(), key=_safe_tuple)
196+
if self._sort_dicts:
197+
items = sorted(object.items(), key=_safe_tuple)
198+
else:
199+
items = object.items()
188200
self._format_dict_items(items, stream, indent, allowance + 1,
189201
context, level)
190202
write('}')
@@ -402,7 +414,7 @@ def format(self, object, context, maxlevels, level):
402414
and flags indicating whether the representation is 'readable'
403415
and whether the object represents a recursive construct.
404416
"""
405-
return _safe_repr(object, context, maxlevels, level)
417+
return _safe_repr(object, context, maxlevels, level, self._sort_dicts)
406418

407419
def _pprint_default_dict(self, object, stream, indent, allowance, context, level):
408420
if not len(object):
@@ -487,7 +499,7 @@ def _pprint_user_string(self, object, stream, indent, allowance, context, level)
487499

488500
# Return triple (repr_string, isreadable, isrecursive).
489501

490-
def _safe_repr(object, context, maxlevels, level):
502+
def _safe_repr(object, context, maxlevels, level, sort_dicts):
491503
typ = type(object)
492504
if typ in _builtin_scalars:
493505
return repr(object), True, False
@@ -507,11 +519,13 @@ def _safe_repr(object, context, maxlevels, level):
507519
components = []
508520
append = components.append
509521
level += 1
510-
saferepr = _safe_repr
511-
items = sorted(object.items(), key=_safe_tuple)
522+
if sort_dicts:
523+
items = sorted(object.items(), key=_safe_tuple)
524+
else:
525+
items = object.items()
512526
for k, v in items:
513-
krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
514-
vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
527+
krepr, kreadable, krecur = _safe_repr(k, context, maxlevels, level, sort_dicts)
528+
vrepr, vreadable, vrecur = _safe_repr(v, context, maxlevels, level, sort_dicts)
515529
append("%s: %s" % (krepr, vrepr))
516530
readable = readable and kreadable and vreadable
517531
if krecur or vrecur:
@@ -543,7 +557,7 @@ def _safe_repr(object, context, maxlevels, level):
543557
append = components.append
544558
level += 1
545559
for o in object:
546-
orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
560+
orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level, sort_dicts)
547561
append(orepr)
548562
if not oreadable:
549563
readable = False
@@ -569,7 +583,7 @@ def _perfcheck(object=None):
569583
object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
570584
p = PrettyPrinter()
571585
t1 = time.perf_counter()
572-
_safe_repr(object, {}, None, 0)
586+
_safe_repr(object, {}, None, 0, True)
573587
t2 = time.perf_counter()
574588
p.pformat(object)
575589
t3 = time.perf_counter()

Lib/test/test_pprint.py

+7
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def test_init(self):
8181
pp = pprint.PrettyPrinter(indent=4, width=40, depth=5,
8282
stream=io.StringIO(), compact=True)
8383
pp = pprint.PrettyPrinter(4, 40, 5, io.StringIO())
84+
pp = pprint.PrettyPrinter(sort_dicts=False)
8485
with self.assertRaises(TypeError):
8586
pp = pprint.PrettyPrinter(4, 40, 5, io.StringIO(), True)
8687
self.assertRaises(ValueError, pprint.PrettyPrinter, indent=-1)
@@ -293,6 +294,12 @@ def test_sorted_dict(self):
293294
self.assertEqual(pprint.pformat({"xy\tab\n": (3,), 5: [[]], (): {}}),
294295
r"{5: [[]], 'xy\tab\n': (3,), (): {}}")
295296

297+
def test_sort_dict(self):
298+
d = dict.fromkeys('cba')
299+
self.assertEqual(pprint.pformat(d, sort_dicts=False), "{'c': None, 'b': None, 'a': None}")
300+
self.assertEqual(pprint.pformat([d, d], sort_dicts=False),
301+
"[{'c': None, 'b': None, 'a': None}, {'c': None, 'b': None, 'a': None}]")
302+
296303
def test_ordered_dict(self):
297304
d = collections.OrderedDict()
298305
self.assertEqual(pprint.pformat(d, width=1), 'OrderedDict()')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
`pprint.pp` has been added to pretty-print objects with dictionary
2+
keys being sorted with their insertion order by default. Parameter
3+
*sort_dicts* has been added to `pprint.pprint`, `pprint.pformat` and
4+
`pprint.PrettyPrinter`. Contributed by Rémi Lapeyre.

0 commit comments

Comments
 (0)