1
|
|
|
# -*- coding: utf-8 |
2
|
|
|
"""Unit tests for code in didyoumean_internal.py.""" |
3
|
|
|
from didyoumean_internal import quote, get_suggestion_string,\ |
4
|
|
|
add_string_to_exception, get_func_by_name,\ |
5
|
|
|
get_objects_in_frame, get_subclasses, get_types_for_str,\ |
6
|
|
|
get_types_for_str_using_inheritance,\ |
7
|
|
|
get_types_for_str_using_names |
8
|
|
|
import didyoumean_common_tests as common |
9
|
|
|
from didyoumean_common_tests import CommonTestOldStyleClass2,\ |
10
|
|
|
CommonTestNewStyleClass2 # to have these 2 in defined names |
11
|
|
|
import unittest2 |
12
|
|
|
import itertools |
13
|
|
|
import sys |
14
|
|
|
|
15
|
|
|
|
16
|
|
|
OLD_CLASS_SUPPORT = sys.version_info >= (3, 0) |
17
|
|
|
IS_PYPY = hasattr(sys, "pypy_translation_info") |
18
|
|
|
U_PREFIX_SUPPORT = not ((3, 0) <= sys.version_info < (3, 3)) |
19
|
|
|
U_PREFIX = "u" if U_PREFIX_SUPPORT else "" |
20
|
|
|
global_var = 42 # Please don't change the value |
21
|
|
|
|
22
|
|
|
|
23
|
|
|
class QuoteTests(unittest2.TestCase): |
24
|
|
|
"""Class for tests related to quote.""" |
25
|
|
|
|
26
|
|
|
def test_quote_empty_str(self): |
27
|
|
|
"""Test quote on empty string.""" |
28
|
|
|
self.assertEqual(quote(''), "''") |
29
|
|
|
|
30
|
|
|
def test_quote_str(self): |
31
|
|
|
"""Test quote on non-empty string.""" |
32
|
|
|
self.assertEqual(quote('abc'), "'abc'") |
33
|
|
|
|
34
|
|
|
|
35
|
|
|
class GetObjectInFrameTests(unittest2.TestCase): |
36
|
|
|
"""Class for tests related to frame/backtrace/etc inspection. |
37
|
|
|
|
38
|
|
|
Tested functions are : get_objects_in_frame. |
39
|
|
|
No tests about 'nonlocal' is written because it is only supported |
40
|
|
|
from Python 3. |
41
|
|
|
""" |
42
|
|
|
|
43
|
|
|
def name_corresponds_to(self, name, expected): |
44
|
|
|
"""Helper functions to test get_objects_in_frame. |
45
|
|
|
|
46
|
|
|
Check that the name corresponds to the expected objects (and their |
47
|
|
|
scope) in the frame of calling function. |
48
|
|
|
None can be used to match any object as it can be hard to describe |
49
|
|
|
an object when it is hidden by something in a closer scope. |
50
|
|
|
Also, extra care is to be taken when calling the function because |
51
|
|
|
giving value by names might affect the result (adding in local |
52
|
|
|
scope). |
53
|
|
|
""" |
54
|
|
|
frame = sys._getframe(1) # frame of calling function |
55
|
|
|
lst = get_objects_in_frame(frame).get(name, []) |
56
|
|
|
self.assertEqual(len(lst), len(expected)) |
57
|
|
|
for scopedobj, exp in zip(lst, expected): |
58
|
|
|
obj, scope = scopedobj |
59
|
|
|
expobj, expscope = exp |
60
|
|
|
self.assertEqual(scope, expscope, name) |
61
|
|
|
if expobj is not None: |
62
|
|
|
self.assertEqual(obj, expobj, name) |
63
|
|
|
|
64
|
|
|
def test_builtin(self): |
65
|
|
|
"""Test with builtin.""" |
66
|
|
|
builtin = len |
67
|
|
|
name = builtin.__name__ |
68
|
|
|
self.name_corresponds_to(name, [(builtin, 'builtin')]) |
69
|
|
|
|
70
|
|
|
def test_builtin2(self): |
71
|
|
|
"""Test with builtin.""" |
72
|
|
|
name = 'True' |
73
|
|
|
self.name_corresponds_to(name, [(bool(1), 'builtin')]) |
74
|
|
|
|
75
|
|
|
def test_global(self): |
76
|
|
|
"""Test with global.""" |
77
|
|
|
name = 'global_var' |
78
|
|
|
self.name_corresponds_to(name, [(42, 'global')]) |
79
|
|
|
|
80
|
|
|
def test_local(self): |
81
|
|
|
"""Test with local.""" |
82
|
|
|
name = 'toto' |
83
|
|
|
self.name_corresponds_to(name, []) |
84
|
|
|
toto = 0 # noqa |
85
|
|
|
self.name_corresponds_to(name, [(0, 'local')]) |
86
|
|
|
|
87
|
|
|
def test_local_and_global(self): |
88
|
|
|
"""Test with local hiding a global.""" |
89
|
|
|
name = 'global_var' |
90
|
|
|
self.name_corresponds_to(name, [(42, 'global')]) |
91
|
|
|
global_var = 1 # noqa |
92
|
|
|
self.name_corresponds_to(name, [(1, 'local'), (42, 'global')]) |
93
|
|
|
|
94
|
|
|
def test_global_keword(self): |
95
|
|
|
"""Test with global keyword.""" |
96
|
|
|
# Funny detail : the global keyword works even if at the end of |
97
|
|
|
# the function (after the code it affects) but this raises a |
98
|
|
|
# SyntaxWarning. |
99
|
|
|
global global_var |
100
|
|
|
name = 'global_var' |
101
|
|
|
global_var = 42 # value is unchanged |
102
|
|
|
self.name_corresponds_to(name, [(42, 'global')]) |
103
|
|
|
|
104
|
|
|
def test_del_local(self): |
105
|
|
|
"""Test with deleted local.""" |
106
|
|
|
name = 'toto' |
107
|
|
|
self.name_corresponds_to(name, []) |
108
|
|
|
toto = 0 |
109
|
|
|
self.name_corresponds_to(name, [(0, 'local')]) |
110
|
|
|
del toto |
111
|
|
|
self.name_corresponds_to(name, []) |
112
|
|
|
|
113
|
|
|
def test_del_local_hiding_global(self): |
114
|
|
|
"""Test with deleted local hiding a global.""" |
115
|
|
|
name = 'global_var' |
116
|
|
|
glob_desc = [(42, 'global')] |
117
|
|
|
local_desc = [(1, 'local')] |
118
|
|
|
self.name_corresponds_to(name, glob_desc) |
119
|
|
|
global_var = 1 |
120
|
|
|
self.name_corresponds_to(name, local_desc + glob_desc) |
121
|
|
|
del global_var |
122
|
|
|
self.name_corresponds_to(name, glob_desc) |
123
|
|
|
|
124
|
|
|
def test_enclosing(self): |
125
|
|
|
"""Test with nested functions.""" |
126
|
|
|
foo = 1 # noqa |
127
|
|
|
bar = 2 # noqa |
128
|
|
|
|
129
|
|
|
def nested_func(foo, baz): |
130
|
|
|
qux = 5 # noqa |
131
|
|
|
self.name_corresponds_to('qux', [(5, 'local')]) |
132
|
|
|
self.name_corresponds_to('baz', [(4, 'local')]) |
133
|
|
|
self.name_corresponds_to('foo', [(3, 'local')]) |
134
|
|
|
self.name_corresponds_to('bar', []) |
135
|
|
|
self.name_corresponds_to( |
136
|
|
|
'global_var', [(42, 'global')]) |
137
|
|
|
nested_func(3, 4) |
138
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
139
|
|
|
self.name_corresponds_to('foo', [(1, 'local')]) |
140
|
|
|
self.name_corresponds_to('baz', []) |
141
|
|
|
|
142
|
|
|
def test_enclosing2(self): |
143
|
|
|
"""Test with nested functions.""" |
144
|
|
|
bar = 2 # noqa |
145
|
|
|
|
146
|
|
|
def nested_func(): |
147
|
|
|
self.name_corresponds_to('bar', []) |
148
|
|
|
bar = 3 # noqa |
149
|
|
|
self.name_corresponds_to('bar', [(3, 'local')]) |
150
|
|
|
|
151
|
|
|
nested_func() |
152
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
153
|
|
|
|
154
|
|
|
def test_enclosing3(self): |
155
|
|
|
"""Test with nested functions.""" |
156
|
|
|
bar = 2 |
157
|
|
|
|
158
|
|
|
def nested_func(): |
159
|
|
|
self.name_corresponds_to('bar', [(2, 'local')]) |
160
|
|
|
tmp = bar # noqa |
161
|
|
|
self.name_corresponds_to('bar', [(2, 'local')]) |
162
|
|
|
|
163
|
|
|
nested_func() |
164
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
165
|
|
|
|
166
|
|
|
def test_enclosing4(self): |
167
|
|
|
"""Test with nested functions.""" |
168
|
|
|
global_var = 1 # noqa |
169
|
|
|
|
170
|
|
|
def nested_func(): |
171
|
|
|
self.name_corresponds_to('global_var', [(42, 'global')]) |
172
|
|
|
|
173
|
|
|
nested_func() |
174
|
|
|
self.name_corresponds_to('global_var', [(1, 'local'), (42, 'global')]) |
175
|
|
|
|
176
|
|
|
def test_enclosing5(self): |
177
|
|
|
"""Test with nested functions.""" |
178
|
|
|
bar = 2 # noqa |
179
|
|
|
foo = 3 # noqa |
180
|
|
|
|
181
|
|
|
def nested_func(): |
182
|
|
|
bar = 4 # noqa |
183
|
|
|
baz = 5 # noqa |
184
|
|
|
self.name_corresponds_to('foo', []) |
185
|
|
|
self.name_corresponds_to('bar', [(4, 'local')]) |
186
|
|
|
|
187
|
|
|
def nested_func2(): |
188
|
|
|
self.name_corresponds_to('foo', []) |
189
|
|
|
self.name_corresponds_to('bar', []) |
190
|
|
|
|
191
|
|
|
nested_func2() |
192
|
|
|
|
193
|
|
|
nested_func() |
194
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
195
|
|
|
|
196
|
|
|
|
197
|
|
|
class OldStyleBaseClass: |
198
|
|
|
"""Dummy class for testing purposes.""" |
199
|
|
|
|
200
|
|
|
pass |
201
|
|
|
|
202
|
|
|
|
203
|
|
|
class OldStyleDerivedClass(OldStyleBaseClass): |
204
|
|
|
"""Dummy class for testing purposes.""" |
205
|
|
|
|
206
|
|
|
pass |
207
|
|
|
|
208
|
|
|
|
209
|
|
|
class NewStyleBaseClass(object): |
210
|
|
|
"""Dummy class for testing purposes.""" |
211
|
|
|
|
212
|
|
|
pass |
213
|
|
|
|
214
|
|
|
|
215
|
|
|
class NewStyleDerivedClass(NewStyleBaseClass): |
216
|
|
|
"""Dummy class for testing purposes.""" |
217
|
|
|
|
218
|
|
|
pass |
219
|
|
|
|
220
|
|
|
|
221
|
|
|
def a_function(): |
222
|
|
|
"""Dummy function for testing purposes.""" |
223
|
|
|
pass |
224
|
|
|
|
225
|
|
|
|
226
|
|
|
def a_generator(): |
227
|
|
|
"""Dummy generator for testing purposes.""" |
228
|
|
|
yield 1 |
229
|
|
|
|
230
|
|
|
|
231
|
|
|
NEW_STYLE_CLASSES = [bool, int, float, str, tuple, list, set, dict, object, |
232
|
|
|
NewStyleBaseClass, NewStyleDerivedClass, |
233
|
|
|
common.CommonTestNewStyleClass, |
234
|
|
|
common.CommonTestNewStyleClass2, |
235
|
|
|
type(a_function), type(a_generator), |
236
|
|
|
type(len), type(None), type(type(None)), |
237
|
|
|
type(object), type(sys), type(range), |
238
|
|
|
type(NewStyleBaseClass), type(NewStyleDerivedClass), |
239
|
|
|
type(OldStyleBaseClass), type(OldStyleDerivedClass)] |
240
|
|
|
OLD_STYLE_CLASSES = [OldStyleBaseClass, OldStyleDerivedClass, |
241
|
|
|
CommonTestOldStyleClass2] |
242
|
|
|
CLASSES = [(c, True) for c in NEW_STYLE_CLASSES] + \ |
243
|
|
|
[(c, False) for c in OLD_STYLE_CLASSES] |
244
|
|
|
|
245
|
|
|
|
246
|
|
|
class GetTypesForStrTests(unittest2.TestCase): |
247
|
|
|
"""Test get_types_for_str.""" |
248
|
|
|
|
249
|
|
|
def test_get_subclasses(self): |
250
|
|
|
"""Test the get_subclasses function. |
251
|
|
|
|
252
|
|
|
All types are found when looking for subclasses of object, except |
253
|
|
|
for the old style classes on Python 2.x. |
254
|
|
|
""" |
255
|
|
|
all_classes = get_subclasses(object) |
256
|
|
|
for typ, new in CLASSES: |
257
|
|
|
self.assertTrue(typ in get_subclasses(typ)) |
258
|
|
|
if new or OLD_CLASS_SUPPORT: |
259
|
|
|
self.assertTrue(typ in all_classes) |
260
|
|
|
else: |
261
|
|
|
self.assertFalse(typ in all_classes) |
262
|
|
|
self.assertFalse(0 in all_classes) |
263
|
|
|
|
264
|
|
|
def test_get_types_for_str_using_inheritance(self): |
265
|
|
|
"""Test the get_types_for_str_using_inheritance function. |
266
|
|
|
|
267
|
|
|
All types are found when looking for subclasses of object, except |
268
|
|
|
for the old style classes on Python 2.x. |
269
|
|
|
|
270
|
|
|
Also, it seems like the returns is (almost) always precise as the |
271
|
|
|
returned set contains only the expected type and nothing else. |
272
|
|
|
""" |
273
|
|
|
for typ, new in CLASSES: |
274
|
|
|
types = get_types_for_str_using_inheritance(typ.__name__) |
275
|
|
|
if new or OLD_CLASS_SUPPORT: |
276
|
|
|
self.assertEqual(types, set([typ]), typ) |
277
|
|
|
else: |
278
|
|
|
self.assertEqual(types, set(), typ) |
279
|
|
|
|
280
|
|
|
self.assertFalse(get_types_for_str_using_inheritance('faketype')) |
281
|
|
|
|
282
|
|
|
def get_types_using_names(self, type_str): |
283
|
|
|
"""Wrapper around the get_types_using_names function.""" |
284
|
|
|
return get_types_for_str_using_names(type_str, sys._getframe(1)) |
285
|
|
|
|
286
|
|
|
def test_get_types_for_str_using_names(self): |
287
|
|
|
"""Test the get_types_using_names function. |
288
|
|
|
|
289
|
|
|
Old style classes are retrieved even on Python 2.x. |
290
|
|
|
However, a few builtin types are not in the names so can't be found. |
291
|
|
|
""" |
292
|
|
|
for typ in OLD_STYLE_CLASSES: |
293
|
|
|
types = self.get_types_using_names(typ.__name__) |
294
|
|
|
self.assertEqual(types, set([typ]), typ) |
295
|
|
|
for n in ['generator', 'module', 'function', 'faketype']: |
296
|
|
|
self.assertEqual(self.get_types_using_names(n), set(), n) |
297
|
|
|
n = 'NoneType' |
298
|
|
|
if IS_PYPY: |
299
|
|
|
self.assertEqual(len(self.get_types_using_names(n)), 1, n) |
300
|
|
|
else: |
301
|
|
|
self.assertEqual(self.get_types_using_names(n), set(), n) |
302
|
|
|
|
303
|
|
|
def get_types_for_str(self, type_str): |
304
|
|
|
"""Wrapper around the get_types_for_str function.""" |
305
|
|
|
return get_types_for_str(type_str, sys._getframe(1)) |
306
|
|
|
|
307
|
|
|
def test_get_types_for_str(self): |
308
|
|
|
"""Test the get_types_for_str function. |
309
|
|
|
|
310
|
|
|
Check that for all tested types, the proper type is retrieved. |
311
|
|
|
""" |
312
|
|
|
for typ, _ in CLASSES: |
313
|
|
|
types = self.get_types_for_str(typ.__name__) |
314
|
|
|
self.assertEqual(types, set([typ]), typ) |
315
|
|
|
|
316
|
|
|
self.assertEqual(self.get_types_for_str('faketype'), set()) |
317
|
|
|
|
318
|
|
|
def test_get_types_for_str2(self): |
319
|
|
|
"""Test the get_types_for_str function. |
320
|
|
|
|
321
|
|
|
Check that for all tested strings, a single type is retrived. |
322
|
|
|
This is useful to ensure that we are using the right names. |
323
|
|
|
""" |
324
|
|
|
for n in ['module', 'NoneType', 'function', |
325
|
|
|
'NewStyleBaseClass', 'NewStyleDerivedClass', |
326
|
|
|
'OldStyleBaseClass', 'OldStyleDerivedClass']: |
327
|
|
|
types = self.get_types_for_str(n) |
328
|
|
|
self.assertEqual(len(types), 1, n) |
329
|
|
|
for n in ['generator']: # FIXME: with pypy, we find an additional type |
330
|
|
|
types = self.get_types_for_str(n) |
331
|
|
|
self.assertEqual(len(types), 2 if IS_PYPY else 1, n) |
332
|
|
|
|
333
|
|
|
def test_old_class_not_in_namespace(self): |
334
|
|
|
"""Test the get_types_for_str function. |
335
|
|
|
|
336
|
|
|
Check that at the moment, CommonTestOldStyleClass is not found |
337
|
|
|
because it is not in the namespace. This behavior is to be improved. |
338
|
|
|
""" |
339
|
|
|
typ = common.CommonTestOldStyleClass |
340
|
|
|
expect_with_inherit = set([typ]) if OLD_CLASS_SUPPORT else set() |
341
|
|
|
name = typ.__name__ |
342
|
|
|
types1 = get_types_for_str_using_inheritance(name) |
343
|
|
|
types2 = self.get_types_using_names(name) |
344
|
|
|
types3 = self.get_types_for_str(name) |
345
|
|
|
self.assertEqual(types1, expect_with_inherit) |
346
|
|
|
self.assertEqual(types2, set()) |
347
|
|
|
self.assertEqual(types3, expect_with_inherit) |
348
|
|
|
|
349
|
|
|
|
350
|
|
|
class GetFuncByNameTests(unittest2.TestCase): |
351
|
|
|
"""Test get_func_by_name.""" |
352
|
|
|
|
353
|
|
|
def get_func_by_name(self, func_name): |
354
|
|
|
"""Wrapper around the get_func_by_name function.""" |
355
|
|
|
return get_func_by_name(func_name, sys._getframe(1)) |
356
|
|
|
|
357
|
|
|
def check_get_func_by_name(self, function, exact_match=True): |
358
|
|
|
"""Wrapper around the get_func_by_name to check its result.""" |
359
|
|
|
self.assertTrue(hasattr(function, '__name__'), function) |
360
|
|
|
res = self.get_func_by_name(function.__name__) |
361
|
|
|
self.assertTrue(function in res) |
362
|
|
|
self.assertTrue(len(res) >= 1, res) |
363
|
|
|
if exact_match: |
364
|
|
|
# Equality above does not hold |
365
|
|
|
# Using set is complicated because everything can't be hashed |
366
|
|
|
# But using id, something seems to be possible |
367
|
|
|
self.assertEqual(len(set(res)), 1, res) |
368
|
|
|
res_ids = [id(e) for e in res] |
369
|
|
|
set_ids = set(res_ids) |
370
|
|
|
self.assertEqual(len(set_ids), 1, set_ids) |
371
|
|
|
|
372
|
|
|
def test_get_builtin_by_name(self): |
373
|
|
|
"""Test get_func_by_name on builtin functions.""" |
374
|
|
|
for f in [bool, int, float, str, tuple, list, set, dict, all]: |
375
|
|
|
self.check_get_func_by_name(f) |
376
|
|
|
for f in [object]: |
377
|
|
|
self.check_get_func_by_name(f, False) |
378
|
|
|
|
379
|
|
|
def test_get_builtin_attr_by_name(self): |
380
|
|
|
"""Test get_func_by_name on builtin attributes.""" |
381
|
|
|
for f in [dict.get]: |
382
|
|
|
self.check_get_func_by_name(f, False) |
383
|
|
|
|
384
|
|
|
def test_get_lambda_by_name(self): |
385
|
|
|
"""Test get_func_by_name on lambda functions.""" |
386
|
|
|
self.check_get_func_by_name(lambda x: x) |
387
|
|
|
|
388
|
|
|
def test_get_custom_func_by_name(self): |
389
|
|
|
"""Test get_func_by_name on custom functions.""" |
390
|
|
|
for f in [a_function, a_generator]: |
391
|
|
|
self.check_get_func_by_name(f) |
392
|
|
|
|
393
|
|
|
def test_get_class_func_by_name(self): |
394
|
|
|
"""Test get_func_by_name on custom functions.""" |
395
|
|
|
for f, new in CLASSES: |
396
|
|
|
self.check_get_func_by_name(f, False) |
397
|
|
|
|
398
|
|
|
def test_inexisting_func(self): |
399
|
|
|
"""Test get_func_by_name on an inexisting function name.""" |
400
|
|
|
self.assertEqual(self.get_func_by_name('dkalskjdas'), []) |
401
|
|
|
|
402
|
|
|
|
403
|
|
|
class GetSuggStringTests(unittest2.TestCase): |
404
|
|
|
"""Tests about get_suggestion_string.""" |
405
|
|
|
|
406
|
|
|
def test_no_sugg(self): |
407
|
|
|
"""Empty list of suggestions.""" |
408
|
|
|
self.assertEqual(get_suggestion_string(()), "") |
409
|
|
|
|
410
|
|
|
def test_one_sugg(self): |
411
|
|
|
"""Single suggestion.""" |
412
|
|
|
self.assertEqual(get_suggestion_string(('0',)), ". Did you mean 0?") |
413
|
|
|
|
414
|
|
|
def test_same_sugg(self): |
415
|
|
|
"""Identical suggestion.""" |
416
|
|
|
self.assertEqual( |
417
|
|
|
get_suggestion_string(('0', '0')), ". Did you mean 0, 0?") |
418
|
|
|
|
419
|
|
|
def test_multiple_suggs(self): |
420
|
|
|
"""Multiple suggestions.""" |
421
|
|
|
self.assertEqual( |
422
|
|
|
get_suggestion_string(('0', '1')), ". Did you mean 0, 1?") |
423
|
|
|
|
424
|
|
|
|
425
|
|
|
class AddStringToExcTest(common.TestWithStringFunction): |
426
|
|
|
"""Generic class for tests about add_string_to_exception.""" |
427
|
|
|
|
428
|
|
|
prefix_repr = "" |
429
|
|
|
suffix_repr = "" |
430
|
|
|
check_str_sum = True |
431
|
|
|
|
432
|
|
|
def get_exception(self): |
433
|
|
|
"""Abstract method to get an instance of exception.""" |
434
|
|
|
raise NotImplementedError("'get_exception' needs to be implemented") |
435
|
|
|
|
436
|
|
|
def get_exc_before_and_after(self, string, func): |
437
|
|
|
"""Retrieve string representations of exceptions. |
438
|
|
|
|
439
|
|
|
Retrieve string representations of exceptions raised by code |
440
|
|
|
before and after calling add_string_to_exception. |
441
|
|
|
""" |
442
|
|
|
value = self.get_exception() |
443
|
|
|
before = func(value) |
444
|
|
|
add_string_to_exception(value, string) |
445
|
|
|
after = func(value) |
446
|
|
|
return (before, after) |
447
|
|
|
|
448
|
|
|
def check_string_added(self, func, string, prefix="", suffix=""): |
449
|
|
|
"""Check that add_string_to_exception adds the strings.""" |
450
|
|
|
s1, s2 = self.get_exc_before_and_after(string, func) |
451
|
|
|
self.assertStringAdded( |
452
|
|
|
prefix + string + suffix, s1, s2, self.check_str_sum) |
453
|
|
|
|
454
|
|
|
def test_add_empty_string_to_str(self): |
455
|
|
|
"""Empty string added to error's str value.""" |
456
|
|
|
self.check_string_added(str, "") |
457
|
|
|
|
458
|
|
|
def test_add_empty_string_to_repr(self): |
459
|
|
|
"""Empty string added to error's repr value.""" |
460
|
|
|
self.check_string_added(repr, "") |
461
|
|
|
|
462
|
|
|
def test_add_string_to_str(self): |
463
|
|
|
"""Non-empty string added to error's str value.""" |
464
|
|
|
self.check_string_added(str, "ABCDEstr") |
465
|
|
|
|
466
|
|
|
def test_add_string_to_repr(self): |
467
|
|
|
"""Non-empty string added to error's repr value.""" |
468
|
|
|
self.check_string_added( |
469
|
|
|
repr, "ABCDErepr", self.prefix_repr, self.suffix_repr) |
470
|
|
|
|
471
|
|
|
|
472
|
|
|
class AddStringToExcFromCodeTest(AddStringToExcTest): |
473
|
|
|
"""Generic class for tests about add_string_to_exception. |
474
|
|
|
|
475
|
|
|
The tested function is called on an exception created by running |
476
|
|
|
some failing code (`self.code`) and catching what it throws. |
477
|
|
|
""" |
478
|
|
|
|
479
|
|
|
code = NotImplemented |
480
|
|
|
|
481
|
|
|
def get_exception(self): |
482
|
|
|
"""Get the exception by running the code and catching errors.""" |
483
|
|
|
type_, value, _ = common.get_exception(self.code) |
484
|
|
|
self.assertTrue( |
485
|
|
|
issubclass(type_, self.error_type), |
486
|
|
|
"{0} ({1}) not a subclass of {2}" |
487
|
|
|
.format(type_, value, self.error_type)) |
488
|
|
|
return value |
489
|
|
|
|
490
|
|
|
|
491
|
|
|
class AddStringToNameErrorTest(unittest2.TestCase, AddStringToExcFromCodeTest): |
492
|
|
|
"""Class for tests of add_string_to_exception on NameError.""" |
493
|
|
|
|
494
|
|
|
code = 'babar = 0\nbaba' |
495
|
|
|
error_type = NameError |
496
|
|
|
|
497
|
|
|
|
498
|
|
|
class AddStringToTypeErrorTest(unittest2.TestCase, AddStringToExcFromCodeTest): |
499
|
|
|
"""Class for tests of add_string_to_exception on TypeError.""" |
500
|
|
|
|
501
|
|
|
code = '[0](0)' |
502
|
|
|
error_type = TypeError |
503
|
|
|
|
504
|
|
|
|
505
|
|
|
class AddStringToImportErrorTest( |
506
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
507
|
|
|
"""Class for tests of add_string_to_exception on ImportError.""" |
508
|
|
|
|
509
|
|
|
code = 'import maths' |
510
|
|
|
error_type = ImportError |
511
|
|
|
|
512
|
|
|
|
513
|
|
|
class AddStringToKeyErrorTest( |
514
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
515
|
|
|
"""Class for tests of add_string_to_exception on KeyError.""" |
516
|
|
|
|
517
|
|
|
code = 'dict()["ffdsqmjklfqsd"]' |
518
|
|
|
error_type = KeyError |
519
|
|
|
|
520
|
|
|
|
521
|
|
|
class AddStringToAttributeErrorTest( |
522
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
523
|
|
|
"""Class for tests of add_string_to_exception on AttributeError.""" |
524
|
|
|
|
525
|
|
|
code = '[].does_not_exist' |
526
|
|
|
error_type = AttributeError |
527
|
|
|
|
528
|
|
|
|
529
|
|
|
class AddStringToSyntaxErrorTest( |
530
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
531
|
|
|
"""Class for tests of add_string_to_exception on SyntaxError.""" |
532
|
|
|
|
533
|
|
|
code = 'return' |
534
|
|
|
error_type = SyntaxError |
535
|
|
|
|
536
|
|
|
|
537
|
|
|
class AddStringToMemoryErrorTest( |
538
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
539
|
|
|
"""Class for tests of add_string_to_exception on MemoryError.""" |
540
|
|
|
|
541
|
|
|
code = '[0] * 999999999999999' |
542
|
|
|
error_type = MemoryError |
543
|
|
|
prefix_repr = "'" |
544
|
|
|
# Trailing comma removed from Python 3.7 |
545
|
|
|
# See https://bugs.python.org/issue30399 |
546
|
|
|
suffix_repr = "'" if sys.version_info >= (3, 7) else "'," |
547
|
|
|
|
548
|
|
|
|
549
|
|
|
class AddStringToIOErrorTest( |
550
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
551
|
|
|
"""Class for tests of add_string_to_exception on NoFileIoError.""" |
552
|
|
|
|
553
|
|
|
code = 'with open("/does_not_exist") as f:\n\tpass' |
554
|
|
|
error_type = common.NoFileIoError |
555
|
|
|
|
556
|
|
|
|
557
|
|
|
class AddStringToUnicodeDecodeTest( |
558
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
559
|
|
|
"""Class for tests of add_string_to_exception on UnicodeDecodeError.""" |
560
|
|
|
|
561
|
|
|
code = "'foo'.encode('utf-16').decode('utf-8')" |
562
|
|
|
error_type = UnicodeDecodeError |
563
|
|
|
|
564
|
|
|
|
565
|
|
|
class AddStringToUnicodeEncodeTest( |
566
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
567
|
|
|
"""Class for tests of add_string_to_exception on UnicodeEncodeError.""" |
568
|
|
|
|
569
|
|
|
code = U_PREFIX + '"\u0411".encode("iso-8859-15")' |
570
|
|
|
error_type = UnicodeEncodeError |
571
|
|
|
|
572
|
|
|
|
573
|
|
|
class AddStringToExcFromInstanceTest(AddStringToExcTest): |
574
|
|
|
"""Generic class for tests about add_string_to_exception. |
575
|
|
|
|
576
|
|
|
The tested function is called on an exception created by calling the |
577
|
|
|
constructor (`self.exc_type`) with the right arguments (`self.args`). |
578
|
|
|
Because of the way it creates exception, the tests are somewhat artificial |
579
|
|
|
(compared to AddStringToExcFromCodeTest for instance). However, the major |
580
|
|
|
advantage is that they can be easily generated (to have all subclasses of |
581
|
|
|
Exception tested). |
582
|
|
|
""" |
583
|
|
|
|
584
|
|
|
check_str_sum = False |
585
|
|
|
exc_type = NotImplemented |
586
|
|
|
args = NotImplemented |
587
|
|
|
|
588
|
|
|
def get_exception(self): |
589
|
|
|
"""Get the exception by calling the constructor with correct args.""" |
590
|
|
|
return self.exc_type(*self.args) |
591
|
|
|
|
592
|
|
|
|
593
|
|
|
class AddStringToZeroDivisionError( |
594
|
|
|
unittest2.TestCase, AddStringToExcFromInstanceTest): |
595
|
|
|
"""Class for tests of add_string_to_exception on ZeroDivisionError.""" |
596
|
|
|
|
597
|
|
|
exc_type = ZeroDivisionError |
598
|
|
|
args = ('', '', '', '', '') |
599
|
|
|
|
600
|
|
|
|
601
|
|
|
def get_instance(klass): |
602
|
|
|
"""Get instance for class by bruteforcing the parameters. |
603
|
|
|
|
604
|
|
|
Construction is attempted with a decreasing number of arguments so that |
605
|
|
|
the instanciated object has as many non-null attributes set as possible. |
606
|
|
|
This is important not for the creation but when the object gets used |
607
|
|
|
later on. Also, the order of the values has its importance for similar |
608
|
|
|
reasons. |
609
|
|
|
""" |
610
|
|
|
my_unicode = str if sys.version_info >= (3, 0) else unicode |
611
|
|
|
values_tried = [my_unicode(), bytes(), 0] |
612
|
|
|
for nb_arg in reversed(range(6)): |
613
|
|
|
for p in itertools.product(values_tried, repeat=nb_arg): |
614
|
|
|
try: |
615
|
|
|
return klass(*p), p |
616
|
|
|
except (TypeError, AttributeError) as e: |
617
|
|
|
pass |
618
|
|
|
except Exception as e: |
619
|
|
|
print(type(e), e) |
620
|
|
|
return None |
621
|
|
|
|
622
|
|
|
|
623
|
|
|
def generate_add_string_to_exc_tests(): |
624
|
|
|
"""Generate tests for add_string_to_exception. |
625
|
|
|
|
626
|
|
|
This function dynamically creates tests cases for the function |
627
|
|
|
add_string_to_exception for as many Exception subclasses as possible. |
628
|
|
|
This is not used at the moment because the generated classes need to |
629
|
|
|
be added in the global namespace and there is no good way to do this. |
630
|
|
|
However, it may be a good idea to call this when new versions of |
631
|
|
|
Python are released to ensure we handle all exceptions properly (and |
632
|
|
|
find the tests to be added manually if need be). |
633
|
|
|
""" |
634
|
|
|
for klass in get_subclasses(Exception): |
635
|
|
|
r = get_instance(klass) |
636
|
|
|
if r is not None: |
637
|
|
|
_, p = r |
638
|
|
|
class_name = ("NameForAddStringToExcFromInstanceTest" + |
639
|
|
|
klass.__name__ + str(id(klass))) |
640
|
|
|
assert class_name not in globals(), class_name |
641
|
|
|
globals()[class_name] = type( |
642
|
|
|
class_name, |
643
|
|
|
(AddStringToExcFromInstanceTest, unittest2.TestCase), |
644
|
|
|
{'exc_type': klass, 'args': p}) |
645
|
|
|
|
646
|
|
|
|
647
|
|
|
if __name__ == '__main__': |
648
|
|
|
print(sys.version_info) |
649
|
|
|
unittest2.main() |
650
|
|
|
|