blob: 13819f3fef212a3c05896deeeb62fbfba9efb6f4 [file] [log] [blame]
Guido van Rossum45e2fbc1998-03-26 21:13:24 +00001# Author: Fred L. Drake, Jr.
Fred Drake3e5e6612001-10-09 20:53:48 +00002# fdrake@acm.org
Guido van Rossum5e92aff1997-04-16 00:49:59 +00003#
4# This is a simple little module I wrote to make life easier. I didn't
5# see anything quite like it in the library, though I may have overlooked
6# something. I wrote this when I was trying to read some heavily nested
Thomas Wouters7e474022000-07-16 12:04:32 +00007# tuples with fairly non-descriptive content. This is modeled very much
Guido van Rossum5e92aff1997-04-16 00:49:59 +00008# after Lisp/Scheme - style pretty-printing of lists. If you find it
9# useful, thank small children who sleep at night.
10
11"""Support to pretty-print lists, tuples, & dictionaries recursively.
12
13Very simple, but useful, especially in debugging data structures.
14
Fred Drakea89fda01997-04-16 16:59:30 +000015Classes
16-------
17
18PrettyPrinter()
19 Handle pretty-printing operations onto a stream using a configured
20 set of formatting parameters.
21
Guido van Rossum5e92aff1997-04-16 00:49:59 +000022Functions
23---------
24
25pformat()
26 Format a Python object into a pretty-printed representation.
27
28pprint()
Skip Montanaro2dc0c132004-05-14 16:31:56 +000029 Pretty-print a Python object to a stream [default is sys.stdout].
Guido van Rossum5e92aff1997-04-16 00:49:59 +000030
Fred Drakea89fda01997-04-16 16:59:30 +000031saferepr()
32 Generate a 'standard' repr()-like value, but protect against recursive
33 data structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000034
35"""
36
Serhiy Storchakaaa4c36f2015-03-26 08:51:33 +020037import collections as _collections
Lewis Gaul11159d22021-04-14 00:59:24 +010038import dataclasses as _dataclasses
Antoine Pitrou64c16c32013-03-23 20:30:39 +010039import re
Fred Drake397b6152002-12-31 07:14:18 +000040import sys as _sys
Serhiy Storchaka87eb4822015-03-24 19:31:50 +020041import types as _types
Guido van Rossum34d19282007-08-09 01:03:29 +000042from io import StringIO as _StringIO
Guido van Rossum5e92aff1997-04-16 00:49:59 +000043
Skip Montanaroc62c81e2001-02-12 02:00:42 +000044__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
Rémi Lapeyre96831c72019-03-22 18:22:20 +010045 "PrettyPrinter", "pp"]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000046
Fred Drake49cc01e2001-11-01 17:50:38 +000047
Serhiy Storchaka7c411a42013-10-02 11:56:18 +030048def pprint(object, stream=None, indent=1, width=80, depth=None, *,
sblondon3ba3d512021-03-24 09:23:20 +010049 compact=False, sort_dicts=True, underscore_numbers=False):
Skip Montanaro2dc0c132004-05-14 16:31:56 +000050 """Pretty-print a Python object to a stream [default is sys.stdout]."""
Walter Dörwaldc8de4582003-12-03 20:26:05 +000051 printer = PrettyPrinter(
Serhiy Storchaka7c411a42013-10-02 11:56:18 +030052 stream=stream, indent=indent, width=width, depth=depth,
sblondon3ba3d512021-03-24 09:23:20 +010053 compact=compact, sort_dicts=sort_dicts, underscore_numbers=False)
Fred Drakea89fda01997-04-16 16:59:30 +000054 printer.pprint(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000055
Rémi Lapeyre96831c72019-03-22 18:22:20 +010056def pformat(object, indent=1, width=80, depth=None, *,
sblondon3ba3d512021-03-24 09:23:20 +010057 compact=False, sort_dicts=True, underscore_numbers=False):
Fred Drakea89fda01997-04-16 16:59:30 +000058 """Format a Python object into a pretty-printed representation."""
Serhiy Storchaka7c411a42013-10-02 11:56:18 +030059 return PrettyPrinter(indent=indent, width=width, depth=depth,
sblondon3ba3d512021-03-24 09:23:20 +010060 compact=compact, sort_dicts=sort_dicts,
61 underscore_numbers=underscore_numbers).pformat(object)
Rémi Lapeyre96831c72019-03-22 18:22:20 +010062
63def pp(object, *args, sort_dicts=False, **kwargs):
64 """Pretty-print a Python object"""
65 pprint(object, *args, sort_dicts=sort_dicts, **kwargs)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000066
Fred Drakea89fda01997-04-16 16:59:30 +000067def saferepr(object):
68 """Version of repr() which can handle recursive data structures."""
Irit Katrielff420f02020-11-23 13:31:31 +000069 return PrettyPrinter()._safe_repr(object, {}, None, 0)[0]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000070
Tim Petersa814db52001-05-14 07:05:58 +000071def isreadable(object):
72 """Determine if saferepr(object) is readable by eval()."""
Irit Katrielff420f02020-11-23 13:31:31 +000073 return PrettyPrinter()._safe_repr(object, {}, None, 0)[1]
Tim Petersa814db52001-05-14 07:05:58 +000074
75def isrecursive(object):
76 """Determine if object requires a recursive representation."""
Irit Katrielff420f02020-11-23 13:31:31 +000077 return PrettyPrinter()._safe_repr(object, {}, None, 0)[2]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000078
Raymond Hettingera7da1662009-11-19 01:07:05 +000079class _safe_key:
80 """Helper function for key functions when sorting unorderable objects.
81
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +030082 The wrapped-object will fallback to a Py2.x style comparison for
Raymond Hettingera7da1662009-11-19 01:07:05 +000083 unorderable types (sorting first comparing the type name and then by
84 the obj ids). Does not work recursively, so dict.items() must have
85 _safe_key applied to both the key and the value.
86
87 """
88
89 __slots__ = ['obj']
90
91 def __init__(self, obj):
92 self.obj = obj
93
94 def __lt__(self, other):
Florent Xiclunad6da90f2012-07-21 11:17:38 +020095 try:
Serhiy Storchaka62aa7dc2015-04-06 22:52:44 +030096 return self.obj < other.obj
Florent Xiclunad6da90f2012-07-21 11:17:38 +020097 except TypeError:
Serhiy Storchaka62aa7dc2015-04-06 22:52:44 +030098 return ((str(type(self.obj)), id(self.obj)) < \
99 (str(type(other.obj)), id(other.obj)))
Raymond Hettingera7da1662009-11-19 01:07:05 +0000100
101def _safe_tuple(t):
102 "Helper function for comparing 2-tuples"
103 return _safe_key(t[0]), _safe_key(t[1])
104
Fred Drakea89fda01997-04-16 16:59:30 +0000105class PrettyPrinter:
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300106 def __init__(self, indent=1, width=80, depth=None, stream=None, *,
sblondon3ba3d512021-03-24 09:23:20 +0100107 compact=False, sort_dicts=True, underscore_numbers=False):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000108 """Handle pretty printing operations onto a stream using a set of
109 configured parameters.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000110
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000111 indent
112 Number of spaces to indent for each level of nesting.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000113
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000114 width
115 Attempted maximum number of columns in the output.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000116
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000117 depth
118 The maximum depth to print out nested structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000119
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000120 stream
121 The desired output stream. If omitted (or false), the standard
122 output stream available at construction will be used.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000123
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300124 compact
125 If true, several items will be combined in one line.
126
Rémi Lapeyre96831c72019-03-22 18:22:20 +0100127 sort_dicts
128 If true, dict keys are sorted.
129
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000130 """
131 indent = int(indent)
132 width = int(width)
Serhiy Storchakaf3fa3082015-03-26 08:43:21 +0200133 if indent < 0:
134 raise ValueError('indent must be >= 0')
135 if depth is not None and depth <= 0:
136 raise ValueError('depth must be > 0')
137 if not width:
138 raise ValueError('width must be != 0')
Fred Drakee6691ef2002-07-08 12:28:06 +0000139 self._depth = depth
140 self._indent_per_level = indent
141 self._width = width
Raymond Hettinger16e3c422002-06-01 16:07:16 +0000142 if stream is not None:
Fred Drakee6691ef2002-07-08 12:28:06 +0000143 self._stream = stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000144 else:
Fred Drake397b6152002-12-31 07:14:18 +0000145 self._stream = _sys.stdout
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300146 self._compact = bool(compact)
Rémi Lapeyre96831c72019-03-22 18:22:20 +0100147 self._sort_dicts = sort_dicts
sblondon3ba3d512021-03-24 09:23:20 +0100148 self._underscore_numbers = underscore_numbers
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000149
Fred Drakea89fda01997-04-16 16:59:30 +0000150 def pprint(self, object):
Walter Dörwalde62e9362005-11-11 18:18:51 +0000151 self._format(object, self._stream, 0, 0, {}, 0)
152 self._stream.write("\n")
Fred Drakea89fda01997-04-16 16:59:30 +0000153
154 def pformat(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000155 sio = _StringIO()
Fred Drakee6691ef2002-07-08 12:28:06 +0000156 self._format(object, sio, 0, 0, {}, 0)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000157 return sio.getvalue()
Fred Drakea89fda01997-04-16 16:59:30 +0000158
Fred Drakee0ffabe1997-07-18 20:42:39 +0000159 def isrecursive(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000160 return self.format(object, {}, 0, 0)[2]
Fred Drakee0ffabe1997-07-18 20:42:39 +0000161
162 def isreadable(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000163 s, readable, recursive = self.format(object, {}, 0, 0)
Fred Drakeaee113d2002-04-02 05:08:35 +0000164 return readable and not recursive
Fred Drakee0ffabe1997-07-18 20:42:39 +0000165
Fred Drakee6691ef2002-07-08 12:28:06 +0000166 def _format(self, object, stream, indent, allowance, context, level):
Antoine Pitrou7d36e2f2013-10-03 21:29:36 +0200167 objid = id(object)
Fred Drake49cc01e2001-11-01 17:50:38 +0000168 if objid in context:
169 stream.write(_recursion(object))
Fred Drakee6691ef2002-07-08 12:28:06 +0000170 self._recursive = True
171 self._readable = False
Fred Drake49cc01e2001-11-01 17:50:38 +0000172 return
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200173 rep = self._repr(object, context, level)
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200174 max_width = self._width - indent - allowance
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200175 if len(rep) > max_width:
176 p = self._dispatch.get(type(object).__repr__, None)
177 if p is not None:
178 context[objid] = 1
179 p(self, object, stream, indent, allowance, context, level + 1)
180 del context[objid]
181 return
Lewis Gaul11159d22021-04-14 00:59:24 +0100182 elif (_dataclasses.is_dataclass(object) and
183 not isinstance(object, type) and
184 object.__dataclass_params__.repr and
185 # Check dataclass has generated repr method.
186 hasattr(object.__repr__, "__wrapped__") and
187 "__create_fn__" in object.__repr__.__wrapped__.__qualname__):
188 context[objid] = 1
189 self._pprint_dataclass(object, stream, indent, allowance, context, level + 1)
190 del context[objid]
191 return
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200192 stream.write(rep)
193
Lewis Gaul11159d22021-04-14 00:59:24 +0100194 def _pprint_dataclass(self, object, stream, indent, allowance, context, level):
195 cls_name = object.__class__.__name__
196 indent += len(cls_name) + 1
197 items = [(f.name, getattr(object, f.name)) for f in _dataclasses.fields(object) if f.repr]
198 stream.write(cls_name + '(')
199 self._format_namespace_items(items, stream, indent, allowance, context, level)
200 stream.write(')')
201
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200202 _dispatch = {}
203
204 def _pprint_dict(self, object, stream, indent, allowance, context, level):
Fred Drake49cc01e2001-11-01 17:50:38 +0000205 write = stream.write
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200206 write('{')
207 if self._indent_per_level > 1:
208 write((self._indent_per_level - 1) * ' ')
209 length = len(object)
210 if length:
Rémi Lapeyre96831c72019-03-22 18:22:20 +0100211 if self._sort_dicts:
212 items = sorted(object.items(), key=_safe_tuple)
213 else:
214 items = object.items()
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200215 self._format_dict_items(items, stream, indent, allowance + 1,
216 context, level)
217 write('}')
Fred Drakea89fda01997-04-16 16:59:30 +0000218
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200219 _dispatch[dict.__repr__] = _pprint_dict
Serhiy Storchakaaa4c36f2015-03-26 08:51:33 +0200220
221 def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level):
222 if not len(object):
223 stream.write(repr(object))
224 return
225 cls = object.__class__
226 stream.write(cls.__name__ + '(')
227 self._format(list(object.items()), stream,
228 indent + len(cls.__name__) + 1, allowance + 1,
229 context, level)
230 stream.write(')')
231
232 _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
Fred Drakea89fda01997-04-16 16:59:30 +0000233
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200234 def _pprint_list(self, object, stream, indent, allowance, context, level):
235 stream.write('[')
236 self._format_items(object, stream, indent, allowance + 1,
237 context, level)
238 stream.write(']')
Fred Drakea89fda01997-04-16 16:59:30 +0000239
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200240 _dispatch[list.__repr__] = _pprint_list
241
242 def _pprint_tuple(self, object, stream, indent, allowance, context, level):
243 stream.write('(')
244 endchar = ',)' if len(object) == 1 else ')'
245 self._format_items(object, stream, indent, allowance + len(endchar),
246 context, level)
247 stream.write(endchar)
248
249 _dispatch[tuple.__repr__] = _pprint_tuple
250
251 def _pprint_set(self, object, stream, indent, allowance, context, level):
252 if not len(object):
253 stream.write(repr(object))
254 return
255 typ = object.__class__
256 if typ is set:
257 stream.write('{')
258 endchar = '}'
259 else:
260 stream.write(typ.__name__ + '({')
261 endchar = '})'
262 indent += len(typ.__name__) + 1
263 object = sorted(object, key=_safe_key)
264 self._format_items(object, stream, indent, allowance + len(endchar),
265 context, level)
266 stream.write(endchar)
267
268 _dispatch[set.__repr__] = _pprint_set
269 _dispatch[frozenset.__repr__] = _pprint_set
270
271 def _pprint_str(self, object, stream, indent, allowance, context, level):
272 write = stream.write
273 if not len(object):
274 write(repr(object))
275 return
276 chunks = []
277 lines = object.splitlines(True)
278 if level == 1:
279 indent += 1
280 allowance += 1
281 max_width1 = max_width = self._width - indent
282 for i, line in enumerate(lines):
283 rep = repr(line)
284 if i == len(lines) - 1:
285 max_width1 -= allowance
286 if len(rep) <= max_width1:
287 chunks.append(rep)
288 else:
289 # A list of alternating (non-space, space) strings
290 parts = re.findall(r'\S*\s*', line)
291 assert parts
292 assert not parts[-1]
293 parts.pop() # drop empty last part
294 max_width2 = max_width
295 current = ''
296 for j, part in enumerate(parts):
297 candidate = current + part
298 if j == len(parts) - 1 and i == len(lines) - 1:
299 max_width2 -= allowance
300 if len(repr(candidate)) > max_width2:
Serhiy Storchakafe3dc372014-12-20 20:57:15 +0200301 if current:
302 chunks.append(repr(current))
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200303 current = part
304 else:
305 current = candidate
306 if current:
307 chunks.append(repr(current))
308 if len(chunks) == 1:
309 write(rep)
310 return
311 if level == 1:
312 write('(')
313 for i, rep in enumerate(chunks):
314 if i > 0:
315 write('\n' + ' '*indent)
316 write(rep)
317 if level == 1:
318 write(')')
319
320 _dispatch[str.__repr__] = _pprint_str
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000321
Serhiy Storchaka022f2032015-03-24 19:22:37 +0200322 def _pprint_bytes(self, object, stream, indent, allowance, context, level):
323 write = stream.write
324 if len(object) <= 4:
325 write(repr(object))
326 return
327 parens = level == 1
328 if parens:
329 indent += 1
330 allowance += 1
331 write('(')
332 delim = ''
333 for rep in _wrap_bytes_repr(object, self._width - indent, allowance):
334 write(delim)
335 write(rep)
336 if not delim:
337 delim = '\n' + ' '*indent
338 if parens:
339 write(')')
340
341 _dispatch[bytes.__repr__] = _pprint_bytes
342
343 def _pprint_bytearray(self, object, stream, indent, allowance, context, level):
344 write = stream.write
345 write('bytearray(')
346 self._pprint_bytes(bytes(object), stream, indent + 10,
347 allowance + 1, context, level + 1)
348 write(')')
349
350 _dispatch[bytearray.__repr__] = _pprint_bytearray
351
Serhiy Storchaka87eb4822015-03-24 19:31:50 +0200352 def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level):
353 stream.write('mappingproxy(')
354 self._format(object.copy(), stream, indent + 13, allowance + 1,
355 context, level)
356 stream.write(')')
357
358 _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy
359
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200360 def _pprint_simplenamespace(self, object, stream, indent, allowance, context, level):
361 if type(object) is _types.SimpleNamespace:
362 # The SimpleNamespace repr is "namespace" instead of the class
363 # name, so we do the same here. For subclasses; use the class name.
364 cls_name = 'namespace'
365 else:
366 cls_name = object.__class__.__name__
367 indent += len(cls_name) + 1
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200368 items = object.__dict__.items()
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200369 stream.write(cls_name + '(')
Lewis Gaul11159d22021-04-14 00:59:24 +0100370 self._format_namespace_items(items, stream, indent, allowance, context, level)
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200371 stream.write(')')
372
373 _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
374
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200375 def _format_dict_items(self, items, stream, indent, allowance, context,
376 level):
377 write = stream.write
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200378 indent += self._indent_per_level
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200379 delimnl = ',\n' + ' ' * indent
380 last_index = len(items) - 1
381 for i, (key, ent) in enumerate(items):
382 last = i == last_index
383 rep = self._repr(key, context, level)
384 write(rep)
385 write(': ')
386 self._format(ent, stream, indent + len(rep) + 2,
387 allowance if last else 1,
388 context, level)
389 if not last:
390 write(delimnl)
391
Lewis Gaul11159d22021-04-14 00:59:24 +0100392 def _format_namespace_items(self, items, stream, indent, allowance, context, level):
393 write = stream.write
394 delimnl = ',\n' + ' ' * indent
395 last_index = len(items) - 1
396 for i, (key, ent) in enumerate(items):
397 last = i == last_index
398 write(key)
399 write('=')
400 if id(ent) in context:
401 # Special-case representation of recursion to match standard
402 # recursive dataclass repr.
403 write("...")
404 else:
405 self._format(ent, stream, indent + len(key) + 1,
406 allowance if last else 1,
407 context, level)
408 if not last:
409 write(delimnl)
410
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300411 def _format_items(self, items, stream, indent, allowance, context, level):
412 write = stream.write
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200413 indent += self._indent_per_level
414 if self._indent_per_level > 1:
415 write((self._indent_per_level - 1) * ' ')
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300416 delimnl = ',\n' + ' ' * indent
417 delim = ''
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200418 width = max_width = self._width - indent + 1
419 it = iter(items)
420 try:
421 next_ent = next(it)
422 except StopIteration:
423 return
424 last = False
425 while not last:
426 ent = next_ent
427 try:
428 next_ent = next(it)
429 except StopIteration:
430 last = True
431 max_width -= allowance
432 width -= allowance
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300433 if self._compact:
434 rep = self._repr(ent, context, level)
Antoine Pitrou7d36e2f2013-10-03 21:29:36 +0200435 w = len(rep) + 2
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300436 if width < w:
437 width = max_width
438 if delim:
439 delim = delimnl
440 if width >= w:
441 width -= w
442 write(delim)
443 delim = ', '
444 write(rep)
445 continue
446 write(delim)
447 delim = delimnl
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200448 self._format(ent, stream, indent,
449 allowance if last else 1,
450 context, level)
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300451
Fred Drakee6691ef2002-07-08 12:28:06 +0000452 def _repr(self, object, context, level):
Fred Drakeaee113d2002-04-02 05:08:35 +0000453 repr, readable, recursive = self.format(object, context.copy(),
Fred Drakee6691ef2002-07-08 12:28:06 +0000454 self._depth, level)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000455 if not readable:
Fred Drakee6691ef2002-07-08 12:28:06 +0000456 self._readable = False
Tim Petersa814db52001-05-14 07:05:58 +0000457 if recursive:
Fred Drakee6691ef2002-07-08 12:28:06 +0000458 self._recursive = True
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000459 return repr
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000460
Fred Drakeaee113d2002-04-02 05:08:35 +0000461 def format(self, object, context, maxlevels, level):
462 """Format object for a specific context, returning a string
463 and flags indicating whether the representation is 'readable'
464 and whether the object represents a recursive construct.
465 """
Irit Katrielff420f02020-11-23 13:31:31 +0000466 return self._safe_repr(object, context, maxlevels, level)
Fred Drakeaee113d2002-04-02 05:08:35 +0000467
Serhiy Storchakabedbf962015-05-12 13:35:48 +0300468 def _pprint_default_dict(self, object, stream, indent, allowance, context, level):
469 if not len(object):
470 stream.write(repr(object))
471 return
472 rdf = self._repr(object.default_factory, context, level)
473 cls = object.__class__
474 indent += len(cls.__name__) + 1
475 stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent))
476 self._pprint_dict(object, stream, indent, allowance + 1, context, level)
477 stream.write(')')
478
479 _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict
480
481 def _pprint_counter(self, object, stream, indent, allowance, context, level):
482 if not len(object):
483 stream.write(repr(object))
484 return
485 cls = object.__class__
486 stream.write(cls.__name__ + '({')
487 if self._indent_per_level > 1:
488 stream.write((self._indent_per_level - 1) * ' ')
489 items = object.most_common()
490 self._format_dict_items(items, stream,
491 indent + len(cls.__name__) + 1, allowance + 2,
492 context, level)
493 stream.write('})')
494
495 _dispatch[_collections.Counter.__repr__] = _pprint_counter
496
497 def _pprint_chain_map(self, object, stream, indent, allowance, context, level):
498 if not len(object.maps):
499 stream.write(repr(object))
500 return
501 cls = object.__class__
502 stream.write(cls.__name__ + '(')
503 indent += len(cls.__name__) + 1
504 for i, m in enumerate(object.maps):
505 if i == len(object.maps) - 1:
506 self._format(m, stream, indent, allowance + 1, context, level)
507 stream.write(')')
508 else:
509 self._format(m, stream, indent, 1, context, level)
510 stream.write(',\n' + ' ' * indent)
511
512 _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map
513
514 def _pprint_deque(self, object, stream, indent, allowance, context, level):
515 if not len(object):
516 stream.write(repr(object))
517 return
518 cls = object.__class__
519 stream.write(cls.__name__ + '(')
520 indent += len(cls.__name__) + 1
521 stream.write('[')
522 if object.maxlen is None:
523 self._format_items(object, stream, indent, allowance + 2,
524 context, level)
525 stream.write('])')
526 else:
527 self._format_items(object, stream, indent, 2,
528 context, level)
529 rml = self._repr(object.maxlen, context, level)
530 stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml))
531
532 _dispatch[_collections.deque.__repr__] = _pprint_deque
533
534 def _pprint_user_dict(self, object, stream, indent, allowance, context, level):
535 self._format(object.data, stream, indent, allowance, context, level - 1)
536
537 _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict
538
539 def _pprint_user_list(self, object, stream, indent, allowance, context, level):
540 self._format(object.data, stream, indent, allowance, context, level - 1)
541
542 _dispatch[_collections.UserList.__repr__] = _pprint_user_list
543
544 def _pprint_user_string(self, object, stream, indent, allowance, context, level):
545 self._format(object.data, stream, indent, allowance, context, level - 1)
546
547 _dispatch[_collections.UserString.__repr__] = _pprint_user_string
Fred Drakeaee113d2002-04-02 05:08:35 +0000548
Irit Katrielff420f02020-11-23 13:31:31 +0000549 def _safe_repr(self, object, context, maxlevels, level):
550 # Return triple (repr_string, isreadable, isrecursive).
551 typ = type(object)
552 if typ in _builtin_scalars:
553 return repr(object), True, False
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000554
Irit Katrielff420f02020-11-23 13:31:31 +0000555 r = getattr(typ, "__repr__", None)
sblondon3ba3d512021-03-24 09:23:20 +0100556
557 if issubclass(typ, int) and r is int.__repr__:
558 if self._underscore_numbers:
559 return f"{object:_d}", True, False
560 else:
561 return repr(object), True, False
562
Irit Katrielff420f02020-11-23 13:31:31 +0000563 if issubclass(typ, dict) and r is dict.__repr__:
Fred Drake49cc01e2001-11-01 17:50:38 +0000564 if not object:
Irit Katrielff420f02020-11-23 13:31:31 +0000565 return "{}", True, False
566 objid = id(object)
567 if maxlevels and level >= maxlevels:
568 return "{...}", False, objid in context
569 if objid in context:
570 return _recursion(object), False, True
571 context[objid] = 1
572 readable = True
573 recursive = False
574 components = []
575 append = components.append
576 level += 1
577 if self._sort_dicts:
578 items = sorted(object.items(), key=_safe_tuple)
579 else:
580 items = object.items()
581 for k, v in items:
582 krepr, kreadable, krecur = self.format(
583 k, context, maxlevels, level)
584 vrepr, vreadable, vrecur = self.format(
585 v, context, maxlevels, level)
586 append("%s: %s" % (krepr, vrepr))
587 readable = readable and kreadable and vreadable
588 if krecur or vrecur:
589 recursive = True
590 del context[objid]
591 return "{%s}" % ", ".join(components), readable, recursive
Tim Peters88768482001-11-13 21:51:26 +0000592
Irit Katrielff420f02020-11-23 13:31:31 +0000593 if (issubclass(typ, list) and r is list.__repr__) or \
594 (issubclass(typ, tuple) and r is tuple.__repr__):
595 if issubclass(typ, list):
596 if not object:
597 return "[]", True, False
598 format = "[%s]"
599 elif len(object) == 1:
600 format = "(%s,)"
601 else:
602 if not object:
603 return "()", True, False
604 format = "(%s)"
605 objid = id(object)
606 if maxlevels and level >= maxlevels:
607 return format % "...", False, objid in context
608 if objid in context:
609 return _recursion(object), False, True
610 context[objid] = 1
611 readable = True
612 recursive = False
613 components = []
614 append = components.append
615 level += 1
616 for o in object:
617 orepr, oreadable, orecur = self.format(
618 o, context, maxlevels, level)
619 append(orepr)
620 if not oreadable:
621 readable = False
622 if orecur:
623 recursive = True
624 del context[objid]
625 return format % ", ".join(components), readable, recursive
626
627 rep = repr(object)
628 return rep, (rep and not rep.startswith('<')), False
Tim Peters95b3f782001-05-14 18:39:41 +0000629
sblondon3ba3d512021-03-24 09:23:20 +0100630_builtin_scalars = frozenset({str, bytes, bytearray, float, complex,
Serhiy Storchaka8eb1f072015-05-16 21:38:05 +0300631 bool, type(None)})
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000632
Fred Drake49cc01e2001-11-01 17:50:38 +0000633def _recursion(object):
634 return ("<Recursion on %s with id=%s>"
Antoine Pitrou7d36e2f2013-10-03 21:29:36 +0200635 % (type(object).__name__, id(object)))
Fred Drakea89fda01997-04-16 16:59:30 +0000636
Fred Drake49cc01e2001-11-01 17:50:38 +0000637
638def _perfcheck(object=None):
639 import time
640 if object is None:
641 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
642 p = PrettyPrinter()
Victor Stinner8db5b542018-12-17 11:30:34 +0100643 t1 = time.perf_counter()
Irit Katrielff420f02020-11-23 13:31:31 +0000644 p._safe_repr(object, {}, None, 0, True)
Victor Stinner8db5b542018-12-17 11:30:34 +0100645 t2 = time.perf_counter()
Fred Drake49cc01e2001-11-01 17:50:38 +0000646 p.pformat(object)
Victor Stinner8db5b542018-12-17 11:30:34 +0100647 t3 = time.perf_counter()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000648 print("_safe_repr:", t2 - t1)
649 print("pformat:", t3 - t2)
Fred Drake49cc01e2001-11-01 17:50:38 +0000650
Serhiy Storchaka022f2032015-03-24 19:22:37 +0200651def _wrap_bytes_repr(object, width, allowance):
652 current = b''
653 last = len(object) // 4 * 4
654 for i in range(0, len(object), 4):
655 part = object[i: i+4]
656 candidate = current + part
657 if i == last:
658 width -= allowance
659 if len(repr(candidate)) > width:
660 if current:
661 yield repr(current)
662 current = part
663 else:
664 current = candidate
665 if current:
666 yield repr(current)
667
Fred Drake49cc01e2001-11-01 17:50:38 +0000668if __name__ == "__main__":
669 _perfcheck()