bpo-32610: Fix asyncio.all_tasks() to return only pending tasks. (GH-7174)

This commit is contained in:
Yury Selivanov 2018-05-28 17:54:02 -04:00 committed by GitHub
parent fdccfe09f0
commit 416c1ebd98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 9 deletions

View File

@ -443,6 +443,8 @@ Task
Return a set of all tasks for an event loop. Return a set of all tasks for an event loop.
By default all tasks for the current event loop are returned. By default all tasks for the current event loop are returned.
If *loop* is ``None``, :func:`get_event_loop` function
is used to get the current loop.
.. classmethod:: current_task(loop=None) .. classmethod:: current_task(loop=None)
@ -567,8 +569,9 @@ Task functions
Return a set of :class:`Task` objects created for the loop. Return a set of :class:`Task` objects created for the loop.
If *loop* is ``None`` :func:`get_event_loop` is used for getting If *loop* is ``None``, :func:`get_running_loop` is used for getting
current loop. current loop (contrary to the deprecated :meth:`Task.all_tasks` method
that uses :func:`get_event_loop`.)
.. versionadded:: 3.7 .. versionadded:: 3.7

View File

@ -18,6 +18,10 @@ from .subprocess import *
from .tasks import * from .tasks import *
from .transports import * from .transports import *
# Exposed for _asynciomodule.c to implement now deprecated
# Task.all_tasks() method. This function will be removed in 3.9.
from .tasks import _all_tasks_compat # NoQA
__all__ = (base_events.__all__ + __all__ = (base_events.__all__ +
coroutines.__all__ + coroutines.__all__ +
events.__all__ + events.__all__ +

View File

@ -51,8 +51,7 @@ def run(main, *, debug=False):
def _cancel_all_tasks(loop): def _cancel_all_tasks(loop):
to_cancel = [task for task in tasks.all_tasks(loop) to_cancel = tasks.all_tasks(loop)
if not task.done()]
if not to_cancel: if not to_cancel:
return return

View File

@ -33,6 +33,16 @@ def current_task(loop=None):
def all_tasks(loop=None): def all_tasks(loop=None):
"""Return a set of all tasks for the loop.""" """Return a set of all tasks for the loop."""
if loop is None:
loop = events.get_running_loop()
return {t for t in _all_tasks
if futures._get_loop(t) is loop and not t.done()}
def _all_tasks_compat(loop=None):
# Different from "all_task()" by returning *all* Tasks, including
# the completed ones. Used to implement deprecated "Tasks.all_task()"
# method.
if loop is None: if loop is None:
loop = events.get_event_loop() loop = events.get_event_loop()
return {t for t in _all_tasks if futures._get_loop(t) is loop} return {t for t in _all_tasks if futures._get_loop(t) is loop}
@ -82,7 +92,7 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
"use asyncio.all_tasks() instead", "use asyncio.all_tasks() instead",
PendingDeprecationWarning, PendingDeprecationWarning,
stacklevel=2) stacklevel=2)
return all_tasks(loop) return _all_tasks_compat(loop)
def __init__(self, coro, *, loop=None): def __init__(self, coro, *, loop=None):
super().__init__(loop=loop) super().__init__(loop=loop)

View File

@ -1897,8 +1897,10 @@ class BaseTaskTests:
# See http://bugs.python.org/issue29271 for details: # See http://bugs.python.org/issue29271 for details:
asyncio.set_event_loop(self.loop) asyncio.set_event_loop(self.loop)
try: try:
self.assertEqual(asyncio.all_tasks(), {task}) with self.assertWarns(PendingDeprecationWarning):
self.assertEqual(asyncio.all_tasks(None), {task}) self.assertEqual(Task.all_tasks(), {task})
with self.assertWarns(PendingDeprecationWarning):
self.assertEqual(Task.all_tasks(None), {task})
finally: finally:
asyncio.set_event_loop(None) asyncio.set_event_loop(None)
@ -2483,6 +2485,9 @@ class BaseTaskIntrospectionTests:
def _loop(self): def _loop(self):
return loop return loop
def done(self):
return False
task = TaskLike() task = TaskLike()
loop = mock.Mock() loop = mock.Mock()
@ -2496,6 +2501,9 @@ class BaseTaskIntrospectionTests:
def get_loop(self): def get_loop(self):
return loop return loop
def done(self):
return False
task = TaskLike() task = TaskLike()
loop = mock.Mock() loop = mock.Mock()
@ -2504,6 +2512,23 @@ class BaseTaskIntrospectionTests:
self.assertEqual(asyncio.all_tasks(loop), {task}) self.assertEqual(asyncio.all_tasks(loop), {task})
self._unregister_task(task) self._unregister_task(task)
def test__register_task_3(self):
class TaskLike:
def get_loop(self):
return loop
def done(self):
return True
task = TaskLike()
loop = mock.Mock()
self.assertEqual(asyncio.all_tasks(loop), set())
self._register_task(task)
self.assertEqual(asyncio.all_tasks(loop), set())
self.assertEqual(asyncio.Task.all_tasks(loop), {task})
self._unregister_task(task)
def test__enter_task(self): def test__enter_task(self):
task = mock.Mock() task = mock.Mock()
loop = mock.Mock() loop = mock.Mock()

View File

@ -0,0 +1 @@
Make asyncio.all_tasks() return only pending tasks.

View File

@ -11,7 +11,7 @@ module _asyncio
/* identifiers used from some functions */ /* identifiers used from some functions */
_Py_IDENTIFIER(__asyncio_running_event_loop__); _Py_IDENTIFIER(__asyncio_running_event_loop__);
_Py_IDENTIFIER(add_done_callback); _Py_IDENTIFIER(add_done_callback);
_Py_IDENTIFIER(all_tasks); _Py_IDENTIFIER(_all_tasks_compat);
_Py_IDENTIFIER(call_soon); _Py_IDENTIFIER(call_soon);
_Py_IDENTIFIER(cancel); _Py_IDENTIFIER(cancel);
_Py_IDENTIFIER(current_task); _Py_IDENTIFIER(current_task);
@ -2125,7 +2125,7 @@ _asyncio_Task_all_tasks_impl(PyTypeObject *type, PyObject *loop)
return NULL; return NULL;
} }
all_tasks_func = _PyObject_GetAttrId(asyncio_mod, &PyId_all_tasks); all_tasks_func = _PyObject_GetAttrId(asyncio_mod, &PyId__all_tasks_compat);
if (all_tasks_func == NULL) { if (all_tasks_func == NULL) {
return NULL; return NULL;
} }