]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-33110: Catch errors raised when running add_done_callback on already completed...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 22 May 2019 22:02:24 +0000 (15:02 -0700)
committerGitHub <noreply@github.com>
Wed, 22 May 2019 22:02:24 +0000 (15:02 -0700)
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.
(cherry picked from commit 2a3a2ece502c05ea33c95dd0db497189e0354bfd)

Co-authored-by: Sam Martin <ABitMoreDepth@users.noreply.github.com>
Lib/concurrent/futures/_base.py
Lib/test/test_concurrent_futures.py
Misc/NEWS.d/next/Library/2019-05-06-22-34-47.bpo-33110.rSJSCh.rst [new file with mode: 0644]

index 0b847307a328579e14bc548debb4f32e2ed496d9..46d30a50065e93255c09fac49da1326e7f5b1343 100644 (file)
@@ -400,7 +400,10 @@ class Future(object):
             if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]:
                 self._done_callbacks.append(fn)
                 return
-        fn(self)
+        try:
+            fn(self)
+        except Exception:
+            LOGGER.exception('exception calling callback for %r', self)
 
     def result(self, timeout=None):
         """Return the result of the call that the future represents.
index 59aa0f46f59fddea693d8058bcdd2da1105f6ee5..add2bfdd5abe2cdc56899bcbc1dcf32c065c4a1a 100644 (file)
@@ -1079,6 +1079,22 @@ class FutureTests(BaseTestCase):
         f.add_done_callback(fn)
         self.assertTrue(was_cancelled)
 
+    def test_done_callback_raises_already_succeeded(self):
+        with test.support.captured_stderr() as stderr:
+            def raising_fn(callback_future):
+                raise Exception('doh!')
+
+            f = Future()
+
+            # Set the result first to simulate a future that runs instantly,
+            # effectively allowing the callback to be run immediately.
+            f.set_result(5)
+            f.add_done_callback(raising_fn)
+
+            self.assertIn('exception calling callback for', stderr.getvalue())
+            self.assertIn('doh!', stderr.getvalue())
+
+
     def test_repr(self):
         self.assertRegex(repr(PENDING_FUTURE),
                          '<Future at 0x[0-9a-f]+ state=pending>')
diff --git a/Misc/NEWS.d/next/Library/2019-05-06-22-34-47.bpo-33110.rSJSCh.rst b/Misc/NEWS.d/next/Library/2019-05-06-22-34-47.bpo-33110.rSJSCh.rst
new file mode 100644 (file)
index 0000000..f1e2460
--- /dev/null
@@ -0,0 +1 @@
+Handle exceptions raised by functions added by concurrent.futures add_done_callback correctly when the Future has already completed.