blob: d91421f0a6bf60a5a79b0c46050941d58637f180 [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,
Miss Islington (bot)6b75ad52021-10-21 14:16:59 -070053 compact=compact, sort_dicts=sort_dicts,
54 underscore_numbers=underscore_numbers)
Fred Drakea89fda01997-04-16 16:59:30 +000055 printer.pprint(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000056
Rémi Lapeyre96831c72019-03-22 18:22:20 +010057def pformat(object, indent=1, width=80, depth=None, *,
sblondon3ba3d512021-03-24 09:23:20 +010058 compact=False, sort_dicts=True, underscore_numbers=False):
Fred Drakea89fda01997-04-16 16:59:30 +000059 """Format a Python object into a pretty-printed representation."""
Serhiy Storchaka7c411a42013-10-02 11:56:18 +030060 return PrettyPrinter(indent=indent, width=width, depth=depth,
sblondon3ba3d512021-03-24 09:23:20 +010061 compact=compact, sort_dicts=sort_dicts,
62 underscore_numbers=underscore_numbers).pformat(object)
Rémi Lapeyre96831c72019-03-22 18:22:20 +010063
64def pp(object, *args, sort_dicts=False, **kwargs):
65 """Pretty-print a Python object"""
66 pprint(object, *args, sort_dicts=sort_dicts, **kwargs)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000067
Fred Drakea89fda01997-04-16 16:59:30 +000068def saferepr(object):
69 """Version of repr() which can handle recursive data structures."""
Irit Katrielff420f02020-11-23 13:31:31 +000070 return PrettyPrinter()._safe_repr(object, {}, None, 0)[0]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000071
Tim Petersa814db52001-05-14 07:05:58 +000072def isreadable(object):
73 """Determine if saferepr(object) is readable by eval()."""
Irit Katrielff420f02020-11-23 13:31:31 +000074 return PrettyPrinter()._safe_repr(object, {}, None, 0)[1]
Tim Petersa814db52001-05-14 07:05:58 +000075
76def isrecursive(object):
77 """Determine if object requires a recursive representation."""
Irit Katrielff420f02020-11-23 13:31:31 +000078 return PrettyPrinter()._safe_repr(object, {}, None, 0)[2]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000079
Raymond Hettingera7da1662009-11-19 01:07:05 +000080class _safe_key:
81 """Helper function for key functions when sorting unorderable objects.
82
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +030083 The wrapped-object will fallback to a Py2.x style comparison for
Raymond Hettingera7da1662009-11-19 01:07:05 +000084 unorderable types (sorting first comparing the type name and then by
85 the obj ids). Does not work recursively, so dict.items() must have
86 _safe_key applied to both the key and the value.
87
88 """
89
90 __slots__ = ['obj']
91
92 def __init__(self, obj):
93 self.obj = obj
94
95 def __lt__(self, other):
Florent Xiclunad6da90f2012-07-21 11:17:38 +020096 try:
Serhiy Storchaka62aa7dc2015-04-06 22:52:44 +030097 return self.obj < other.obj
Florent Xiclunad6da90f2012-07-21 11:17:38 +020098 except TypeError:
Serhiy Storchaka62aa7dc2015-04-06 22:52:44 +030099 return ((str(type(self.obj)), id(self.obj)) < \
100 (str(type(other.obj)), id(other.obj)))
Raymond Hettingera7da1662009-11-19 01:07:05 +0000101
102def _safe_tuple(t):
103 "Helper function for comparing 2-tuples"
104 return _safe_key(t[0]), _safe_key(t[1])
105
Fred Drakea89fda01997-04-16 16:59:30 +0000106class PrettyPrinter:
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300107 def __init__(self, indent=1, width=80, depth=None, stream=None, *,
sblondon3ba3d512021-03-24 09:23:20 +0100108 compact=False, sort_dicts=True, underscore_numbers=False):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000109 """Handle pretty printing operations onto a stream using a set of
110 configured parameters.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000111
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000112 indent
113 Number of spaces to indent for each level of nesting.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000114
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000115 width
116 Attempted maximum number of columns in the output.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000117
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000118 depth
119 The maximum depth to print out nested structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000120
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000121 stream
122 The desired output stream. If omitted (or false), the standard
123 output stream available at construction will be used.
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000124
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300125 compact
126 If true, several items will be combined in one line.
127
Rémi Lapeyre96831c72019-03-22 18:22:20 +0100128 sort_dicts
129 If true, dict keys are sorted.
130
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000131 """
132 indent = int(indent)
133 width = int(width)
Serhiy Storchakaf3fa3082015-03-26 08:43:21 +0200134 if indent < 0:
135 raise ValueError('indent must be >= 0')
136 if depth is not None and depth <= 0:
137 raise ValueError('depth must be > 0')
138 if not width:
139 raise ValueError('width must be != 0')
Fred Drakee6691ef2002-07-08 12:28:06 +0000140 self._depth = depth
141 self._indent_per_level = indent
142 self._width = width
Raymond Hettinger16e3c422002-06-01 16:07:16 +0000143 if stream is not None:
Fred Drakee6691ef2002-07-08 12:28:06 +0000144 self._stream = stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000145 else:
Fred Drake397b6152002-12-31 07:14:18 +0000146 self._stream = _sys.stdout
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300147 self._compact = bool(compact)
Rémi Lapeyre96831c72019-03-22 18:22:20 +0100148 self._sort_dicts = sort_dicts
sblondon3ba3d512021-03-24 09:23:20 +0100149 self._underscore_numbers = underscore_numbers
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000150
Fred Drakea89fda01997-04-16 16:59:30 +0000151 def pprint(self, object):
Walter Dörwalde62e9362005-11-11 18:18:51 +0000152 self._format(object, self._stream, 0, 0, {}, 0)
153 self._stream.write("\n")
Fred Drakea89fda01997-04-16 16:59:30 +0000154
155 def pformat(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000156 sio = _StringIO()
Fred Drakee6691ef2002-07-08 12:28:06 +0000157 self._format(object, sio, 0, 0, {}, 0)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000158 return sio.getvalue()
Fred Drakea89fda01997-04-16 16:59:30 +0000159
Fred Drakee0ffabe1997-07-18 20:42:39 +0000160 def isrecursive(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000161 return self.format(object, {}, 0, 0)[2]
Fred Drakee0ffabe1997-07-18 20:42:39 +0000162
163 def isreadable(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000164 s, readable, recursive = self.format(object, {}, 0, 0)
Fred Drakeaee113d2002-04-02 05:08:35 +0000165 return readable and not recursive
Fred Drakee0ffabe1997-07-18 20:42:39 +0000166
Fred Drakee6691ef2002-07-08 12:28:06 +0000167 def _format(self, object, stream, indent, allowance, context, level):
Antoine Pitrou7d36e2f2013-10-03 21:29:36 +0200168 objid = id(object)
Fred Drake49cc01e2001-11-01 17:50:38 +0000169 if objid in context:
170 stream.write(_recursion(object))
Fred Drakee6691ef2002-07-08 12:28:06 +0000171 self._recursive = True
172 self._readable = False
Fred Drake49cc01e2001-11-01 17:50:38 +0000173 return
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200174 rep = self._repr(object, context, level)
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200175 max_width = self._width - indent - allowance
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200176 if len(rep) > max_width:
177 p = self._dispatch.get(type(object).__repr__, None)
178 if p is not None:
179 context[objid] = 1
180 p(self, object, stream, indent, allowance, context, level + 1)
181 del context[objid]
182 return
Lewis Gaul11159d22021-04-14 00:59:24 +0100183 elif (_dataclasses.is_dataclass(object) and
184 not isinstance(object, type) and
185 object.__dataclass_params__.repr and
186 # Check dataclass has generated repr method.
187 hasattr(object.__repr__, "__wrapped__") and
188 "__create_fn__" in object.__repr__.__wrapped__.__qualname__):
189 context[objid] = 1
190 self._pprint_dataclass(object, stream, indent, allowance, context, level + 1)
191 del context[objid]
192 return
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200193 stream.write(rep)
194
Lewis Gaul11159d22021-04-14 00:59:24 +0100195 def _pprint_dataclass(self, object, stream, indent, allowance, context, level):
196 cls_name = object.__class__.__name__
197 indent += len(cls_name) + 1
198 items = [(f.name, getattr(object, f.name)) for f in _dataclasses.fields(object) if f.repr]
199 stream.write(cls_name + '(')
200 self._format_namespace_items(items, stream, indent, allowance, context, level)
201 stream.write(')')
202
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200203 _dispatch = {}
204
205 def _pprint_dict(self, object, stream, indent, allowance, context, level):
Fred Drake49cc01e2001-11-01 17:50:38 +0000206 write = stream.write
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200207 write('{')
208 if self._indent_per_level > 1:
209 write((self._indent_per_level - 1) * ' ')
210 length = len(object)
211 if length:
Rémi Lapeyre96831c72019-03-22 18:22:20 +0100212 if self._sort_dicts:
213 items = sorted(object.items(), key=_safe_tuple)
214 else:
215 items = object.items()
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200216 self._format_dict_items(items, stream, indent, allowance + 1,
217 context, level)
218 write('}')
Fred Drakea89fda01997-04-16 16:59:30 +0000219
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200220 _dispatch[dict.__repr__] = _pprint_dict
Serhiy Storchakaaa4c36f2015-03-26 08:51:33 +0200221
222 def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level):
223 if not len(object):
224 stream.write(repr(object))
225 return
226 cls = object.__class__
227 stream.write(cls.__name__ + '(')
228 self._format(list(object.items()), stream,
229 indent + len(cls.__name__) + 1, allowance + 1,
230 context, level)
231 stream.write(')')
232
233 _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
Fred Drakea89fda01997-04-16 16:59:30 +0000234
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200235 def _pprint_list(self, object, stream, indent, allowance, context, level):
236 stream.write('[')
237 self._format_items(object, stream, indent, allowance + 1,
238 context, level)
239 stream.write(']')
Fred Drakea89fda01997-04-16 16:59:30 +0000240
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200241 _dispatch[list.__repr__] = _pprint_list
242
243 def _pprint_tuple(self, object, stream, indent, allowance, context, level):
244 stream.write('(')
245 endchar = ',)' if len(object) == 1 else ')'
246 self._format_items(object, stream, indent, allowance + len(endchar),
247 context, level)
248 stream.write(endchar)
249
250 _dispatch[tuple.__repr__] = _pprint_tuple
251
252 def _pprint_set(self, object, stream, indent, allowance, context, level):
253 if not len(object):
254 stream.write(repr(object))
255 return
256 typ = object.__class__
257 if typ is set:
258 stream.write('{')
259 endchar = '}'
260 else:
261 stream.write(typ.__name__ + '({')
262 endchar = '})'
263 indent += len(typ.__name__) + 1
264 object = sorted(object, key=_safe_key)
265 self._format_items(object, stream, indent, allowance + len(endchar),
266 context, level)
267 stream.write(endchar)
268
269 _dispatch[set.__repr__] = _pprint_set
270 _dispatch[frozenset.__repr__] = _pprint_set
271
272 def _pprint_str(self, object, stream, indent, allowance, context, level):
273 write = stream.write
274 if not len(object):
275 write(repr(object))
276 return
277 chunks = []
278 lines = object.splitlines(True)
279 if level == 1:
280 indent += 1
281 allowance += 1
282 max_width1 = max_width = self._width - indent
283 for i, line in enumerate(lines):
284 rep = repr(line)
285 if i == len(lines) - 1:
286 max_width1 -= allowance
287 if len(rep) <= max_width1:
288 chunks.append(rep)
289 else:
290 # A list of alternating (non-space, space) strings
291 parts = re.findall(r'\S*\s*', line)
292 assert parts
293 assert not parts[-1]
294 parts.pop() # drop empty last part
295 max_width2 = max_width
296 current = ''
297 for j, part in enumerate(parts):
298 candidate = current + part
299 if j == len(parts) - 1 and i == len(lines) - 1:
300 max_width2 -= allowance
301 if len(repr(candidate)) > max_width2:
Serhiy Storchakafe3dc372014-12-20 20:57:15 +0200302 if current:
303 chunks.append(repr(current))
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200304 current = part
305 else:
306 current = candidate
307 if current:
308 chunks.append(repr(current))
309 if len(chunks) == 1:
310 write(rep)
311 return
312 if level == 1:
313 write('(')
314 for i, rep in enumerate(chunks):
315 if i > 0:
316 write('\n' + ' '*indent)
317 write(rep)
318 if level == 1:
319 write(')')
320
321 _dispatch[str.__repr__] = _pprint_str
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000322
Serhiy Storchaka022f2032015-03-24 19:22:37 +0200323 def _pprint_bytes(self, object, stream, indent, allowance, context, level):
324 write = stream.write
325 if len(object) <= 4:
326 write(repr(object))
327 return
328 parens = level == 1
329 if parens:
330 indent += 1
331 allowance += 1
332 write('(')
333 delim = ''
334 for rep in _wrap_bytes_repr(object, self._width - indent, allowance):
335 write(delim)
336 write(rep)
337 if not delim:
338 delim = '\n' + ' '*indent
339 if parens:
340 write(')')
341
342 _dispatch[bytes.__repr__] = _pprint_bytes
343
344 def _pprint_bytearray(self, object, stream, indent, allowance, context, level):
345 write = stream.write
346 write('bytearray(')
347 self._pprint_bytes(bytes(object), stream, indent + 10,
348 allowance + 1, context, level + 1)
349 write(')')
350
351 _dispatch[bytearray.__repr__] = _pprint_bytearray
352
Serhiy Storchaka87eb4822015-03-24 19:31:50 +0200353 def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level):
354 stream.write('mappingproxy(')
355 self._format(object.copy(), stream, indent + 13, allowance + 1,
356 context, level)
357 stream.write(')')
358
359 _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy
360
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200361 def _pprint_simplenamespace(self, object, stream, indent, allowance, context, level):
362 if type(object) is _types.SimpleNamespace:
363 # The SimpleNamespace repr is "namespace" instead of the class
364 # name, so we do the same here. For subclasses; use the class name.
365 cls_name = 'namespace'
366 else:
367 cls_name = object.__class__.__name__
368 indent += len(cls_name) + 1
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200369 items = object.__dict__.items()
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200370 stream.write(cls_name + '(')
Lewis Gaul11159d22021-04-14 00:59:24 +0100371 self._format_namespace_items(items, stream, indent, allowance, context, level)
Carl Bordum Hansen06a89162019-06-27 01:13:18 +0200372 stream.write(')')
373
374 _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
375
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200376 def _format_dict_items(self, items, stream, indent, allowance, context,
377 level):
378 write = stream.write
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200379 indent += self._indent_per_level
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200380 delimnl = ',\n' + ' ' * indent
381 last_index = len(items) - 1
382 for i, (key, ent) in enumerate(items):
383 last = i == last_index
384 rep = self._repr(key, context, level)
385 write(rep)
386 write(': ')
387 self._format(ent, stream, indent + len(rep) + 2,
388 allowance if last else 1,
389 context, level)
390 if not last:
391 write(delimnl)
392
Lewis Gaul11159d22021-04-14 00:59:24 +0100393 def _format_namespace_items(self, items, stream, indent, allowance, context, level):
394 write = stream.write
395 delimnl = ',\n' + ' ' * indent
396 last_index = len(items) - 1
397 for i, (key, ent) in enumerate(items):
398 last = i == last_index
399 write(key)
400 write('=')
401 if id(ent) in context:
402 # Special-case representation of recursion to match standard
403 # recursive dataclass repr.
404 write("...")
405 else:
406 self._format(ent, stream, indent + len(key) + 1,
407 allowance if last else 1,
408 context, level)
409 if not last:
410 write(delimnl)
411
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300412 def _format_items(self, items, stream, indent, allowance, context, level):
413 write = stream.write
Serhiy Storchaka8e2aa882015-03-24 18:45:23 +0200414 indent += self._indent_per_level
415 if self._indent_per_level > 1:
416 write((self._indent_per_level - 1) * ' ')
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300417 delimnl = ',\n' + ' ' * indent
418 delim = ''
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200419 width = max_width = self._width - indent + 1
420 it = iter(items)
421 try:
422 next_ent = next(it)
423 except StopIteration:
424 return
425 last = False
426 while not last:
427 ent = next_ent
428 try:
429 next_ent = next(it)
430 except StopIteration:
431 last = True
432 max_width -= allowance
433 width -= allowance
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300434 if self._compact:
435 rep = self._repr(ent, context, level)
Antoine Pitrou7d36e2f2013-10-03 21:29:36 +0200436 w = len(rep) + 2
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300437 if width < w:
438 width = max_width
439 if delim:
440 delim = delimnl
441 if width >= w:
442 width -= w
443 write(delim)
444 delim = ', '
445 write(rep)
446 continue
447 write(delim)
448 delim = delimnl
Serhiy Storchakaa750ce32015-02-14 10:55:19 +0200449 self._format(ent, stream, indent,
450 allowance if last else 1,
451 context, level)
Serhiy Storchaka7c411a42013-10-02 11:56:18 +0300452
Fred Drakee6691ef2002-07-08 12:28:06 +0000453 def _repr(self, object, context, level):
Fred Drakeaee113d2002-04-02 05:08:35 +0000454 repr, readable, recursive = self.format(object, context.copy(),
Fred Drakee6691ef2002-07-08 12:28:06 +0000455 self._depth, level)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000456 if not readable:
Fred Drakee6691ef2002-07-08 12:28:06 +0000457 self._readable = False
Tim Petersa814db52001-05-14 07:05:58 +0000458 if recursive:
Fred Drakee6691ef2002-07-08 12:28:06 +0000459 self._recursive = True
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000460 return repr
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000461
Fred Drakeaee113d2002-04-02 05:08:35 +0000462 def format(self, object, context, maxlevels, level):
463 """Format object for a specific context, returning a string
464 and flags indicating whether the representation is 'readable'
465 and whether the object represents a recursive construct.
466 """
Irit Katrielff420f02020-11-23 13:31:31 +0000467 return self._safe_repr(object, context, maxlevels, level)
Fred Drakeaee113d2002-04-02 05:08:35 +0000468
Serhiy Storchakabedbf962015-05-12 13:35:48 +0300469 def _pprint_default_dict(self, object, stream, indent, allowance, context, level):
470 if not len(object):
471 stream.write(repr(object))
472 return
473 rdf = self._repr(object.default_factory, context, level)
474 cls = object.__class__
475 indent += len(cls.__name__) + 1
476 stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent))
477 self._pprint_dict(object, stream, indent, allowance + 1, context, level)
478 stream.write(')')
479
480 _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict
481
482 def _pprint_counter(self, object, stream, indent, allowance, context, level):
483 if not len(object):
484 stream.write(repr(object))
485 return
486 cls = object.__class__
487 stream.write(cls.__name__ + '({')
488 if self._indent_per_level > 1:
489 stream.write((self._indent_per_level - 1) * ' ')
490 items = object.most_common()
491 self._format_dict_items(items, stream,
492 indent + len(cls.__name__) + 1, allowance + 2,
493 context, level)
494 stream.write('})')
495
496 _dispatch[_collections.Counter.__repr__] = _pprint_counter
497
498 def _pprint_chain_map(self, object, stream, indent, allowance, context, level):
499 if not len(object.maps):
500 stream.write(repr(object))
501 return
502 cls = object.__class__
503 stream.write(cls.__name__ + '(')
504 indent += len(cls.__name__) + 1
505 for i, m in enumerate(object.maps):
506 if i == len(object.maps) - 1:
507 self._format(m, stream, indent, allowance + 1, context, level)
508 stream.write(')')
509 else:
510 self._format(m, stream, indent, 1, context, level)
511 stream.write(',\n' + ' ' * indent)
512
513 _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map
514
515 def _pprint_deque(self, object, stream, indent, allowance, context, level):
516 if not len(object):
517 stream.write(repr(object))
518 return
519 cls = object.__class__
520 stream.write(cls.__name__ + '(')
521 indent += len(cls.__name__) + 1
522 stream.write('[')
523 if object.maxlen is None:
524 self._format_items(object, stream, indent, allowance + 2,
525 context, level)
526 stream.write('])')
527 else:
528 self._format_items(object, stream, indent, 2,
529 context, level)
530 rml = self._repr(object.maxlen, context, level)
531 stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml))
532
533 _dispatch[_collections.deque.__repr__] = _pprint_deque
534
535 def _pprint_user_dict(self, object, stream, indent, allowance, context, level):
536 self._format(object.data, stream, indent, allowance, context, level - 1)
537
538 _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict
539
540 def _pprint_user_list(self, object, stream, indent, allowance, context, level):
541 self._format(object.data, stream, indent, allowance, context, level - 1)
542
543 _dispatch[_collections.UserList.__repr__] = _pprint_user_list
544
545 def _pprint_user_string(self, object, stream, indent, allowance, context, level):
546 self._format(object.data, stream, indent, allowance, context, level - 1)
547
548 _dispatch[_collections.UserString.__repr__] = _pprint_user_string
Fred Drakeaee113d2002-04-02 05:08:35 +0000549
Irit Katrielff420f02020-11-23 13:31:31 +0000550 def _safe_repr(self, object, context, maxlevels, level):
551 # Return triple (repr_string, isreadable, isrecursive).
552 typ = type(object)
553 if typ in _builtin_scalars:
554 return repr(object), True, False
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000555
Irit Katrielff420f02020-11-23 13:31:31 +0000556 r = getattr(typ, "__repr__", None)
sblondon3ba3d512021-03-24 09:23:20 +0100557
558 if issubclass(typ, int) and r is int.__repr__:
559 if self._underscore_numbers:
560 return f"{object:_d}", True, False
561 else:
562 return repr(object), True, False
563
Irit Katrielff420f02020-11-23 13:31:31 +0000564 if issubclass(typ, dict) and r is dict.__repr__:
Fred Drake49cc01e2001-11-01 17:50:38 +0000565 if not object:
Irit Katrielff420f02020-11-23 13:31:31 +0000566 return "{}", True, False
567 objid = id(object)
568 if maxlevels and level >= maxlevels:
569 return "{...}", False, objid in context
570 if objid in context:
571 return _recursion(object), False, True
572 context[objid] = 1
573 readable = True
574 recursive = False
575 components = []
576 append = components.append
577 level += 1
578 if self._sort_dicts:
579 items = sorted(object.items(), key=_safe_tuple)
580 else:
581 items = object.items()
582 for k, v in items:
583 krepr, kreadable, krecur = self.format(
584 k, context, maxlevels, level)
585 vrepr, vreadable, vrecur = self.format(
586 v, context, maxlevels, level)
587 append("%s: %s" % (krepr, vrepr))
588 readable = readable and kreadable and vreadable
589 if krecur or vrecur:
590 recursive = True
591 del context[objid]
592 return "{%s}" % ", ".join(components), readable, recursive
Tim Peters88768482001-11-13 21:51:26 +0000593
Irit Katrielff420f02020-11-23 13:31:31 +0000594 if (issubclass(typ, list) and r is list.__repr__) or \
595 (issubclass(typ, tuple) and r is tuple.__repr__):
596 if issubclass(typ, list):
597 if not object:
598 return "[]", True, False
599 format = "[%s]"
600 elif len(object) == 1:
601 format = "(%s,)"
602 else:
603 if not object:
604 return "()", True, False
605 format = "(%s)"
606 objid = id(object)
607 if maxlevels and level >= maxlevels:
608 return format % "...", False, objid in context
609 if objid in context:
610 return _recursion(object), False, True
611 context[objid] = 1
612 readable = True
613 recursive = False
614 components = []
615 append = components.append
616 level += 1
617 for o in object:
618 orepr, oreadable, orecur = self.format(
619 o, context, maxlevels, level)
620 append(orepr)
621 if not oreadable:
622 readable = False
623 if orecur:
624 recursive = True
625 del context[objid]
626 return format % ", ".join(components), readable, recursive
627
628 rep = repr(object)
629 return rep, (rep and not rep.startswith('<')), False
Tim Peters95b3f782001-05-14 18:39:41 +0000630
sblondon3ba3d512021-03-24 09:23:20 +0100631_builtin_scalars = frozenset({str, bytes, bytearray, float, complex,
Serhiy Storchaka8eb1f072015-05-16 21:38:05 +0300632 bool, type(None)})
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000633
Fred Drake49cc01e2001-11-01 17:50:38 +0000634def _recursion(object):
635 return ("<Recursion on %s with id=%s>"
Antoine Pitrou7d36e2f2013-10-03 21:29:36 +0200636 % (type(object).__name__, id(object)))
Fred Drakea89fda01997-04-16 16:59:30 +0000637
Fred Drake49cc01e2001-11-01 17:50:38 +0000638
639def _perfcheck(object=None):
640 import time
641 if object is None:
642 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
643 p = PrettyPrinter()
Victor Stinner8db5b542018-12-17 11:30:34 +0100644 t1 = time.perf_counter()
Irit Katrielff420f02020-11-23 13:31:31 +0000645 p._safe_repr(object, {}, None, 0, True)
Victor Stinner8db5b542018-12-17 11:30:34 +0100646 t2 = time.perf_counter()
Fred Drake49cc01e2001-11-01 17:50:38 +0000647 p.pformat(object)
Victor Stinner8db5b542018-12-17 11:30:34 +0100648 t3 = time.perf_counter()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000649 print("_safe_repr:", t2 - t1)
650 print("pformat:", t3 - t2)
Fred Drake49cc01e2001-11-01 17:50:38 +0000651
Serhiy Storchaka022f2032015-03-24 19:22:37 +0200652def _wrap_bytes_repr(object, width, allowance):
653 current = b''
654 last = len(object) // 4 * 4
655 for i in range(0, len(object), 4):
656 part = object[i: i+4]
657 candidate = current + part
658 if i == last:
659 width -= allowance
660 if len(repr(candidate)) > width:
661 if current:
662 yield repr(current)
663 current = part
664 else:
665 current = candidate
666 if current:
667 yield repr(current)
668
Fred Drake49cc01e2001-11-01 17:50:38 +0000669if __name__ == "__main__":
670 _perfcheck()