@@ -37,11 +37,35 @@ def __exit__(self, exc_type, exc_value, tb):
37
37
return False
38
38
39
39
40
+ # These are used to provide required context to things like subTest
41
+ __current_test__ = None
42
+ __test_result__ = None
43
+
44
+
45
+ class SubtestContext :
46
+ def __enter__ (self ):
47
+ pass
48
+
49
+ def __exit__ (self , * exc_info ):
50
+ if exc_info [0 ] is not None :
51
+ # Exception raised
52
+ global __test_result__ , __current_test__
53
+ handle_test_exception (
54
+ __current_test__ ,
55
+ __test_result__ ,
56
+ exc_info
57
+ )
58
+ # Suppress the exception as we've captured it above
59
+ return True
60
+
61
+
62
+
63
+
40
64
class NullContext :
41
65
def __enter__ (self ):
42
66
pass
43
67
44
- def __exit__ (self , a , b , c ):
68
+ def __exit__ (self , exc_type , exc_value , traceback ):
45
69
pass
46
70
47
71
@@ -61,7 +85,7 @@ def doCleanups(self):
61
85
func (* args , ** kwargs )
62
86
63
87
def subTest (self , msg = None , ** params ):
64
- return NullContext ( )
88
+ return SubtestContext ( msg = msg , params = params )
65
89
66
90
def skipTest (self , reason ):
67
91
raise SkipTest (reason )
@@ -298,15 +322,29 @@ def __add__(self, other):
298
322
return self
299
323
300
324
301
- def capture_exc (e ):
325
+ def capture_exc (exc , traceback ):
302
326
buf = io .StringIO ()
303
327
if hasattr (sys , "print_exception" ):
304
- sys .print_exception (e , buf )
328
+ sys .print_exception (exc , buf )
305
329
elif traceback is not None :
306
- traceback .print_exception (None , e , sys . exc_info ()[ 2 ] , file = buf )
330
+ traceback .print_exception (None , exc , traceback , file = buf )
307
331
return buf .getvalue ()
308
332
309
333
334
+ def handle_test_exception (current_test : tuple , test_result : TestResult , exc_info : tuple ):
335
+ exc = exc_info [1 ]
336
+ traceback = exc_info [2 ]
337
+ ex_str = capture_exc (exc , traceback )
338
+ if isinstance (exc , AssertionError ):
339
+ test_result .failuresNum += 1
340
+ test_result .failures .append ((current_test , ex_str ))
341
+ print (" FAIL" )
342
+ else :
343
+ test_result .errorsNum += 1
344
+ test_result .errors .append ((current_test , ex_str ))
345
+ print (" ERROR" )
346
+
347
+
310
348
def run_suite (c , test_result , suite_name = "" ):
311
349
if isinstance (c , TestSuite ):
312
350
c .run (test_result )
@@ -324,29 +362,34 @@ def run_suite(c, test_result, suite_name=""):
324
362
except AttributeError :
325
363
pass
326
364
327
- def run_one (m ):
365
+ def run_one (test_function ):
366
+ global __test_result__ , __current_test__
328
367
print ("%s (%s) ..." % (name , suite_name ), end = "" )
329
368
set_up ()
369
+ __test_result__ = test_result
370
+ test_container = f"({ suite_name } )"
371
+ __current_test__ = (name , test_container )
330
372
try :
331
373
test_result .testsRun += 1
332
- m ()
374
+ test_globals = dict (** globals ())
375
+ test_globals ["test_function" ] = test_function
376
+ exec ("test_function()" , test_globals , test_globals )
377
+ # No exception occurred, test passed
333
378
print (" ok" )
334
379
except SkipTest as e :
335
380
print (" skipped:" , e .args [0 ])
336
381
test_result .skippedNum += 1
337
382
except Exception as ex :
338
- ex_str = capture_exc (ex )
339
- if isinstance (ex , AssertionError ):
340
- test_result .failuresNum += 1
341
- test_result .failures .append (((name , c ), ex_str ))
342
- print (" FAIL" )
343
- else :
344
- test_result .errorsNum += 1
345
- test_result .errors .append (((name , c ), ex_str ))
346
- print (" ERROR" )
383
+ handle_test_exception (
384
+ current_test = (name , c ),
385
+ test_result = test_result ,
386
+ exc_info = sys .exc_info ()
387
+ )
347
388
# Uncomment to investigate failure in detail
348
389
# raise
349
390
finally :
391
+ __test_result__ = None
392
+ __current_test__ = None
350
393
tear_down ()
351
394
try :
352
395
o .doCleanups ()
0 commit comments