From dad6be5ffe48beb74fad78cf758b886afddc7aed Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Wed, 13 Nov 2019 23:36:46 +0200 Subject: [PATCH] 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 --- Lib/asyncio/futures.py | 5 ++- Lib/test/test_asyncio/test_futures.py | 39 +++++++++++++++++++ .../2019-11-13-16-17-43.bpo-38785.NEOEfk.rst | 2 + Modules/_asynciomodule.c | 1 + 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2019-11-13-16-17-43.bpo-38785.NEOEfk.rst diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 98a5308ed06..9afda220bd7 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -115,7 +115,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 2e4583d1245..ee5edd5bd31 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -822,5 +822,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 00000000000..49e99379980 --- /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 89b2fdea0f6..5e1bcfbde24 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1091,6 +1091,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; }