]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-38785: Prevent asyncio from crashing (GH-17144)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 13 Nov 2019 21:54:50 +0000 (13:54 -0800)
committerGitHub <noreply@github.com>
Wed, 13 Nov 2019 21:54:50 +0000 (13:54 -0800)
if parent `__init__` is not called from a constructor of object derived from `asyncio.Future`

https://bugs.python.org/issue38785
(cherry picked from commit dad6be5ffe48beb74fad78cf758b886afddc7aed)

Co-authored-by: Andrew Svetlov <andrew.svetlov@gmail.com>
Lib/asyncio/futures.py
Lib/test/test_asyncio/test_futures.py
Misc/NEWS.d/next/Library/2019-11-13-16-17-43.bpo-38785.NEOEfk.rst [new file with mode: 0644]
Modules/_asynciomodule.c

index 0e0e696a253541d6042a63a70091ef4f217e7314..1bd6c81406fa2b9c5b448181502438d4a0fb202c 100644 (file)
@@ -118,7 +118,10 @@ class Future:
 
     def get_loop(self):
         """Return the event loop the Future is bound to."""
-        return self._loop
+        loop = self._loop
+        if loop is None:
+            raise RuntimeError("Future object is not initialized.")
+        return loop
 
     def cancel(self):
         """Cancel the future and schedule callbacks.
index 9608a3a81cc334fc01635668822bdc0afa046265..8bc861d3740f769814aa7032dcc5875ee72d5dd8 100644 (file)
@@ -818,5 +818,44 @@ class PyFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
         return futures._PyFuture(loop=self.loop)
 
 
+class BaseFutureInheritanceTests:
+
+    def _get_future_cls(self):
+        raise NotImplementedError
+
+    def setUp(self):
+        super().setUp()
+        self.loop = self.new_test_loop()
+        self.addCleanup(self.loop.close)
+
+    def test_inherit_without_calling_super_init(self):
+        # See https://bugs.python.org/issue38785 for the context
+        cls = self._get_future_cls()
+
+        class MyFut(cls):
+            def __init__(self, *args, **kwargs):
+                # don't call super().__init__()
+                pass
+
+        fut = MyFut(loop=self.loop)
+        with self.assertRaisesRegex(
+            RuntimeError,
+            "Future object is not initialized."
+        ):
+            fut.get_loop()
+
+
+class PyFutureInheritanceTests(BaseFutureInheritanceTests,
+                               test_utils.TestCase):
+    def _get_future_cls(self):
+        return futures._PyFuture
+
+
+class CFutureInheritanceTests(BaseFutureInheritanceTests,
+                              test_utils.TestCase):
+    def _get_future_cls(self):
+        return futures._CFuture
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2019-11-13-16-17-43.bpo-38785.NEOEfk.rst b/Misc/NEWS.d/next/Library/2019-11-13-16-17-43.bpo-38785.NEOEfk.rst
new file mode 100644 (file)
index 0000000..49e9937
--- /dev/null
@@ -0,0 +1,2 @@
+Prevent asyncio from crashing if parent ``__init__`` is not called from a
+constructor of object derived from ``asyncio.Future``.
index 35264f5815c3bce2ca37d184134f2e249a1ca433..62173994a6ec9de6ee4659ed415b506431c0ceb1 100644 (file)
@@ -1089,6 +1089,7 @@ static PyObject *
 _asyncio_Future_get_loop_impl(FutureObj *self)
 /*[clinic end generated code: output=119b6ea0c9816c3f input=cba48c2136c79d1f]*/
 {
+    ENSURE_FUTURE_ALIVE(self)
     Py_INCREF(self->fut_loop);
     return self->fut_loop;
 }