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 ast
66import datetime
67import re
68import struct
69import sys
70import types
71import warnings
72from collections import Counter, OrderedDict, defaultdict, deque
73from collections.abc import Generator, Iterable, Sequence
74from contextlib import contextmanager, suppress
75from enum import Enum, Flag
76from functools import partial
77from io import StringIO, TextIOBase
78from math import copysign, isnan
79from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
80
81if TYPE_CHECKING:
82 from typing import TypeAlias
83
84 from hypothesis.control import BuildContext
85
86T = TypeVar("T")
87PrettyPrintFunction: "TypeAlias" = Callable[[Any, "RepresentationPrinter", bool], None]
88
89__all__ = [
90 "IDKey",
91 "RepresentationPrinter",
92 "pretty",
93]
94
95
96def _safe_getattr(obj: object, attr: str, default: Optional[Any] = None) -> Any:
97 """Safe version of getattr.
98
99 Same as getattr, but will return ``default`` on any Exception,
100 rather than raising.
101
102 """
103 try:
104 return getattr(obj, attr, default)
105 except Exception:
106 return default
107
108
109def pretty(obj: object) -> str:
110 """Pretty print the object's representation."""
111 printer = RepresentationPrinter()
112 printer.pretty(obj)
113 return printer.getvalue()
114
115
116class IDKey:
117 def __init__(self, value: object):
118 self.value = value
119
120 def __hash__(self) -> int:
121 return hash((type(self), id(self.value)))
122
123 def __eq__(self, __o: object) -> bool:
124 return isinstance(__o, type(self)) and id(self.value) == id(__o.value)
125
126
127class RepresentationPrinter:
128 """Special pretty printer that has a `pretty` method that calls the pretty
129 printer for a python object.
130
131 This class stores processing data on `self` so you must *never* use
132 this class in a threaded environment. Always lock it or
133 reinstantiate it.
134
135 """
136
137 def __init__(
138 self,
139 output: Optional[TextIOBase] = None,
140 *,
141 context: Optional["BuildContext"] = None,
142 ) -> None:
143 """Optionally pass the output stream and the current build context.
144
145 We use the context to represent objects constructed by strategies by showing
146 *how* they were constructed, and add annotations showing which parts of the
147 minimal failing example can vary without changing the test result.
148 """
149 self.broken: bool = False
150 self.output: TextIOBase = StringIO() if output is None else output
151 self.max_width: int = 79
152 self.max_seq_length: int = 1000
153 self.output_width: int = 0
154 self.buffer_width: int = 0
155 self.buffer: deque[Union[Breakable, Text]] = deque()
156
157 root_group = Group(0)
158 self.group_stack = [root_group]
159 self.group_queue = GroupQueue(root_group)
160 self.indentation: int = 0
161
162 self.stack: list[int] = []
163 self.singleton_pprinters: dict[int, PrettyPrintFunction] = {}
164 self.type_pprinters: dict[type, PrettyPrintFunction] = {}
165 self.deferred_pprinters: dict[tuple[str, str], PrettyPrintFunction] = {}
166 # If IPython has been imported, load up their pretty-printer registry
167 if "IPython.lib.pretty" in sys.modules:
168 ipp = sys.modules["IPython.lib.pretty"]
169 self.singleton_pprinters.update(ipp._singleton_pprinters)
170 self.type_pprinters.update(ipp._type_pprinters)
171 self.deferred_pprinters.update(ipp._deferred_type_pprinters)
172 # If there's overlap between our pprinters and IPython's, we'll use ours.
173 self.singleton_pprinters.update(_singleton_pprinters)
174 self.type_pprinters.update(_type_pprinters)
175 self.deferred_pprinters.update(_deferred_type_pprinters)
176
177 # for which-parts-matter, we track a mapping from the (start_idx, end_idx)
178 # of slices into the minimal failing example; this is per-interesting_origin
179 # but we report each separately so that's someone else's problem here.
180 # Invocations of self.repr_call() can report the slice for each argument,
181 # which will then be used to look up the relevant comment if any.
182 self.known_object_printers: dict[IDKey, list[PrettyPrintFunction]]
183 self.slice_comments: dict[tuple[int, int], str]
184 if context is None:
185 self.known_object_printers = defaultdict(list)
186 self.slice_comments = {}
187 else:
188 self.known_object_printers = context.known_object_printers
189 self.slice_comments = context.data.slice_comments
190 assert all(isinstance(k, IDKey) for k in self.known_object_printers)
191
192 def pretty(self, obj: object) -> None:
193 """Pretty print the given object."""
194 obj_id = id(obj)
195 cycle = obj_id in self.stack
196 self.stack.append(obj_id)
197 try:
198 with self.group():
199 obj_class = _safe_getattr(obj, "__class__", None) or type(obj)
200 # First try to find registered singleton printers for the type.
201 try:
202 printer = self.singleton_pprinters[obj_id]
203 except (TypeError, KeyError):
204 pass
205 else:
206 return printer(obj, self, cycle)
207
208 # Look for the _repr_pretty_ method which allows users
209 # to define custom pretty printing.
210 # Some objects automatically create any requested
211 # attribute. Try to ignore most of them by checking for
212 # callability.
213 pretty_method = _safe_getattr(obj, "_repr_pretty_", None)
214 if callable(pretty_method):
215 return pretty_method(self, cycle)
216
217 # Next walk the mro and check for either:
218 # 1) a registered printer
219 # 2) a _repr_pretty_ method
220 for cls in obj_class.__mro__:
221 if cls in self.type_pprinters:
222 # printer registered in self.type_pprinters
223 return self.type_pprinters[cls](obj, self, cycle)
224 else:
225 # Check if the given class is specified in the deferred type
226 # registry; move it to the regular type registry if so.
227 key = (
228 _safe_getattr(cls, "__module__", None),
229 _safe_getattr(cls, "__name__", None),
230 )
231 if key in self.deferred_pprinters:
232 # Move the printer over to the regular registry.
233 printer = self.deferred_pprinters.pop(key)
234 self.type_pprinters[cls] = printer
235 return printer(obj, self, cycle)
236 else:
237 if hasattr(cls, "__attrs_attrs__"):
238 return pprint_fields(
239 obj,
240 self,
241 cycle,
242 [at.name for at in cls.__attrs_attrs__ if at.init],
243 )
244 if hasattr(cls, "__dataclass_fields__"):
245 return pprint_fields(
246 obj,
247 self,
248 cycle,
249 [
250 k
251 for k, v in cls.__dataclass_fields__.items()
252 if v.init
253 ],
254 )
255 # Now check for object-specific printers which show how this
256 # object was constructed (a Hypothesis special feature).
257 printers = self.known_object_printers[IDKey(obj)]
258 if len(printers) == 1:
259 return printers[0](obj, self, cycle)
260 elif printers:
261 # We've ended up with multiple registered functions for the same
262 # object, which must have been returned from multiple calls due to
263 # e.g. memoization. If they all return the same string, we'll use
264 # the first; otherwise we'll pretend that *none* were registered.
265 #
266 # It's annoying, but still seems to be the best option for which-
267 # parts-matter too, as unreportable results aren't very useful.
268 strs = set()
269 for f in printers:
270 p = RepresentationPrinter()
271 f(obj, p, cycle)
272 strs.add(p.getvalue())
273 if len(strs) == 1:
274 return printers[0](obj, self, cycle)
275
276 # A user-provided repr. Find newlines and replace them with p.break_()
277 return _repr_pprint(obj, self, cycle)
278 finally:
279 self.stack.pop()
280
281 def _break_outer_groups(self) -> None:
282 while self.max_width < self.output_width + self.buffer_width:
283 group = self.group_queue.deq()
284 if not group:
285 return
286 while group.breakables:
287 x = self.buffer.popleft()
288 self.output_width = x.output(self.output, self.output_width)
289 self.buffer_width -= x.width
290 while self.buffer and isinstance(self.buffer[0], Text):
291 x = self.buffer.popleft()
292 self.output_width = x.output(self.output, self.output_width)
293 self.buffer_width -= x.width
294
295 def text(self, obj: str) -> None:
296 """Add literal text to the output."""
297 width = len(obj)
298 if self.buffer:
299 text = self.buffer[-1]
300 if not isinstance(text, Text):
301 text = Text()
302 self.buffer.append(text)
303 text.add(obj, width)
304 self.buffer_width += width
305 self._break_outer_groups()
306 else:
307 self.output.write(obj)
308 self.output_width += width
309
310 def breakable(self, sep: str = " ") -> None:
311 """Add a breakable separator to the output.
312
313 This does not mean that it will automatically break here. If no
314 breaking on this position takes place the `sep` is inserted
315 which default to one space.
316
317 """
318 width = len(sep)
319 group = self.group_stack[-1]
320 if group.want_break:
321 self.flush()
322 self.output.write("\n" + " " * self.indentation)
323 self.output_width = self.indentation
324 self.buffer_width = 0
325 else:
326 self.buffer.append(Breakable(sep, width, self))
327 self.buffer_width += width
328 self._break_outer_groups()
329
330 def break_(self) -> None:
331 """Explicitly insert a newline into the output, maintaining correct
332 indentation."""
333 self.flush()
334 self.output.write("\n" + " " * self.indentation)
335 self.output_width = self.indentation
336 self.buffer_width = 0
337
338 @contextmanager
339 def indent(self, indent: int) -> Generator[None, None, None]:
340 """`with`-statement support for indenting/dedenting."""
341 self.indentation += indent
342 try:
343 yield
344 finally:
345 self.indentation -= indent
346
347 @contextmanager
348 def group(
349 self, indent: int = 0, open: str = "", close: str = ""
350 ) -> Generator[None, None, None]:
351 """Context manager for an indented group.
352
353 with p.group(1, '{', '}'):
354
355 The first parameter specifies the indentation for the next line
356 (usually the width of the opening text), the second and third the
357 opening and closing delimiters.
358 """
359 self.begin_group(indent=indent, open=open)
360 try:
361 yield
362 finally:
363 self.end_group(dedent=indent, close=close)
364
365 def begin_group(self, indent: int = 0, open: str = "") -> None:
366 """Use the `with group(...) context manager instead.
367
368 The begin_group() and end_group() methods are for IPython compatibility only;
369 see https://github.com/HypothesisWorks/hypothesis/issues/3721 for details.
370 """
371 if open:
372 self.text(open)
373 group = Group(self.group_stack[-1].depth + 1)
374 self.group_stack.append(group)
375 self.group_queue.enq(group)
376 self.indentation += indent
377
378 def end_group(self, dedent: int = 0, close: str = "") -> None:
379 """See begin_group()."""
380 self.indentation -= dedent
381 group = self.group_stack.pop()
382 if not group.breakables:
383 self.group_queue.remove(group)
384 if close:
385 self.text(close)
386
387 def _enumerate(self, seq: Iterable[T]) -> Generator[tuple[int, T], None, None]:
388 """Like enumerate, but with an upper limit on the number of items."""
389 for idx, x in enumerate(seq):
390 if self.max_seq_length and idx >= self.max_seq_length:
391 self.text(",")
392 self.breakable()
393 self.text("...")
394 return
395 yield idx, x
396
397 def flush(self) -> None:
398 """Flush data that is left in the buffer."""
399 for data in self.buffer:
400 self.output_width += data.output(self.output, self.output_width)
401 self.buffer.clear()
402 self.buffer_width = 0
403
404 def getvalue(self) -> str:
405 assert isinstance(self.output, StringIO)
406 self.flush()
407 return self.output.getvalue()
408
409 def maybe_repr_known_object_as_call(
410 self,
411 obj: object,
412 cycle: bool,
413 name: str,
414 args: Sequence[object],
415 kwargs: dict[str, object],
416 ) -> None:
417 # pprint this object as a call, _unless_ the call would be invalid syntax
418 # and the repr would be valid and there are not comments on arguments.
419 if cycle:
420 return self.text("<...>")
421 # Since we don't yet track comments for sub-argument parts, we omit the
422 # "if no comments" condition here for now. Add it when we revive
423 # https://github.com/HypothesisWorks/hypothesis/pull/3624/
424 with suppress(Exception):
425 # Check whether the repr is valid syntax:
426 ast.parse(repr(obj))
427 # Given that the repr is valid syntax, check the call:
428 p = RepresentationPrinter()
429 p.stack = self.stack.copy()
430 p.known_object_printers = self.known_object_printers
431 p.repr_call(name, args, kwargs)
432 # If the call is not valid syntax, use the repr
433 try:
434 ast.parse(p.getvalue())
435 except Exception:
436 return _repr_pprint(obj, self, cycle)
437 return self.repr_call(name, args, kwargs)
438
439 def repr_call(
440 self,
441 func_name: str,
442 args: Sequence[object],
443 kwargs: dict[str, object],
444 *,
445 force_split: Optional[bool] = None,
446 arg_slices: Optional[dict[str, tuple[int, int]]] = None,
447 leading_comment: Optional[str] = None,
448 avoid_realization: bool = False,
449 ) -> None:
450 """Helper function to represent a function call.
451
452 - func_name, args, and kwargs should all be pretty obvious.
453 - If split_lines, we'll force one-argument-per-line; otherwise we'll place
454 calls that fit on a single line (and split otherwise).
455 - arg_slices is a mapping from pos-idx or keyword to (start_idx, end_idx)
456 of the Conjecture buffer, by which we can look up comments to add.
457 """
458 assert isinstance(func_name, str)
459 if func_name.startswith(("lambda:", "lambda ")):
460 func_name = f"({func_name})"
461 self.text(func_name)
462 all_args = [(None, v) for v in args] + list(kwargs.items())
463 # int indicates the position of a positional argument, rather than a keyword
464 # argument. Currently no callers use this; see #3624.
465 comments: dict[Union[int, str], object] = {
466 k: self.slice_comments[v]
467 for k, v in (arg_slices or {}).items()
468 if v in self.slice_comments
469 }
470
471 if leading_comment or any(k in comments for k, _ in all_args):
472 # We have to split one arg per line in order to leave comments on them.
473 force_split = True
474 if force_split is None:
475 # We're OK with printing this call on a single line, but will it fit?
476 # If not, we'd rather fall back to one-argument-per-line instead.
477 p = RepresentationPrinter()
478 p.stack = self.stack.copy()
479 p.known_object_printers = self.known_object_printers
480 p.repr_call("_" * self.output_width, args, kwargs, force_split=False)
481 s = p.getvalue()
482 force_split = "\n" in s
483
484 with self.group(indent=4, open="(", close=""):
485 for i, (k, v) in enumerate(all_args):
486 if force_split:
487 if i == 0 and leading_comment:
488 self.break_()
489 self.text(leading_comment)
490 self.break_()
491 else:
492 assert leading_comment is None # only passed by top-level report
493 self.breakable(" " if i else "")
494 if k:
495 self.text(f"{k}=")
496 if avoid_realization:
497 self.text("<symbolic>")
498 else:
499 self.pretty(v)
500 if force_split or i + 1 < len(all_args):
501 self.text(",")
502 comment = None
503 if k is not None:
504 comment = comments.get(i) or comments.get(k)
505 if comment:
506 self.text(f" # {comment}")
507 if all_args and force_split:
508 self.break_()
509 self.text(")") # after dedent
510
511
512class Printable:
513 def output(self, stream: TextIOBase, output_width: int) -> int: # pragma: no cover
514 raise NotImplementedError
515
516
517class Text(Printable):
518 def __init__(self) -> None:
519 self.objs: list[str] = []
520 self.width: int = 0
521
522 def output(self, stream: TextIOBase, output_width: int) -> int:
523 for obj in self.objs:
524 stream.write(obj)
525 return output_width + self.width
526
527 def add(self, obj: str, width: int) -> None:
528 self.objs.append(obj)
529 self.width += width
530
531
532class Breakable(Printable):
533 def __init__(self, seq: str, width: int, pretty: RepresentationPrinter) -> None:
534 self.obj = seq
535 self.width = width
536 self.pretty = pretty
537 self.indentation = pretty.indentation
538 self.group = pretty.group_stack[-1]
539 self.group.breakables.append(self)
540
541 def output(self, stream: TextIOBase, output_width: int) -> int:
542 self.group.breakables.popleft()
543 if self.group.want_break:
544 stream.write("\n" + " " * self.indentation)
545 return self.indentation
546 if not self.group.breakables:
547 self.pretty.group_queue.remove(self.group)
548 stream.write(self.obj)
549 return output_width + self.width
550
551
552class Group(Printable):
553 def __init__(self, depth: int) -> None:
554 self.depth = depth
555 self.breakables: deque[Breakable] = deque()
556 self.want_break: bool = False
557
558
559class GroupQueue:
560 def __init__(self, *groups: Group) -> None:
561 self.queue: list[list[Group]] = []
562 for group in groups:
563 self.enq(group)
564
565 def enq(self, group: Group) -> None:
566 depth = group.depth
567 while depth > len(self.queue) - 1:
568 self.queue.append([])
569 self.queue[depth].append(group)
570
571 def deq(self) -> Optional[Group]:
572 for stack in self.queue:
573 for idx, group in enumerate(reversed(stack)):
574 if group.breakables:
575 del stack[idx]
576 group.want_break = True
577 return group
578 for group in stack:
579 group.want_break = True
580 del stack[:]
581 return None
582
583 def remove(self, group: Group) -> None:
584 try:
585 self.queue[group.depth].remove(group)
586 except ValueError:
587 pass
588
589
590def _seq_pprinter_factory(start: str, end: str, basetype: type) -> PrettyPrintFunction:
591 """Factory that returns a pprint function useful for sequences.
592
593 Used by the default pprint for tuples, dicts, and lists.
594 """
595
596 def inner(
597 obj: Union[tuple[object], list[object]], p: RepresentationPrinter, cycle: bool
598 ) -> None:
599 typ = type(obj)
600 if (
601 basetype is not None
602 and typ is not basetype
603 and typ.__repr__ != basetype.__repr__ # type: ignore[comparison-overlap]
604 ):
605 # If the subclass provides its own repr, use it instead.
606 return p.text(typ.__repr__(obj))
607
608 if cycle:
609 return p.text(start + "..." + end)
610 step = len(start)
611 with p.group(step, start, end):
612 for idx, x in p._enumerate(obj):
613 if idx:
614 p.text(",")
615 p.breakable()
616 p.pretty(x)
617 if len(obj) == 1 and type(obj) is tuple:
618 # Special case for 1-item tuples.
619 p.text(",")
620
621 return inner
622
623
624def get_class_name(cls: type[object]) -> str:
625 class_name = _safe_getattr(cls, "__qualname__", cls.__name__)
626 assert isinstance(class_name, str)
627 return class_name
628
629
630def _set_pprinter_factory(
631 start: str, end: str, basetype: type[object]
632) -> PrettyPrintFunction:
633 """Factory that returns a pprint function useful for sets and
634 frozensets."""
635
636 def inner(
637 obj: Union[set[Any], frozenset[Any]],
638 p: RepresentationPrinter,
639 cycle: bool,
640 ) -> None:
641 typ = type(obj)
642 if (
643 basetype is not None
644 and typ is not basetype
645 and typ.__repr__ != basetype.__repr__
646 ):
647 # If the subclass provides its own repr, use it instead.
648 return p.text(typ.__repr__(obj))
649
650 if cycle:
651 return p.text(start + "..." + end)
652 if not obj:
653 # Special case.
654 p.text(get_class_name(basetype) + "()")
655 else:
656 step = len(start)
657 with p.group(step, start, end):
658 # Like dictionary keys, try to sort the items if there aren't too many
659 items: Iterable[object] = obj
660 if not (p.max_seq_length and len(obj) >= p.max_seq_length):
661 try:
662 items = sorted(obj)
663 except Exception:
664 # Sometimes the items don't sort.
665 pass
666 for idx, x in p._enumerate(items):
667 if idx:
668 p.text(",")
669 p.breakable()
670 p.pretty(x)
671
672 return inner
673
674
675def _dict_pprinter_factory(
676 start: str, end: str, basetype: Optional[type[object]] = None
677) -> PrettyPrintFunction:
678 """Factory that returns a pprint function used by the default pprint of
679 dicts and dict proxies."""
680
681 def inner(obj: dict[object, object], p: RepresentationPrinter, cycle: bool) -> None:
682 typ = type(obj)
683 if (
684 basetype is not None
685 and typ is not basetype
686 and typ.__repr__ != basetype.__repr__
687 ):
688 # If the subclass provides its own repr, use it instead.
689 return p.text(typ.__repr__(obj))
690
691 if cycle:
692 return p.text("{...}")
693 # NOTE: For compatibility with Python 3.9's LL(1)
694 # parser, this is written as a nested with-statement,
695 # instead of a compound one.
696 with p.group(1, start, end):
697 # If the dict contains both "" and b"" (empty string and empty bytes), we
698 # ignore the BytesWarning raised by `python -bb` mode. We can't use
699 # `.items()` because it might be a non-`dict` type of mapping.
700 with warnings.catch_warnings():
701 warnings.simplefilter("ignore", BytesWarning)
702 for idx, key in p._enumerate(obj):
703 if idx:
704 p.text(",")
705 p.breakable()
706 p.pretty(key)
707 p.text(": ")
708 p.pretty(obj[key])
709
710 inner.__name__ = f"_dict_pprinter_factory({start!r}, {end!r}, {basetype!r})"
711 return inner
712
713
714def _super_pprint(obj: Any, p: RepresentationPrinter, cycle: bool) -> None:
715 """The pprint for the super type."""
716 with p.group(8, "<super: ", ">"):
717 p.pretty(obj.__thisclass__)
718 p.text(",")
719 p.breakable()
720 p.pretty(obj.__self__)
721
722
723def _re_pattern_pprint(obj: re.Pattern, p: RepresentationPrinter, cycle: bool) -> None:
724 """The pprint function for regular expression patterns."""
725 p.text("re.compile(")
726 pattern = repr(obj.pattern)
727 if pattern[:1] in "uU": # pragma: no cover
728 pattern = pattern[1:]
729 prefix = "ur"
730 else:
731 prefix = "r"
732 pattern = prefix + pattern.replace("\\\\", "\\")
733 p.text(pattern)
734 if obj.flags:
735 p.text(",")
736 p.breakable()
737 done_one = False
738 for flag in (
739 "TEMPLATE",
740 "IGNORECASE",
741 "LOCALE",
742 "MULTILINE",
743 "DOTALL",
744 "UNICODE",
745 "VERBOSE",
746 "DEBUG",
747 ):
748 if obj.flags & getattr(re, flag, 0):
749 if done_one:
750 p.text("|")
751 p.text("re." + flag)
752 done_one = True
753 p.text(")")
754
755
756def _type_pprint(obj: type[object], p: RepresentationPrinter, cycle: bool) -> None:
757 """The pprint for classes and types."""
758 # Heap allocated types might not have the module attribute,
759 # and others may set it to None.
760
761 # Checks for a __repr__ override in the metaclass
762 # != rather than is not because pypy compatibility
763 if type(obj).__repr__ != type.__repr__: # type: ignore[comparison-overlap]
764 _repr_pprint(obj, p, cycle)
765 return
766
767 mod = _safe_getattr(obj, "__module__", None)
768 try:
769 name = obj.__qualname__
770 except Exception: # pragma: no cover
771 name = obj.__name__
772 if not isinstance(name, str):
773 name = "<unknown type>"
774
775 if mod in (None, "__builtin__", "builtins", "exceptions"):
776 p.text(name)
777 else:
778 p.text(mod + "." + name)
779
780
781def _repr_pprint(obj: object, p: RepresentationPrinter, cycle: bool) -> None:
782 """A pprint that just redirects to the normal repr function."""
783 # Find newlines and replace them with p.break_()
784 output = repr(obj)
785 for idx, output_line in enumerate(output.splitlines()):
786 if idx:
787 p.break_()
788 p.text(output_line)
789
790
791def pprint_fields(
792 obj: object, p: RepresentationPrinter, cycle: bool, fields: Iterable[str]
793) -> None:
794 name = get_class_name(obj.__class__)
795 if cycle:
796 return p.text(f"{name}(...)")
797 with p.group(1, name + "(", ")"):
798 for idx, field in enumerate(fields):
799 if idx:
800 p.text(",")
801 p.breakable()
802 p.text(field)
803 p.text("=")
804 p.pretty(getattr(obj, field))
805
806
807def _function_pprint(
808 obj: Union[types.FunctionType, types.BuiltinFunctionType, types.MethodType],
809 p: RepresentationPrinter,
810 cycle: bool,
811) -> None:
812 """Base pprint for all functions and builtin functions."""
813 from hypothesis.internal.reflection import get_pretty_function_description
814
815 p.text(get_pretty_function_description(obj))
816
817
818def _exception_pprint(
819 obj: BaseException, p: RepresentationPrinter, cycle: bool
820) -> None:
821 """Base pprint for all exceptions."""
822 name = getattr(obj.__class__, "__qualname__", obj.__class__.__name__)
823 if obj.__class__.__module__ not in ("exceptions", "builtins"):
824 name = f"{obj.__class__.__module__}.{name}"
825 step = len(name) + 1
826 with p.group(step, name + "(", ")"):
827 for idx, arg in enumerate(getattr(obj, "args", ())):
828 if idx:
829 p.text(",")
830 p.breakable()
831 p.pretty(arg)
832
833
834def _repr_integer(obj: int, p: RepresentationPrinter, cycle: bool) -> None:
835 if abs(obj) < 1_000_000_000:
836 p.text(repr(obj))
837 elif abs(obj) < 10**640:
838 # add underscores for integers over ten decimal digits
839 p.text(f"{obj:#_d}")
840 else:
841 # for very very large integers, use hex because power-of-two bases are cheaper
842 # https://docs.python.org/3/library/stdtypes.html#integer-string-conversion-length-limitation
843 p.text(f"{obj:#_x}")
844
845
846def _repr_float_counting_nans(
847 obj: float, p: RepresentationPrinter, cycle: bool
848) -> None:
849 if isnan(obj):
850 if struct.pack("!d", abs(obj)) != struct.pack("!d", float("nan")):
851 show = hex(*struct.unpack("Q", struct.pack("d", obj)))
852 return p.text(f"struct.unpack('d', struct.pack('Q', {show}))[0]")
853 elif copysign(1.0, obj) == -1.0:
854 return p.text("-nan")
855 p.text(repr(obj))
856
857
858#: printers for builtin types
859_type_pprinters: dict[type, PrettyPrintFunction] = {
860 int: _repr_integer,
861 float: _repr_float_counting_nans,
862 str: _repr_pprint,
863 tuple: _seq_pprinter_factory("(", ")", tuple),
864 list: _seq_pprinter_factory("[", "]", list),
865 dict: _dict_pprinter_factory("{", "}", dict),
866 set: _set_pprinter_factory("{", "}", set),
867 frozenset: _set_pprinter_factory("frozenset({", "})", frozenset),
868 super: _super_pprint,
869 re.Pattern: _re_pattern_pprint,
870 type: _type_pprint,
871 types.FunctionType: _function_pprint,
872 types.BuiltinFunctionType: _function_pprint,
873 types.MethodType: _function_pprint,
874 datetime.datetime: _repr_pprint,
875 datetime.timedelta: _repr_pprint,
876 BaseException: _exception_pprint,
877 slice: _repr_pprint,
878 range: _repr_pprint,
879 bytes: _repr_pprint,
880}
881
882#: printers for types specified by name
883_deferred_type_pprinters: dict[tuple[str, str], PrettyPrintFunction] = {}
884
885
886def for_type_by_name(
887 type_module: str, type_name: str, func: PrettyPrintFunction
888) -> Optional[PrettyPrintFunction]:
889 """Add a pretty printer for a type specified by the module and name of a
890 type rather than the type object itself."""
891 key = (type_module, type_name)
892 oldfunc = _deferred_type_pprinters.get(key)
893 _deferred_type_pprinters[key] = func
894 return oldfunc
895
896
897#: printers for the default singletons
898_singleton_pprinters: dict[int, PrettyPrintFunction] = dict.fromkeys(
899 map(id, [None, True, False, Ellipsis, NotImplemented]), _repr_pprint
900)
901
902
903def _defaultdict_pprint(
904 obj: defaultdict[object, object], p: RepresentationPrinter, cycle: bool
905) -> None:
906 name = obj.__class__.__name__
907 with p.group(len(name) + 1, name + "(", ")"):
908 if cycle:
909 p.text("...")
910 else:
911 p.pretty(obj.default_factory)
912 p.text(",")
913 p.breakable()
914 p.pretty(dict(obj))
915
916
917def _ordereddict_pprint(
918 obj: OrderedDict[object, object], p: RepresentationPrinter, cycle: bool
919) -> None:
920 name = obj.__class__.__name__
921 with p.group(len(name) + 1, name + "(", ")"):
922 if cycle:
923 p.text("...")
924 elif obj:
925 p.pretty(list(obj.items()))
926
927
928def _deque_pprint(obj: deque[object], p: RepresentationPrinter, cycle: bool) -> None:
929 name = obj.__class__.__name__
930 with p.group(len(name) + 1, name + "(", ")"):
931 if cycle:
932 p.text("...")
933 else:
934 p.pretty(list(obj))
935
936
937def _counter_pprint(
938 obj: Counter[object], p: RepresentationPrinter, cycle: bool
939) -> None:
940 name = obj.__class__.__name__
941 with p.group(len(name) + 1, name + "(", ")"):
942 if cycle:
943 p.text("...")
944 elif obj:
945 p.pretty(dict(obj))
946
947
948def _repr_dataframe(
949 obj: object, p: RepresentationPrinter, cycle: bool
950) -> None: # pragma: no cover
951 with p.indent(4):
952 p.break_()
953 _repr_pprint(obj, p, cycle)
954 p.break_()
955
956
957def _repr_enum(obj: Enum, p: RepresentationPrinter, cycle: bool) -> None:
958 tname = get_class_name(type(obj))
959 if isinstance(obj, Flag):
960 p.text(
961 " | ".join(f"{tname}.{x.name}" for x in type(obj) if x & obj == x)
962 or f"{tname}({obj.value!r})" # if no matching members
963 )
964 else:
965 p.text(f"{tname}.{obj.name}")
966
967
968class _ReprDots:
969 def __repr__(self) -> str:
970 return "..."
971
972
973def _repr_partial(obj: partial[Any], p: RepresentationPrinter, cycle: bool) -> None:
974 args, kw = obj.args, obj.keywords
975 if cycle:
976 args, kw = (_ReprDots(),), {}
977 p.repr_call(pretty(type(obj)), (obj.func, *args), kw)
978
979
980for_type_by_name("collections", "defaultdict", _defaultdict_pprint)
981for_type_by_name("collections", "OrderedDict", _ordereddict_pprint)
982for_type_by_name("ordereddict", "OrderedDict", _ordereddict_pprint)
983for_type_by_name("collections", "deque", _deque_pprint)
984for_type_by_name("collections", "Counter", _counter_pprint)
985for_type_by_name("pandas.core.frame", "DataFrame", _repr_dataframe)
986for_type_by_name("enum", "Enum", _repr_enum)
987for_type_by_name("functools", "partial", _repr_partial)