|
| 1 | +import gc |
1 | 2 | import itertools |
2 | 3 | import threading |
3 | 4 | import time |
@@ -52,11 +53,33 @@ def test_map(self): |
52 | 53 | list(map(pow, range(10), range(10)))) |
53 | 54 |
|
54 | 55 | def test_map_exception(self): |
55 | | - i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5]) |
56 | | - self.assertEqual(i.__next__(), (0, 1)) |
57 | | - self.assertEqual(i.__next__(), (0, 1)) |
58 | | - with self.assertRaises(ZeroDivisionError): |
59 | | - i.__next__() |
| 56 | + results = self.executor.map(divmod, [3, 2, 1, 1], [2, 2, 0, 0]) |
| 57 | + |
| 58 | + # wait for all futures to complete |
| 59 | + self.executor.shutdown(wait=True) |
| 60 | + |
| 61 | + # the first two futures should have completed successfully |
| 62 | + self.assertEqual(next(results), (1, 1)) |
| 63 | + self.assertEqual(next(results), (1, 0)) |
| 64 | + |
| 65 | + # the next one should raise a ZeroDivisionError |
| 66 | + error = None |
| 67 | + try: |
| 68 | + next(results) |
| 69 | + except ZeroDivisionError as e: |
| 70 | + error = e |
| 71 | + self.assertTrue(error) |
| 72 | + |
| 73 | + # a failed future must not be captured in its |
| 74 | + # future._exception.__traceback__ to avoid a reference cycle |
| 75 | + self.assertFalse(gc.get_referrers(error)) |
| 76 | + |
| 77 | + traceback = error.__traceback__ # skip current local scope |
| 78 | + while (traceback := traceback.tb_next): |
| 79 | + self.assertNotRegex( |
| 80 | + str(traceback.tb_frame.f_locals), |
| 81 | + "<Future at 0x[a-z0-9]+ state=finished raised ZeroDivisionError>", |
| 82 | + ) |
60 | 83 |
|
61 | 84 | @support.requires_resource('walltime') |
62 | 85 | def test_map_timeout(self): |
|
0 commit comments