Skip to content

Commit 87b4d39

Browse files
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 dad6be5) Co-authored-by: Andrew Svetlov <[email protected]>
1 parent 753d0c0 commit 87b4d39

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

Lib/asyncio/futures.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ def _log_traceback(self, val):
118118

119119
def get_loop(self):
120120
"""Return the event loop the Future is bound to."""
121-
return self._loop
121+
loop = self._loop
122+
if loop is None:
123+
raise RuntimeError("Future object is not initialized.")
124+
return loop
122125

123126
def cancel(self):
124127
"""Cancel the future and schedule callbacks.

Lib/test/test_asyncio/test_futures.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,5 +818,44 @@ def _new_future(self):
818818
return futures._PyFuture(loop=self.loop)
819819

820820

821+
class BaseFutureInheritanceTests:
822+
823+
def _get_future_cls(self):
824+
raise NotImplementedError
825+
826+
def setUp(self):
827+
super().setUp()
828+
self.loop = self.new_test_loop()
829+
self.addCleanup(self.loop.close)
830+
831+
def test_inherit_without_calling_super_init(self):
832+
# See https://bugs.python.org/issue38785 for the context
833+
cls = self._get_future_cls()
834+
835+
class MyFut(cls):
836+
def __init__(self, *args, **kwargs):
837+
# don't call super().__init__()
838+
pass
839+
840+
fut = MyFut(loop=self.loop)
841+
with self.assertRaisesRegex(
842+
RuntimeError,
843+
"Future object is not initialized."
844+
):
845+
fut.get_loop()
846+
847+
848+
class PyFutureInheritanceTests(BaseFutureInheritanceTests,
849+
test_utils.TestCase):
850+
def _get_future_cls(self):
851+
return futures._PyFuture
852+
853+
854+
class CFutureInheritanceTests(BaseFutureInheritanceTests,
855+
test_utils.TestCase):
856+
def _get_future_cls(self):
857+
return futures._CFuture
858+
859+
821860
if __name__ == '__main__':
822861
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Prevent asyncio from crashing if parent ``__init__`` is not called from a
2+
constructor of object derived from ``asyncio.Future``.

Modules/_asynciomodule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,7 @@ static PyObject *
10891089
_asyncio_Future_get_loop_impl(FutureObj *self)
10901090
/*[clinic end generated code: output=119b6ea0c9816c3f input=cba48c2136c79d1f]*/
10911091
{
1092+
ENSURE_FUTURE_ALIVE(self)
10921093
Py_INCREF(self->fut_loop);
10931094
return self->fut_loop;
10941095
}

0 commit comments

Comments
 (0)