Skip to content

Commit 86a0b35

Browse files
Anusha ShekharAnusha Shekhar
authored andcommitted
Add tests for saferepr
1 parent cff191d commit 86a0b35

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed

testing/test_tracer.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import pytest
44

5+
from pluggy._tracing import saferepr, DEFAULT_REPR_MAX_SIZE
56
from pluggy._tracing import TagTracer
67

78

@@ -77,3 +78,173 @@ def test_setprocessor(rootlogger: TagTracer) -> None:
7778
log2("seen")
7879
tags, args = l2[0]
7980
assert args == ("seen",)
81+
82+
def test_saferepr_simple_repr():
83+
assert saferepr(1) == "1"
84+
assert saferepr(None) == "None"
85+
86+
87+
def test_saferepr_maxsize():
88+
s = saferepr("x" * 50, maxsize=25)
89+
assert len(s) == 25
90+
expected = repr("x" * 10 + "..." + "x" * 10)
91+
assert s == expected
92+
93+
94+
def test_saferepr_no_maxsize():
95+
text = "x" * DEFAULT_REPR_MAX_SIZE * 10
96+
s = saferepr(text, maxsize=None)
97+
expected = repr(text)
98+
assert s == expected
99+
100+
101+
def test_saferepr_maxsize_error_on_instance():
102+
class A:
103+
def __repr__(self):
104+
raise ValueError("...")
105+
106+
s = saferepr(("*" * 50, A()), maxsize=25)
107+
assert len(s) == 25
108+
assert s[0] == "(" and s[-1] == ")"
109+
110+
111+
def test_saferepr_exceptions() -> None:
112+
class BrokenRepr:
113+
def __init__(self, ex):
114+
self.ex = ex
115+
116+
def __repr__(self):
117+
raise self.ex
118+
119+
class BrokenReprException(Exception):
120+
__str__ = None # type: ignore[assignment]
121+
__repr__ = None # type: ignore[assignment]
122+
123+
assert "Exception" in saferepr(BrokenRepr(Exception("broken")))
124+
s = saferepr(BrokenReprException("really broken"))
125+
assert "TypeError" in s
126+
assert "TypeError" in saferepr(BrokenRepr("string"))
127+
128+
none = None
129+
try:
130+
none() # type: ignore[misc]
131+
except BaseException as exc:
132+
exp_exc = repr(exc)
133+
obj = BrokenRepr(BrokenReprException("omg even worse"))
134+
s2 = saferepr(obj)
135+
assert s2 == (
136+
"<[unpresentable exception ({!s}) raised in repr()] BrokenRepr object at 0x{:x}>".format(
137+
exp_exc, id(obj)
138+
)
139+
)
140+
141+
142+
def test_saferepr_baseexception():
143+
"""Test saferepr() with BaseExceptions, which includes pytest outcomes."""
144+
145+
class RaisingOnStrRepr(BaseException):
146+
def __init__(self, exc_types):
147+
self.exc_types = exc_types
148+
149+
def raise_exc(self, *args):
150+
try:
151+
self.exc_type = self.exc_types.pop(0)
152+
except IndexError:
153+
pass
154+
if hasattr(self.exc_type, "__call__"):
155+
raise self.exc_type(*args)
156+
raise self.exc_type
157+
158+
def __str__(self):
159+
self.raise_exc("__str__")
160+
161+
def __repr__(self):
162+
self.raise_exc("__repr__")
163+
164+
class BrokenObj:
165+
def __init__(self, exc):
166+
self.exc = exc
167+
168+
def __repr__(self):
169+
raise self.exc
170+
171+
__str__ = __repr__
172+
173+
baseexc_str = BaseException("__str__")
174+
obj = BrokenObj(RaisingOnStrRepr([BaseException]))
175+
assert saferepr(obj) == (
176+
"<[unpresentable exception ({!r}) "
177+
"raised in repr()] BrokenObj object at 0x{:x}>".format(baseexc_str, id(obj))
178+
)
179+
obj = BrokenObj(RaisingOnStrRepr([RaisingOnStrRepr([BaseException])]))
180+
assert saferepr(obj) == (
181+
"<[{!r} raised in repr()] BrokenObj object at 0x{:x}>".format(
182+
baseexc_str, id(obj)
183+
)
184+
)
185+
186+
with pytest.raises(KeyboardInterrupt):
187+
saferepr(BrokenObj(KeyboardInterrupt()))
188+
189+
with pytest.raises(SystemExit):
190+
saferepr(BrokenObj(SystemExit()))
191+
192+
with pytest.raises(KeyboardInterrupt):
193+
saferepr(BrokenObj(RaisingOnStrRepr([KeyboardInterrupt])))
194+
195+
with pytest.raises(SystemExit):
196+
saferepr(BrokenObj(RaisingOnStrRepr([SystemExit])))
197+
198+
with pytest.raises(KeyboardInterrupt):
199+
print(saferepr(BrokenObj(RaisingOnStrRepr([BaseException, KeyboardInterrupt]))))
200+
201+
with pytest.raises(SystemExit):
202+
saferepr(BrokenObj(RaisingOnStrRepr([BaseException, SystemExit])))
203+
204+
205+
def test_saferepr_buggy_builtin_repr():
206+
# Simulate a case where a repr for a builtin raises.
207+
# reprlib dispatches by type name, so use "int".
208+
209+
class int:
210+
def __repr__(self):
211+
raise ValueError("Buggy repr!")
212+
213+
assert "Buggy" in saferepr(int())
214+
215+
216+
def test_saferepr_big_repr():
217+
from _pytest._io.saferepr import SafeRepr
218+
219+
assert len(saferepr(range(1000))) <= len("[" + SafeRepr(0).maxlist * "1000" + "]")
220+
221+
222+
def test_saferepr_repr_on_newstyle() -> None:
223+
class Function:
224+
def __repr__(self):
225+
return "<%s>" % (self.name) # type: ignore[attr-defined]
226+
227+
assert saferepr(Function())
228+
229+
230+
def test_saferepr_unicode():
231+
val = "£€"
232+
reprval = "'£€'"
233+
assert saferepr(val) == reprval
234+
235+
236+
def test_saferepr_broken_getattribute():
237+
"""saferepr() can create proper representations of classes with
238+
broken __getattribute__ (#7145)
239+
"""
240+
241+
class SomeClass:
242+
def __getattribute__(self, attr):
243+
raise RuntimeError
244+
245+
def __repr__(self):
246+
raise RuntimeError
247+
248+
assert saferepr(SomeClass()).startswith(
249+
"<[RuntimeError() raised in repr()] SomeClass object at 0x"
250+
)

0 commit comments

Comments
 (0)