Skip to content

Commit 3499ef5

Browse files
Anusha ShekharAnusha Shekhar
authored andcommitted
Add Saferepr to tracer class
1 parent 82d4e1c commit 3499ef5

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

src/pluggy/_tracing.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import Callable
88
from typing import Sequence
99
from typing import Tuple
10+
import reprlib
1011

1112

1213
_Writer = Callable[[str], object]
@@ -59,6 +60,96 @@ def setprocessor(self, tags: str | tuple[str, ...], processor: _Processor) -> No
5960
assert isinstance(tags, tuple)
6061
self._tags2proc[tags] = processor
6162

63+
def _try_repr_or_str(obj: object) -> str:
64+
try:
65+
return repr(obj)
66+
except (KeyboardInterrupt, SystemExit):
67+
raise
68+
except BaseException:
69+
return f'{type(obj).__name__}("{obj}")'
70+
71+
def _format_repr_exception(exc: BaseException, obj: object) -> str:
72+
try:
73+
exc_info = _try_repr_or_str(exc)
74+
except (KeyboardInterrupt, SystemExit):
75+
raise
76+
except BaseException as exc:
77+
exc_info = f"unpresentable exception ({_try_repr_or_str(exc)})"
78+
return "<[{} raised in repr()] {} object at 0x{:x}>".format(
79+
exc_info, type(obj).__name__, id(obj)
80+
)
81+
82+
def _ellipsize(s: str, maxsize: int) -> str:
83+
if len(s) > maxsize:
84+
i = max(0, (maxsize - 3) // 2)
85+
j = max(0, maxsize - 3 - i)
86+
return s[:i] + "..." + s[len(s) - j :]
87+
return s
88+
89+
class SafeRepr(reprlib.Repr):
90+
"""
91+
repr.Repr that limits the resulting size of repr() and includes
92+
information on exceptions raised during the call.
93+
"""
94+
95+
def __init__(self, maxsize: Optional[int], use_ascii: bool = False) -> None:
96+
"""
97+
:param maxsize:
98+
If not None, will truncate the resulting repr to that specific size, using ellipsis
99+
somewhere in the middle to hide the extra text.
100+
If None, will not impose any size limits on the returning repr.
101+
"""
102+
super().__init__()
103+
# ``maxstring`` is used by the superclass, and needs to be an int; using a
104+
# very large number in case maxsize is None, meaning we want to disable
105+
# truncation.
106+
self.maxstring = maxsize if maxsize is not None else 1_000_000_000
107+
self.maxsize = maxsize
108+
self.use_ascii = use_ascii
109+
110+
def repr(self, x: object) -> str:
111+
try:
112+
if self.use_ascii:
113+
s = ascii(x)
114+
else:
115+
s = super().repr(x)
116+
117+
except (KeyboardInterrupt, SystemExit):
118+
raise
119+
except BaseException as exc:
120+
s = _format_repr_exception(exc, x)
121+
if self.maxsize is not None:
122+
s = _ellipsize(s, self.maxsize)
123+
return s
124+
125+
def repr_instance(self, x: object, level: int) -> str:
126+
try:
127+
s = repr(x)
128+
except (KeyboardInterrupt, SystemExit):
129+
raise
130+
except BaseException as exc:
131+
s = _format_repr_exception(exc, x)
132+
if self.maxsize is not None:
133+
s = _ellipsize(s, self.maxsize)
134+
return s
135+
136+
137+
# Maximum size of overall repr of objects to display during assertion errors.
138+
DEFAULT_REPR_MAX_SIZE = 240
139+
def saferepr(
140+
obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False
141+
) -> str:
142+
"""Return a size-limited safe repr-string for the given object.
143+
144+
Failing __repr__ functions of user instances will be represented
145+
with a short exception info and 'saferepr' generally takes
146+
care to never raise exceptions itself.
147+
148+
This function is a wrapper around the Repr/reprlib functionality of the
149+
stdlib.
150+
"""
151+
152+
return SafeRepr(maxsize, use_ascii).repr(obj)
62153

63154
class TagTracerSub:
64155
def __init__(self, root: TagTracer, tags: tuple[str, ...]) -> None:

0 commit comments

Comments
 (0)