Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/_io/saferepr.py: 24%
82 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
1import pprint
2import reprlib
3from typing import Any
4from typing import Dict
5from typing import IO
6from typing import Optional
9def _try_repr_or_str(obj: object) -> str:
10 try:
11 return repr(obj)
12 except (KeyboardInterrupt, SystemExit):
13 raise
14 except BaseException:
15 return f'{type(obj).__name__}("{obj}")'
18def _format_repr_exception(exc: BaseException, obj: object) -> str:
19 try:
20 exc_info = _try_repr_or_str(exc)
21 except (KeyboardInterrupt, SystemExit):
22 raise
23 except BaseException as exc:
24 exc_info = f"unpresentable exception ({_try_repr_or_str(exc)})"
25 return "<[{} raised in repr()] {} object at 0x{:x}>".format(
26 exc_info, type(obj).__name__, id(obj)
27 )
30def _ellipsize(s: str, maxsize: int) -> str:
31 if len(s) > maxsize:
32 i = max(0, (maxsize - 3) // 2)
33 j = max(0, maxsize - 3 - i)
34 return s[:i] + "..." + s[len(s) - j :]
35 return s
38class SafeRepr(reprlib.Repr):
39 """
40 repr.Repr that limits the resulting size of repr() and includes
41 information on exceptions raised during the call.
42 """
44 def __init__(self, maxsize: Optional[int], use_ascii: bool = False) -> None:
45 """
46 :param maxsize:
47 If not None, will truncate the resulting repr to that specific size, using ellipsis
48 somewhere in the middle to hide the extra text.
49 If None, will not impose any size limits on the returning repr.
50 """
51 super().__init__()
52 # ``maxstring`` is used by the superclass, and needs to be an int; using a
53 # very large number in case maxsize is None, meaning we want to disable
54 # truncation.
55 self.maxstring = maxsize if maxsize is not None else 1_000_000_000
56 self.maxsize = maxsize
57 self.use_ascii = use_ascii
59 def repr(self, x: object) -> str:
60 try:
61 if self.use_ascii:
62 s = ascii(x)
63 else:
64 s = super().repr(x)
66 except (KeyboardInterrupt, SystemExit):
67 raise
68 except BaseException as exc:
69 s = _format_repr_exception(exc, x)
70 if self.maxsize is not None:
71 s = _ellipsize(s, self.maxsize)
72 return s
74 def repr_instance(self, x: object, level: int) -> str:
75 try:
76 s = repr(x)
77 except (KeyboardInterrupt, SystemExit):
78 raise
79 except BaseException as exc:
80 s = _format_repr_exception(exc, x)
81 if self.maxsize is not None:
82 s = _ellipsize(s, self.maxsize)
83 return s
86def safeformat(obj: object) -> str:
87 """Return a pretty printed string for the given object.
89 Failing __repr__ functions of user instances will be represented
90 with a short exception info.
91 """
92 try:
93 return pprint.pformat(obj)
94 except Exception as exc:
95 return _format_repr_exception(exc, obj)
98# Maximum size of overall repr of objects to display during assertion errors.
99DEFAULT_REPR_MAX_SIZE = 240
102def saferepr(
103 obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False
104) -> str:
105 """Return a size-limited safe repr-string for the given object.
107 Failing __repr__ functions of user instances will be represented
108 with a short exception info and 'saferepr' generally takes
109 care to never raise exceptions itself.
111 This function is a wrapper around the Repr/reprlib functionality of the
112 stdlib.
113 """
115 return SafeRepr(maxsize, use_ascii).repr(obj)
118def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str:
119 """Return an unlimited-size safe repr-string for the given object.
121 As with saferepr, failing __repr__ functions of user instances
122 will be represented with a short exception info.
124 This function is a wrapper around simple repr.
126 Note: a cleaner solution would be to alter ``saferepr``this way
127 when maxsize=None, but that might affect some other code.
128 """
129 try:
130 if use_ascii:
131 return ascii(obj)
132 return repr(obj)
133 except Exception as exc:
134 return _format_repr_exception(exc, obj)
137class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter):
138 """PrettyPrinter that always dispatches (regardless of width)."""
140 def _format(
141 self,
142 object: object,
143 stream: IO[str],
144 indent: int,
145 allowance: int,
146 context: Dict[int, Any],
147 level: int,
148 ) -> None:
149 # Type ignored because _dispatch is private.
150 p = self._dispatch.get(type(object).__repr__, None) # type: ignore[attr-defined]
152 objid = id(object)
153 if objid in context or p is None:
154 # Type ignored because _format is private.
155 super()._format( # type: ignore[misc]
156 object,
157 stream,
158 indent,
159 allowance,
160 context,
161 level,
162 )
163 return
165 context[objid] = 1
166 p(self, object, stream, indent, allowance, context, level + 1)
167 del context[objid]
170def _pformat_dispatch(
171 object: object,
172 indent: int = 1,
173 width: int = 80,
174 depth: Optional[int] = None,
175 *,
176 compact: bool = False,
177) -> str:
178 return AlwaysDispatchingPrettyPrinter(
179 indent=indent, width=width, depth=depth, compact=compact
180 ).pformat(object)