Skip to content

Commit 2a3a2ec

Browse files
Sam Martinpitrou
authored andcommitted
bpo-33110: Catch errors raised when running add_done_callback on already completed futures (GH-13141)
Wrap the callback call within the `add_done_callback` function within concurrent.futures, in order to behave in an identical manner to callbacks added to a running future are triggered once it has completed.
1 parent d8a82e2 commit 2a3a2ec

File tree

3 files changed

+21
-1
lines changed

3 files changed

+21
-1
lines changed

Lib/concurrent/futures/_base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,10 @@ def add_done_callback(self, fn):
404404
if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]:
405405
self._done_callbacks.append(fn)
406406
return
407-
fn(self)
407+
try:
408+
fn(self)
409+
except Exception:
410+
LOGGER.exception('exception calling callback for %r', self)
408411

409412
def result(self, timeout=None):
410413
"""Return the result of the call that the future represents.

Lib/test/test_concurrent_futures.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,22 @@ def fn(callback_future):
10871087
f.add_done_callback(fn)
10881088
self.assertTrue(was_cancelled)
10891089

1090+
def test_done_callback_raises_already_succeeded(self):
1091+
with test.support.captured_stderr() as stderr:
1092+
def raising_fn(callback_future):
1093+
raise Exception('doh!')
1094+
1095+
f = Future()
1096+
1097+
# Set the result first to simulate a future that runs instantly,
1098+
# effectively allowing the callback to be run immediately.
1099+
f.set_result(5)
1100+
f.add_done_callback(raising_fn)
1101+
1102+
self.assertIn('exception calling callback for', stderr.getvalue())
1103+
self.assertIn('doh!', stderr.getvalue())
1104+
1105+
10901106
def test_repr(self):
10911107
self.assertRegex(repr(PENDING_FUTURE),
10921108
'<Future at 0x[0-9a-f]+ state=pending>')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Handle exceptions raised by functions added by concurrent.futures add_done_callback correctly when the Future has already completed.

0 commit comments

Comments
 (0)