2
2
3
3
import pytest
4
4
5
+ from pluggy ._tracing import saferepr , DEFAULT_REPR_MAX_SIZE
5
6
from pluggy ._tracing import TagTracer
6
7
7
8
@@ -77,3 +78,173 @@ def test_setprocessor(rootlogger: TagTracer) -> None:
77
78
log2 ("seen" )
78
79
tags , args = l2 [0 ]
79
80
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