From 7052fc38d51eaa3ef873cbeffa854c26bcaa9ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Dam=C3=A1zio?= Date: Wed, 29 Dec 2021 10:02:44 -0300 Subject: [PATCH 1/2] bpo-26552: close dangling coroutine or future if loop is closed --- Lib/asyncio/tasks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 53eef84427be10c..857755a4c27d0e5 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -612,6 +612,8 @@ def ensure_future(coro_or_future, *, loop=None): If the argument is a Future, it is returned directly. """ + if loop is None: + coro_or_future.close() return _ensure_future(coro_or_future, loop=loop) From 03f790d9406a818035d63ded535124ebbff8d215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Dam=C3=A1zio?= Date: Wed, 29 Dec 2021 16:53:57 -0300 Subject: [PATCH 2/2] adding flags to check for futures not wrapped --- Lib/asyncio/base_events.py | 10 ++++++++-- Lib/asyncio/events.py | 2 +- Lib/asyncio/tasks.py | 6 ++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 56ea7ba44e2eac1..96b768beffb72cd 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -419,12 +419,18 @@ def create_future(self): """Create a Future object attached to the loop.""" return futures.Future(loop=self) - def create_task(self, coro, *, name=None): + def create_task(self, coro, used_wrap_await=False, name=None): """Schedule a coroutine object. Return a task object. """ - self._check_closed() + try: + self._check_closed() + except RuntimeError: + if used_wrap_await: + coro.close() + raise + if self._task_factory is None: task = tasks.Task(coro, loop=self, name=name) if task._source_traceback: diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index d91fe8db2b02044..13244f7b9e7669b 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -274,7 +274,7 @@ def create_future(self): # Method scheduling a coroutine object: create a task. - def create_task(self, coro, *, name=None): + def create_task(self, coro, used_wrap_await=False, name=None): raise NotImplementedError # Methods for interacting with threads. diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 857755a4c27d0e5..b84e93103f14efd 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -328,7 +328,7 @@ def __wakeup(self, future): Task = _CTask = _asyncio.Task -def create_task(coro, *, name=None): +def create_task(coro, used_wrap_await=False, name=None): """Schedule the execution of a coroutine object in a spawn task. Return a Task object. @@ -618,6 +618,7 @@ def ensure_future(coro_or_future, *, loop=None): def _ensure_future(coro_or_future, *, loop=None): + wrap_await = False if futures.isfuture(coro_or_future): if loop is not None and loop is not futures._get_loop(coro_or_future): raise ValueError('The future belongs to a different loop than ' @@ -627,13 +628,14 @@ def _ensure_future(coro_or_future, *, loop=None): if not coroutines.iscoroutine(coro_or_future): if inspect.isawaitable(coro_or_future): coro_or_future = _wrap_awaitable(coro_or_future) + wrap_await = True else: raise TypeError('An asyncio.Future, a coroutine or an awaitable ' 'is required') if loop is None: loop = events._get_event_loop(stacklevel=4) - return loop.create_task(coro_or_future) + return loop.create_task(coro_or_future, used_wrap_await=wrap_await) @types.coroutine