1# This file is part of Hypothesis, which may be found at
2# https://github.com/HypothesisWorks/hypothesis/
3#
4# Copyright the Hypothesis Authors.
5# Individual contributors are listed in AUTHORS.rst and the git log.
6#
7# This Source Code Form is subject to the terms of the Mozilla Public License,
8# v. 2.0. If a copy of the MPL was not distributed with this file, You can
9# obtain one at https://mozilla.org/MPL/2.0/.
10
11"""
12Python advanced pretty printer. This pretty printer is intended to
13replace the old `pprint` python module which does not allow developers
14to provide their own pretty print callbacks.
15This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`.
16Example Usage
17-------------
18To get a string of the output use `pretty`::
19 from pretty import pretty
20 string = pretty(complex_object)
21Extending
22---------
23The pretty library allows developers to add pretty printing rules for their
24own objects. This process is straightforward. All you have to do is to
25add a `_repr_pretty_` method to your object and call the methods on the
26pretty printer passed::
27 class MyObject(object):
28 def _repr_pretty_(self, p, cycle):
29 ...
30Here is an example implementation of a `_repr_pretty_` method for a list
31subclass::
32 class MyList(list):
33 def _repr_pretty_(self, p, cycle):
34 if cycle:
35 p.text('MyList(...)')
36 else:
37 with p.group(8, 'MyList([', '])'):
38 for idx, item in enumerate(self):
39 if idx:
40 p.text(',')
41 p.breakable()
42 p.pretty(item)
43The `cycle` parameter is `True` if pretty detected a cycle. You *have* to
44react to that or the result is an infinite loop. `p.text()` just adds
45non breaking text to the output, `p.breakable()` either adds a whitespace
46or breaks here. If you pass it an argument it's used instead of the
47default space. `p.pretty` prettyprints another object using the pretty print
48method.
49The first parameter to the `group` function specifies the extra indentation
50of the next line. In this example the next item will either be on the same
51line (if the items are short enough) or aligned with the right edge of the
52opening bracket of `MyList`.
53If you just want to indent something you can use the group function
54without open / close parameters. You can also use this code::
55 with p.indent(2):
56 ...
57Inheritance diagram:
58.. inheritance-diagram:: IPython.lib.pretty
59 :parts: 3
60:copyright: 2007 by Armin Ronacher.
61 Portions (c) 2009 by Robert Kern.
62:license: BSD License.
63"""
64
65import datetime
66import re
67import struct
68import sys
69import types
70import warnings
71from collections import defaultdict, deque
72from contextlib import contextmanager
73from enum import Flag
74from io import StringIO
75from math import copysign, isnan
76
77__all__ = [
78 "pretty",
79 "IDKey",
80 "RepresentationPrinter",
81]
82
83
84def _safe_getattr(obj, attr, default=None):
85 """Safe version of getattr.
86
87 Same as getattr, but will return ``default`` on any Exception,
88 rather than raising.
89
90 """
91 try:
92 return getattr(obj, attr, default)
93 except Exception:
94 return default
95
96
97def pretty(obj):
98 """Pretty print the object's representation."""
99 printer = RepresentationPrinter()
100 printer.pretty(obj)
101 return printer.getvalue()
102
103
104class IDKey:
105 def __init__(self, value):
106 self.value = value
107
108 def __hash__(self) -> int:
109 return hash((type(self), id(self.value)))
110
111 def __eq__(self, __o: object) -> bool:
112 return isinstance(__o, type(self)) and id(self.value) == id(__o.value)
113
114
115class RepresentationPrinter:
116 """Special pretty printer that has a `pretty` method that calls the pretty
117 printer for a python object.
118
119 This class stores processing data on `self` so you must *never* use
120 this class in a threaded environment. Always lock it or
121 reinstantiate it.
122
123 """
124
125 def __init__(self, output=None, *, context=None):
126 """Pass the output stream, and optionally the current build context.
127
128 We use the context to represent objects constructed by strategies by showing
129 *how* they were constructed, and add annotations showing which parts of the
130 minimal failing example can vary without changing the test result.
131 """
132 self.broken = False
133 self.output = StringIO() if output is None else output
134 self.max_width = 79
135 self.max_seq_length = 1000
136 self.output_width = 0
137 self.buffer_width = 0
138 self.buffer = deque()
139
140 root_group = Group(0)
141 self.group_stack = [root_group]
142 self.group_queue = GroupQueue(root_group)
143 self.indentation = 0
144
145 self.snans = 0
146
147 self.stack = []
148 self.singleton_pprinters = {}
149 self.type_pprinters = {}
150 self.deferred_pprinters = {}
151 # If IPython has been imported, load up their pretty-printer registry
152 if "IPython.lib.pretty" in sys.modules:
153 ipp = sys.modules["IPython.lib.pretty"]
154 self.singleton_pprinters.update(ipp._singleton_pprinters)
155 self.type_pprinters.update(ipp._type_pprinters)
156 self.deferred_pprinters.update(ipp._deferred_type_pprinters)
157 # If there's overlap between our pprinters and IPython's, we'll use ours.
158 self.singleton_pprinters.update(_singleton_pprinters)
159 self.type_pprinters.update(_type_pprinters)
160 self.deferred_pprinters.update(_deferred_type_pprinters)
161
162 # for which-parts-matter, we track a mapping from the (start_idx, end_idx)
163 # of slices into the minimal failing example; this is per-interesting_origin
164 # but we report each separately so that's someone else's problem here.
165 # Invocations of self.repr_call() can report the slice for each argument,
166 # which will then be used to look up the relevant comment if any.
167 if context is None:
168 self.known_object_printers = defaultdict(list)
169 self.slice_comments = {}
170 else:
171 self.known_object_printers = context.known_object_printers
172 self.slice_comments = context.data.slice_comments
173 assert all(isinstance(k, IDKey) for k in self.known_object_printers)
174
175 def pretty(self, obj):
176 """Pretty print the given object."""
177 obj_id = id(obj)
178 cycle = obj_id in self.stack
179 self.stack.append(obj_id)
180 try:
181 with self.group():
182 obj_class = _safe_getattr(obj, "__class__", None) or type(obj)
183 # First try to find registered singleton printers for the type.
184 try:
185 printer = self.singleton_pprinters[obj_id]
186 except (TypeError, KeyError):
187 pass
188 else:
189 return printer(obj, self, cycle)
190 # Next walk the mro and check for either:
191 # 1) a registered printer
192 # 2) a _repr_pretty_ method
193 for cls in obj_class.__mro__:
194 if cls in self.type_pprinters:
195 # printer registered in self.type_pprinters
196 return self.type_pprinters[cls](obj, self, cycle)
197 else:
198 # Check if the given class is specified in the deferred type
199 # registry; move it to the regular type registry if so.
200 key = (
201 _safe_getattr(cls, "__module__", None),
202 _safe_getattr(cls, "__name__", None),
203 )
204 if key in self.deferred_pprinters:
205 # Move the printer over to the regular registry.
206 printer = self.deferred_pprinters.pop(key)
207 self.type_pprinters[cls] = printer
208 return printer(obj, self, cycle)
209 else:
210 # Finally look for special method names.
211 # Some objects automatically create any requested
212 # attribute. Try to ignore most of them by checking for
213 # callability.
214 if "_repr_pretty_" in cls.__dict__:
215 meth = cls._repr_pretty_
216 if callable(meth):
217 return meth(obj, self, cycle)
218 # Now check for object-specific printers which show how this
219 # object was constructed (a Hypothesis special feature).
220 printers = self.known_object_printers[IDKey(obj)]
221 if len(printers) == 1:
222 return printers[0](obj, self, cycle)
223 elif printers:
224 # We've ended up with multiple registered functions for the same
225 # object, which must have been returned from multiple calls due to
226 # e.g. memoization. If they all return the same string, we'll use
227 # the first; otherwise we'll pretend that *none* were registered.
228 #
229 # It's annoying, but still seems to be the best option for which-
230 # parts-matter too, as unreportable results aren't very useful.
231 strs = set()
232 for f in printers:
233 p = RepresentationPrinter()
234 f(obj, p, cycle)
235 strs.add(p.getvalue())
236 if len(strs) == 1:
237 return printers[0](obj, self, cycle)
238
239 # A user-provided repr. Find newlines and replace them with p.break_()
240 return _repr_pprint(obj, self, cycle)
241 finally:
242 self.stack.pop()
243
244 def _break_outer_groups(self):
245 while self.max_width < self.output_width + self.buffer_width:
246 group = self.group_queue.deq()
247 if not group:
248 return
249 while group.breakables:
250 x = self.buffer.popleft()
251 self.output_width = x.output(self.output, self.output_width)
252 self.buffer_width -= x.width
253 while self.buffer and isinstance(self.buffer[0], Text):
254 x = self.buffer.popleft()
255 self.output_width = x.output(self.output, self.output_width)
256 self.buffer_width -= x.width
257
258 def text(self, obj):
259 """Add literal text to the output."""
260 width = len(obj)
261 if self.buffer:
262 text = self.buffer[-1]
263 if not isinstance(text, Text):
264 text = Text()
265 self.buffer.append(text)
266 text.add(obj, width)
267 self.buffer_width += width
268 self._break_outer_groups()
269 else:
270 self.output.write(obj)
271 self.output_width += width
272
273 def breakable(self, sep=" "):
274 """Add a breakable separator to the output.
275
276 This does not mean that it will automatically break here. If no
277 breaking on this position takes place the `sep` is inserted
278 which default to one space.
279
280 """
281 width = len(sep)
282 group = self.group_stack[-1]
283 if group.want_break:
284 self.flush()
285 self.output.write("\n" + " " * self.indentation)
286 self.output_width = self.indentation
287 self.buffer_width = 0
288 else:
289 self.buffer.append(Breakable(sep, width, self))
290 self.buffer_width += width
291 self._break_outer_groups()
292
293 def break_(self):
294 """Explicitly insert a newline into the output, maintaining correct
295 indentation."""
296 self.flush()
297 self.output.write("\n" + " " * self.indentation)
298 self.output_width = self.indentation
299 self.buffer_width = 0
300
301 @contextmanager
302 def indent(self, indent):
303 """`with`-statement support for indenting/dedenting."""
304 self.indentation += indent
305 try:
306 yield
307 finally:
308 self.indentation -= indent
309
310 @contextmanager
311 def group(self, indent=0, open="", close=""):
312 """Context manager for an indented group.
313
314 with p.group(1, '{', '}'):
315
316 The first parameter specifies the indentation for the next line
317 (usually the width of the opening text), the second and third the
318 opening and closing delimiters.
319 """
320 self.begin_group(indent=indent, open=open)
321 try:
322 yield
323 finally:
324 self.end_group(dedent=indent, close=close)
325
326 def begin_group(self, indent=0, open=""):
327 """Use the `with group(...) context manager instead.
328
329 The begin_group() and end_group() methods are for IPython compatibility only;
330 see https://github.com/HypothesisWorks/hypothesis/issues/3721 for details.
331 """
332 if open:
333 self.text(open)
334 group = Group(self.group_stack[-1].depth + 1)
335 self.group_stack.append(group)
336 self.group_queue.enq(group)
337 self.indentation += indent
338
339 def end_group(self, dedent=0, close=""):
340 """See begin_group()."""
341 self.indentation -= dedent
342 group = self.group_stack.pop()
343 if not group.breakables:
344 self.group_queue.remove(group)
345 if close:
346 self.text(close)
347
348 def _enumerate(self, seq):
349 """Like enumerate, but with an upper limit on the number of items."""
350 for idx, x in enumerate(seq):
351 if self.max_seq_length and idx >= self.max_seq_length:
352 self.text(",")
353 self.breakable()
354 self.text("...")
355 return
356 yield idx, x
357
358 def flush(self):
359 """Flush data that is left in the buffer."""
360 if self.snans:
361 # Reset self.snans *before* calling breakable(), which might flush()
362 snans = self.snans
363 self.snans = 0
364 self.breakable(" ")
365 self.text(f"# Saw {snans} signaling NaN" + "s" * (snans > 1))
366 for data in self.buffer:
367 self.output_width += data.output(self.output, self.output_width)
368 self.buffer.clear()
369 self.buffer_width = 0
370
371 def getvalue(self):
372 assert isinstance(self.output, StringIO)
373 self.flush()
374 return self.output.getvalue()
375
376 def repr_call(
377 self,
378 func_name,
379 args,
380 kwargs,
381 *,
382 force_split=None,
383 arg_slices=None,
384 leading_comment=None,
385 ):
386 """Helper function to represent a function call.
387
388 - func_name, args, and kwargs should all be pretty obvious.
389 - If split_lines, we'll force one-argument-per-line; otherwise we'll place
390 calls that fit on a single line (and split otherwise).
391 - arg_slices is a mapping from pos-idx or keyword to (start_idx, end_idx)
392 of the Conjecture buffer, by which we can look up comments to add.
393 """
394 assert isinstance(func_name, str)
395 if func_name.startswith(("lambda:", "lambda ")):
396 func_name = f"({func_name})"
397 self.text(func_name)
398 all_args = [(None, v) for v in args] + list(kwargs.items())
399 comments = {
400 k: self.slice_comments[v]
401 for k, v in (arg_slices or {}).items()
402 if v in self.slice_comments
403 }
404
405 if leading_comment or any(k in comments for k, _ in all_args):
406 # We have to split one arg per line in order to leave comments on them.
407 force_split = True
408 if force_split is None:
409 # We're OK with printing this call on a single line, but will it fit?
410 # If not, we'd rather fall back to one-argument-per-line instead.
411 p = RepresentationPrinter()
412 p.stack = self.stack.copy()
413 p.known_object_printers = self.known_object_printers
414 p.repr_call("_" * self.output_width, args, kwargs, force_split=False)
415 s = p.getvalue()
416 force_split = "\n" in s
417
418 with self.group(indent=4, open="(", close=""):
419 for i, (k, v) in enumerate(all_args):
420 if force_split:
421 if i == 0 and leading_comment:
422 self.break_()
423 self.text(leading_comment)
424 self.break_()
425 else:
426 self.breakable(" " if i else "")
427 if k:
428 self.text(f"{k}=")
429 self.pretty(v)
430 if force_split or i + 1 < len(all_args):
431 self.text(",")
432 # Optional comments are used to annotate which-parts-matter
433 comment = comments.get(i) or comments.get(k)
434 if comment:
435 self.text(f" # {comment}")
436 if all_args and force_split:
437 self.break_()
438 self.text(")") # after dedent
439
440
441class Printable:
442 def output(self, stream, output_width): # pragma: no cover
443 raise NotImplementedError
444
445
446class Text(Printable):
447 def __init__(self):
448 self.objs = []
449 self.width = 0
450
451 def output(self, stream, output_width):
452 for obj in self.objs:
453 stream.write(obj)
454 return output_width + self.width
455
456 def add(self, obj, width):
457 self.objs.append(obj)
458 self.width += width
459
460
461class Breakable(Printable):
462 def __init__(self, seq, width, pretty):
463 self.obj = seq
464 self.width = width
465 self.pretty = pretty
466 self.indentation = pretty.indentation
467 self.group = pretty.group_stack[-1]
468 self.group.breakables.append(self)
469
470 def output(self, stream, output_width):
471 self.group.breakables.popleft()
472 if self.group.want_break:
473 stream.write("\n" + " " * self.indentation)
474 return self.indentation
475 if not self.group.breakables:
476 self.pretty.group_queue.remove(self.group)
477 stream.write(self.obj)
478 return output_width + self.width
479
480
481class Group(Printable):
482 def __init__(self, depth):
483 self.depth = depth
484 self.breakables = deque()
485 self.want_break = False
486
487
488class GroupQueue:
489 def __init__(self, *groups):
490 self.queue = []
491 for group in groups:
492 self.enq(group)
493
494 def enq(self, group):
495 depth = group.depth
496 while depth > len(self.queue) - 1:
497 self.queue.append([])
498 self.queue[depth].append(group)
499
500 def deq(self):
501 for stack in self.queue:
502 for idx, group in enumerate(reversed(stack)):
503 if group.breakables:
504 del stack[idx]
505 group.want_break = True
506 return group
507 for group in stack:
508 group.want_break = True
509 del stack[:]
510
511 def remove(self, group):
512 try:
513 self.queue[group.depth].remove(group)
514 except ValueError:
515 pass
516
517
518def _seq_pprinter_factory(start, end, basetype):
519 """Factory that returns a pprint function useful for sequences.
520
521 Used by the default pprint for tuples, dicts, and lists.
522 """
523
524 def inner(obj, p, cycle):
525 typ = type(obj)
526 if (
527 basetype is not None
528 and typ is not basetype
529 and typ.__repr__ != basetype.__repr__
530 ):
531 # If the subclass provides its own repr, use it instead.
532 return p.text(typ.__repr__(obj))
533
534 if cycle:
535 return p.text(start + "..." + end)
536 step = len(start)
537 with p.group(step, start, end):
538 for idx, x in p._enumerate(obj):
539 if idx:
540 p.text(",")
541 p.breakable()
542 p.pretty(x)
543 if len(obj) == 1 and type(obj) is tuple:
544 # Special case for 1-item tuples.
545 p.text(",")
546
547 return inner
548
549
550def _set_pprinter_factory(start, end, basetype):
551 """Factory that returns a pprint function useful for sets and
552 frozensets."""
553
554 def inner(obj, p, cycle):
555 typ = type(obj)
556 if (
557 basetype is not None
558 and typ is not basetype
559 and typ.__repr__ != basetype.__repr__
560 ):
561 # If the subclass provides its own repr, use it instead.
562 return p.text(typ.__repr__(obj))
563
564 if cycle:
565 return p.text(start + "..." + end)
566 if not obj:
567 # Special case.
568 p.text(basetype.__name__ + "()")
569 else:
570 step = len(start)
571 with p.group(step, start, end):
572 # Like dictionary keys, try to sort the items if there aren't too many
573 items = obj
574 if not (p.max_seq_length and len(obj) >= p.max_seq_length):
575 try:
576 items = sorted(obj)
577 except Exception:
578 # Sometimes the items don't sort.
579 pass
580 for idx, x in p._enumerate(items):
581 if idx:
582 p.text(",")
583 p.breakable()
584 p.pretty(x)
585
586 return inner
587
588
589def _dict_pprinter_factory(start, end, basetype=None):
590 """Factory that returns a pprint function used by the default pprint of
591 dicts and dict proxies."""
592
593 def inner(obj, p, cycle):
594 typ = type(obj)
595 if (
596 basetype is not None
597 and typ is not basetype
598 and typ.__repr__ != basetype.__repr__
599 ):
600 # If the subclass provides its own repr, use it instead.
601 return p.text(typ.__repr__(obj))
602
603 if cycle:
604 return p.text("{...}")
605 with p.group(1, start, end):
606 # If the dict contains both "" and b"" (empty string and empty bytes), we
607 # ignore the BytesWarning raised by `python -bb` mode. We can't use
608 # `.items()` because it might be a non-`dict` type of mapping.
609 with warnings.catch_warnings():
610 warnings.simplefilter("ignore", BytesWarning)
611 for idx, key in p._enumerate(obj):
612 if idx:
613 p.text(",")
614 p.breakable()
615 p.pretty(key)
616 p.text(": ")
617 p.pretty(obj[key])
618
619 inner.__name__ = f"_dict_pprinter_factory({start!r}, {end!r}, {basetype!r})"
620 return inner
621
622
623def _super_pprint(obj, p, cycle):
624 """The pprint for the super type."""
625 with p.group(8, "<super: ", ">"):
626 p.pretty(obj.__thisclass__)
627 p.text(",")
628 p.breakable()
629 p.pretty(obj.__self__)
630
631
632def _re_pattern_pprint(obj, p, cycle):
633 """The pprint function for regular expression patterns."""
634 p.text("re.compile(")
635 pattern = repr(obj.pattern)
636 if pattern[:1] in "uU": # pragma: no cover
637 pattern = pattern[1:]
638 prefix = "ur"
639 else:
640 prefix = "r"
641 pattern = prefix + pattern.replace("\\\\", "\\")
642 p.text(pattern)
643 if obj.flags:
644 p.text(",")
645 p.breakable()
646 done_one = False
647 for flag in (
648 "TEMPLATE",
649 "IGNORECASE",
650 "LOCALE",
651 "MULTILINE",
652 "DOTALL",
653 "UNICODE",
654 "VERBOSE",
655 "DEBUG",
656 ):
657 if obj.flags & getattr(re, flag, 0):
658 if done_one:
659 p.text("|")
660 p.text("re." + flag)
661 done_one = True
662 p.text(")")
663
664
665def _type_pprint(obj, p, cycle):
666 """The pprint for classes and types."""
667 # Heap allocated types might not have the module attribute,
668 # and others may set it to None.
669
670 # Checks for a __repr__ override in the metaclass
671 # != rather than is not because pypy compatibility
672 if type(obj).__repr__ != type.__repr__:
673 _repr_pprint(obj, p, cycle)
674 return
675
676 mod = _safe_getattr(obj, "__module__", None)
677 try:
678 name = obj.__qualname__
679 if not isinstance(name, str): # pragma: no cover
680 # This can happen if the type implements __qualname__ as a property
681 # or other descriptor in Python 2.
682 raise Exception("Try __name__")
683 except Exception: # pragma: no cover
684 name = obj.__name__
685 if not isinstance(name, str):
686 name = "<unknown type>"
687
688 if mod in (None, "__builtin__", "builtins", "exceptions"):
689 p.text(name)
690 else:
691 p.text(mod + "." + name)
692
693
694def _repr_pprint(obj, p, cycle):
695 """A pprint that just redirects to the normal repr function."""
696 # Find newlines and replace them with p.break_()
697 output = repr(obj)
698 for idx, output_line in enumerate(output.splitlines()):
699 if idx:
700 p.break_()
701 p.text(output_line)
702
703
704def _function_pprint(obj, p, cycle):
705 """Base pprint for all functions and builtin functions."""
706 from hypothesis.internal.reflection import get_pretty_function_description
707
708 p.text(get_pretty_function_description(obj))
709
710
711def _exception_pprint(obj, p, cycle):
712 """Base pprint for all exceptions."""
713 name = getattr(obj.__class__, "__qualname__", obj.__class__.__name__)
714 if obj.__class__.__module__ not in ("exceptions", "builtins"):
715 name = f"{obj.__class__.__module__}.{name}"
716 step = len(name) + 1
717 with p.group(step, name + "(", ")"):
718 for idx, arg in enumerate(getattr(obj, "args", ())):
719 if idx:
720 p.text(",")
721 p.breakable()
722 p.pretty(arg)
723
724
725def _repr_float_counting_nans(obj, p, cycle):
726 if isnan(obj) and hasattr(p, "snans"):
727 if struct.pack("!d", abs(obj)) != struct.pack("!d", float("nan")):
728 p.snans += 1
729 if copysign(1.0, obj) == -1.0:
730 p.text("-nan")
731 return
732 p.text(repr(obj))
733
734
735#: printers for builtin types
736_type_pprinters = {
737 int: _repr_pprint,
738 float: _repr_float_counting_nans,
739 str: _repr_pprint,
740 tuple: _seq_pprinter_factory("(", ")", tuple),
741 list: _seq_pprinter_factory("[", "]", list),
742 dict: _dict_pprinter_factory("{", "}", dict),
743 set: _set_pprinter_factory("{", "}", set),
744 frozenset: _set_pprinter_factory("frozenset({", "})", frozenset),
745 super: _super_pprint,
746 re.Pattern: _re_pattern_pprint,
747 type: _type_pprint,
748 types.FunctionType: _function_pprint,
749 types.BuiltinFunctionType: _function_pprint,
750 types.MethodType: _function_pprint,
751 datetime.datetime: _repr_pprint,
752 datetime.timedelta: _repr_pprint,
753 BaseException: _exception_pprint,
754 slice: _repr_pprint,
755 range: _repr_pprint,
756 bytes: _repr_pprint,
757}
758
759#: printers for types specified by name
760_deferred_type_pprinters = {} # type: ignore
761
762
763def for_type_by_name(type_module, type_name, func):
764 """Add a pretty printer for a type specified by the module and name of a
765 type rather than the type object itself."""
766 key = (type_module, type_name)
767 oldfunc = _deferred_type_pprinters.get(key)
768 _deferred_type_pprinters[key] = func
769 return oldfunc
770
771
772#: printers for the default singletons
773_singleton_pprinters = dict.fromkeys(
774 map(id, [None, True, False, Ellipsis, NotImplemented]), _repr_pprint
775)
776
777
778def _defaultdict_pprint(obj, p, cycle):
779 name = obj.__class__.__name__
780 with p.group(len(name) + 1, name + "(", ")"):
781 if cycle:
782 p.text("...")
783 else:
784 p.pretty(obj.default_factory)
785 p.text(",")
786 p.breakable()
787 p.pretty(dict(obj))
788
789
790def _ordereddict_pprint(obj, p, cycle):
791 name = obj.__class__.__name__
792 with p.group(len(name) + 1, name + "(", ")"):
793 if cycle:
794 p.text("...")
795 elif obj:
796 p.pretty(list(obj.items()))
797
798
799def _deque_pprint(obj, p, cycle):
800 name = obj.__class__.__name__
801 with p.group(len(name) + 1, name + "(", ")"):
802 if cycle:
803 p.text("...")
804 else:
805 p.pretty(list(obj))
806
807
808def _counter_pprint(obj, p, cycle):
809 name = obj.__class__.__name__
810 with p.group(len(name) + 1, name + "(", ")"):
811 if cycle:
812 p.text("...")
813 elif obj:
814 p.pretty(dict(obj))
815
816
817def _repr_dataframe(obj, p, cycle): # pragma: no cover
818 with p.indent(4):
819 p.break_()
820 _repr_pprint(obj, p, cycle)
821 p.break_()
822
823
824def _repr_enum(obj, p, cycle):
825 tname = type(obj).__name__
826 if isinstance(obj, Flag):
827 p.text(
828 " | ".join(f"{tname}.{x.name}" for x in type(obj) if x & obj == x)
829 or f"{tname}({obj.value!r})" # if no matching members
830 )
831 else:
832 p.text(f"{tname}.{obj.name}")
833
834
835class _ReprDots:
836 def __repr__(self) -> str:
837 return "..."
838
839
840def _repr_partial(obj, p, cycle):
841 args, kw = obj.args, obj.keywords
842 if cycle:
843 args, kw = (_ReprDots(),), {}
844 p.repr_call(pretty(type(obj)), (obj.func, *args), kw)
845
846
847for_type_by_name("collections", "defaultdict", _defaultdict_pprint)
848for_type_by_name("collections", "OrderedDict", _ordereddict_pprint)
849for_type_by_name("ordereddict", "OrderedDict", _ordereddict_pprint)
850for_type_by_name("collections", "deque", _deque_pprint)
851for_type_by_name("collections", "Counter", _counter_pprint)
852for_type_by_name("pandas.core.frame", "DataFrame", _repr_dataframe)
853for_type_by_name("enum", "Enum", _repr_enum)
854for_type_by_name("functools", "partial", _repr_partial)