From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Wed, 13 Nov 2019 21:54:50 +0000 (-0800) Subject: bpo-38785: Prevent asyncio from crashing (GH-17144) X-Git-Tag: v3.7.6rc1~54 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=87b4d3994e4f81d6e39a5e86c1b7040df065ea37;p=thirdparty%2FPython%2Fcpython.git bpo-38785: Prevent asyncio from crashing (GH-17144) 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 --- diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 0e0e696a2535..1bd6c81406fa 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -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. diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 9608a3a81cc3..8bc861d3740f 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -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 index 000000000000..49e993799808 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-13-16-17-43.bpo-38785.NEOEfk.rst @@ -0,0 +1,2 @@ +Prevent asyncio from crashing if parent ``__init__`` is not called from a +constructor of object derived from ``asyncio.Future``. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 35264f5815c3..62173994a6ec 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -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; }