Skip to content

Commit 78a7592

Browse files
committed
extend test_map_exception to assert there is no reference cycles with future._exception
1 parent a307dad commit 78a7592

1 file changed

Lines changed: 28 additions & 5 deletions

File tree

Lib/test/test_concurrent_futures/executor.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import gc
12
import itertools
23
import threading
34
import time
@@ -52,11 +53,33 @@ def test_map(self):
5253
list(map(pow, range(10), range(10))))
5354

5455
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+
)
6083

6184
@support.requires_resource('walltime')
6285
def test_map_timeout(self):

0 commit comments

Comments
 (0)