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