|
| 1 | +import gc |
1 | 2 | import itertools |
2 | 3 | import threading |
3 | 4 | import time |
| 5 | +import traceback |
4 | 6 | import weakref |
5 | 7 | from concurrent import futures |
6 | 8 | from operator import add |
@@ -80,7 +82,44 @@ def test_map_exception(self): |
80 | 82 | i = self.executor.map(divmod, [5, 5, 5, 5], [2, 3, 0, 5]) |
81 | 83 | self.assertEqual(next(i), (2, 1)) |
82 | 84 | self.assertEqual(next(i), (1, 2)) |
83 | | - self.assertRaises(ZeroDivisionError, next, i) |
| 85 | + |
| 86 | + exception = None |
| 87 | + try: |
| 88 | + i.__next__() |
| 89 | + except Exception as e: |
| 90 | + exception = e |
| 91 | + self.assertTrue( |
| 92 | + isinstance(exception, ZeroDivisionError), |
| 93 | + msg="should raise a ZeroDivisionError", |
| 94 | + ) |
| 95 | + |
| 96 | + # Wait for the completing thread to release its transient reference |
| 97 | + # to the exception; do not keep the referrers list alive in the loop. |
| 98 | + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, error=False): |
| 99 | + if not gc.get_referrers(exception): |
| 100 | + break |
| 101 | + |
| 102 | + self.assertFalse( |
| 103 | + gc.get_referrers(exception), |
| 104 | + msg="the exception should not have any referrers", |
| 105 | + ) |
| 106 | + |
| 107 | + self.assertFalse( |
| 108 | + [ |
| 109 | + (var, val) |
| 110 | + # go through the frames of the exception's traceback |
| 111 | + for frame, _ in traceback.walk_tb(exception.__traceback__) |
| 112 | + # skipping the current frame |
| 113 | + if frame is not exception.__traceback__.tb_frame |
| 114 | + # go through the locals captured in that frame |
| 115 | + for var, val in frame.f_locals.items() |
| 116 | + # check if one of them is an exception |
| 117 | + if isinstance(val, Exception) |
| 118 | + # check if it is captured in its own traceback |
| 119 | + and frame is val.__traceback__.tb_frame |
| 120 | + ], |
| 121 | + msg=f"the exception's traceback should not contain an exception captured in its own traceback", |
| 122 | + ) |
84 | 123 | self.assertEqual(next(i), (1, 0)) |
85 | 124 | self.assertRaises(StopIteration, next, i) |
86 | 125 | self.assertRaises(StopIteration, next, i) |
@@ -212,7 +251,7 @@ def test_map_buffersize_when_buffer_is_full(self): |
212 | 251 | self.assertEqual( |
213 | 252 | next(ints), |
214 | 253 | buffersize, |
215 | | - msg="should have fetched only `buffersize` elements from `ints`.", |
| 254 | + msg="should have fetched only `buffersize` elements from `ints`", |
216 | 255 | ) |
217 | 256 |
|
218 | 257 | @warnings_helper.ignore_fork_in_thread_deprecation_warnings() |
|
0 commit comments