mirror of https://github.com/python/cpython
bpo-32311: Implement asyncio.create_task() shortcut (#4848)
* Implement functionality * Add documentation
This commit is contained in:
parent
19a44f63c7
commit
f74ef458ab
|
@ -371,10 +371,21 @@ with the result.
|
||||||
Task
|
Task
|
||||||
----
|
----
|
||||||
|
|
||||||
|
.. function:: create_task(coro)
|
||||||
|
|
||||||
|
Wrap a :ref:`coroutine <coroutine>` *coro* into a task and schedule
|
||||||
|
its execution. Return the task object.
|
||||||
|
|
||||||
|
The task is executed in :func:`get_running_loop` context,
|
||||||
|
:exc:`RuntimeError` is raised if there is no running loop in
|
||||||
|
current thread.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
.. class:: Task(coro, \*, loop=None)
|
.. class:: Task(coro, \*, loop=None)
|
||||||
|
|
||||||
Schedule the execution of a :ref:`coroutine <coroutine>`: wrap it in a
|
A unit for concurrent running of :ref:`coroutines <coroutine>`,
|
||||||
future. A task is a subclass of :class:`Future`.
|
subclass of :class:`Future`.
|
||||||
|
|
||||||
A task is responsible for executing a coroutine object in an event loop. If
|
A task is responsible for executing a coroutine object in an event loop. If
|
||||||
the wrapped coroutine yields from a future, the task suspends the execution
|
the wrapped coroutine yields from a future, the task suspends the execution
|
||||||
|
@ -399,7 +410,7 @@ Task
|
||||||
<coroutine>` did not complete. It is probably a bug and a warning is
|
<coroutine>` did not complete. It is probably a bug and a warning is
|
||||||
logged: see :ref:`Pending task destroyed <asyncio-pending-task-destroyed>`.
|
logged: see :ref:`Pending task destroyed <asyncio-pending-task-destroyed>`.
|
||||||
|
|
||||||
Don't directly create :class:`Task` instances: use the :func:`ensure_future`
|
Don't directly create :class:`Task` instances: use the :func:`create_task`
|
||||||
function or the :meth:`AbstractEventLoop.create_task` method.
|
function or the :meth:`AbstractEventLoop.create_task` method.
|
||||||
|
|
||||||
This class is :ref:`not thread safe <asyncio-multithreading>`.
|
This class is :ref:`not thread safe <asyncio-multithreading>`.
|
||||||
|
@ -547,9 +558,15 @@ Task functions
|
||||||
.. versionchanged:: 3.5.1
|
.. versionchanged:: 3.5.1
|
||||||
The function accepts any :term:`awaitable` object.
|
The function accepts any :term:`awaitable` object.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
:func:`create_task` (added in Python 3.7) is the preferable way
|
||||||
|
for spawning new tasks.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
The :meth:`AbstractEventLoop.create_task` method.
|
The :func:`create_task` function and
|
||||||
|
:meth:`AbstractEventLoop.create_task` method.
|
||||||
|
|
||||||
.. function:: wrap_future(future, \*, loop=None)
|
.. function:: wrap_future(future, \*, loop=None)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ __all__ = ()
|
||||||
import concurrent.futures._base
|
import concurrent.futures._base
|
||||||
import reprlib
|
import reprlib
|
||||||
|
|
||||||
from . import events
|
from . import format_helpers
|
||||||
|
|
||||||
Error = concurrent.futures._base.Error
|
Error = concurrent.futures._base.Error
|
||||||
CancelledError = concurrent.futures.CancelledError
|
CancelledError = concurrent.futures.CancelledError
|
||||||
|
@ -38,7 +38,7 @@ def _format_callbacks(cb):
|
||||||
cb = ''
|
cb = ''
|
||||||
|
|
||||||
def format_cb(callback):
|
def format_cb(callback):
|
||||||
return events._format_callback_source(callback, ())
|
return format_helpers._format_callback_source(callback, ())
|
||||||
|
|
||||||
if size == 1:
|
if size == 1:
|
||||||
cb = format_cb(cb[0])
|
cb = format_cb(cb[0])
|
||||||
|
|
|
@ -6,5 +6,5 @@ ACCEPT_RETRY_DELAY = 1
|
||||||
|
|
||||||
# Number of stack entries to capture in debug mode.
|
# Number of stack entries to capture in debug mode.
|
||||||
# The larger the number, the slower the operation in debug mode
|
# The larger the number, the slower the operation in debug mode
|
||||||
# (see extract_stack() in events.py).
|
# (see extract_stack() in format_helpers.py).
|
||||||
DEBUG_STACK_DEPTH = 10
|
DEBUG_STACK_DEPTH = 10
|
||||||
|
|
|
@ -9,9 +9,9 @@ import types
|
||||||
|
|
||||||
from collections.abc import Awaitable, Coroutine
|
from collections.abc import Awaitable, Coroutine
|
||||||
|
|
||||||
from . import constants
|
|
||||||
from . import events
|
|
||||||
from . import base_futures
|
from . import base_futures
|
||||||
|
from . import constants
|
||||||
|
from . import format_helpers
|
||||||
from .log import logger
|
from .log import logger
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class CoroWrapper:
|
||||||
assert inspect.isgenerator(gen) or inspect.iscoroutine(gen), gen
|
assert inspect.isgenerator(gen) or inspect.iscoroutine(gen), gen
|
||||||
self.gen = gen
|
self.gen = gen
|
||||||
self.func = func # Used to unwrap @coroutine decorator
|
self.func = func # Used to unwrap @coroutine decorator
|
||||||
self._source_traceback = events.extract_stack(sys._getframe(1))
|
self._source_traceback = format_helpers.extract_stack(sys._getframe(1))
|
||||||
self.__name__ = getattr(gen, '__name__', None)
|
self.__name__ = getattr(gen, '__name__', None)
|
||||||
self.__qualname__ = getattr(gen, '__qualname__', None)
|
self.__qualname__ = getattr(gen, '__qualname__', None)
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ def _format_coroutine(coro):
|
||||||
func = coro
|
func = coro
|
||||||
|
|
||||||
if coro_name is None:
|
if coro_name is None:
|
||||||
coro_name = events._format_callback(func, (), {})
|
coro_name = format_helpers._format_callback(func, (), {})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
coro_code = coro.gi_code
|
coro_code = coro.gi_code
|
||||||
|
@ -260,7 +260,7 @@ def _format_coroutine(coro):
|
||||||
if (isinstance(coro, CoroWrapper) and
|
if (isinstance(coro, CoroWrapper) and
|
||||||
not inspect.isgeneratorfunction(coro.func) and
|
not inspect.isgeneratorfunction(coro.func) and
|
||||||
coro.func is not None):
|
coro.func is not None):
|
||||||
source = events._get_function_source(coro.func)
|
source = format_helpers._get_function_source(coro.func)
|
||||||
if source is not None:
|
if source is not None:
|
||||||
filename, lineno = source
|
filename, lineno = source
|
||||||
if coro_frame is None:
|
if coro_frame is None:
|
||||||
|
|
|
@ -11,86 +11,14 @@ __all__ = (
|
||||||
'_get_running_loop',
|
'_get_running_loop',
|
||||||
)
|
)
|
||||||
|
|
||||||
import functools
|
|
||||||
import inspect
|
|
||||||
import os
|
import os
|
||||||
import reprlib
|
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
|
||||||
|
|
||||||
from . import constants
|
from . import constants
|
||||||
|
from . import format_helpers
|
||||||
|
|
||||||
def _get_function_source(func):
|
|
||||||
func = inspect.unwrap(func)
|
|
||||||
if inspect.isfunction(func):
|
|
||||||
code = func.__code__
|
|
||||||
return (code.co_filename, code.co_firstlineno)
|
|
||||||
if isinstance(func, functools.partial):
|
|
||||||
return _get_function_source(func.func)
|
|
||||||
if isinstance(func, functools.partialmethod):
|
|
||||||
return _get_function_source(func.func)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _format_args_and_kwargs(args, kwargs):
|
|
||||||
"""Format function arguments and keyword arguments.
|
|
||||||
|
|
||||||
Special case for a single parameter: ('hello',) is formatted as ('hello').
|
|
||||||
"""
|
|
||||||
# use reprlib to limit the length of the output
|
|
||||||
items = []
|
|
||||||
if args:
|
|
||||||
items.extend(reprlib.repr(arg) for arg in args)
|
|
||||||
if kwargs:
|
|
||||||
items.extend(f'{k}={reprlib.repr(v)}' for k, v in kwargs.items())
|
|
||||||
return '({})'.format(', '.join(items))
|
|
||||||
|
|
||||||
|
|
||||||
def _format_callback(func, args, kwargs, suffix=''):
|
|
||||||
if isinstance(func, functools.partial):
|
|
||||||
suffix = _format_args_and_kwargs(args, kwargs) + suffix
|
|
||||||
return _format_callback(func.func, func.args, func.keywords, suffix)
|
|
||||||
|
|
||||||
if hasattr(func, '__qualname__'):
|
|
||||||
func_repr = getattr(func, '__qualname__')
|
|
||||||
elif hasattr(func, '__name__'):
|
|
||||||
func_repr = getattr(func, '__name__')
|
|
||||||
else:
|
|
||||||
func_repr = repr(func)
|
|
||||||
|
|
||||||
func_repr += _format_args_and_kwargs(args, kwargs)
|
|
||||||
if suffix:
|
|
||||||
func_repr += suffix
|
|
||||||
return func_repr
|
|
||||||
|
|
||||||
|
|
||||||
def _format_callback_source(func, args):
|
|
||||||
func_repr = _format_callback(func, args, None)
|
|
||||||
source = _get_function_source(func)
|
|
||||||
if source:
|
|
||||||
func_repr += f' at {source[0]}:{source[1]}'
|
|
||||||
return func_repr
|
|
||||||
|
|
||||||
|
|
||||||
def extract_stack(f=None, limit=None):
|
|
||||||
"""Replacement for traceback.extract_stack() that only does the
|
|
||||||
necessary work for asyncio debug mode.
|
|
||||||
"""
|
|
||||||
if f is None:
|
|
||||||
f = sys._getframe().f_back
|
|
||||||
if limit is None:
|
|
||||||
# Limit the amount of work to a reasonable amount, as extract_stack()
|
|
||||||
# can be called for each coroutine and future in debug mode.
|
|
||||||
limit = constants.DEBUG_STACK_DEPTH
|
|
||||||
stack = traceback.StackSummary.extract(traceback.walk_stack(f),
|
|
||||||
limit=limit,
|
|
||||||
lookup_lines=False)
|
|
||||||
stack.reverse()
|
|
||||||
return stack
|
|
||||||
|
|
||||||
|
|
||||||
class Handle:
|
class Handle:
|
||||||
|
@ -106,7 +34,8 @@ class Handle:
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
self._repr = None
|
self._repr = None
|
||||||
if self._loop.get_debug():
|
if self._loop.get_debug():
|
||||||
self._source_traceback = extract_stack(sys._getframe(1))
|
self._source_traceback = format_helpers.extract_stack(
|
||||||
|
sys._getframe(1))
|
||||||
else:
|
else:
|
||||||
self._source_traceback = None
|
self._source_traceback = None
|
||||||
|
|
||||||
|
@ -115,7 +44,8 @@ class Handle:
|
||||||
if self._cancelled:
|
if self._cancelled:
|
||||||
info.append('cancelled')
|
info.append('cancelled')
|
||||||
if self._callback is not None:
|
if self._callback is not None:
|
||||||
info.append(_format_callback_source(self._callback, self._args))
|
info.append(format_helpers._format_callback_source(
|
||||||
|
self._callback, self._args))
|
||||||
if self._source_traceback:
|
if self._source_traceback:
|
||||||
frame = self._source_traceback[-1]
|
frame = self._source_traceback[-1]
|
||||||
info.append(f'created at {frame[0]}:{frame[1]}')
|
info.append(f'created at {frame[0]}:{frame[1]}')
|
||||||
|
@ -145,7 +75,8 @@ class Handle:
|
||||||
try:
|
try:
|
||||||
self._callback(*self._args)
|
self._callback(*self._args)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
cb = _format_callback_source(self._callback, self._args)
|
cb = format_helpers._format_callback_source(
|
||||||
|
self._callback, self._args)
|
||||||
msg = f'Exception in callback {cb}'
|
msg = f'Exception in callback {cb}'
|
||||||
context = {
|
context = {
|
||||||
'message': msg,
|
'message': msg,
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import functools
|
||||||
|
import inspect
|
||||||
|
import reprlib
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from . import constants
|
||||||
|
|
||||||
|
|
||||||
|
def _get_function_source(func):
|
||||||
|
func = inspect.unwrap(func)
|
||||||
|
if inspect.isfunction(func):
|
||||||
|
code = func.__code__
|
||||||
|
return (code.co_filename, code.co_firstlineno)
|
||||||
|
if isinstance(func, functools.partial):
|
||||||
|
return _get_function_source(func.func)
|
||||||
|
if isinstance(func, functools.partialmethod):
|
||||||
|
return _get_function_source(func.func)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_callback_source(func, args):
|
||||||
|
func_repr = _format_callback(func, args, None)
|
||||||
|
source = _get_function_source(func)
|
||||||
|
if source:
|
||||||
|
func_repr += f' at {source[0]}:{source[1]}'
|
||||||
|
return func_repr
|
||||||
|
|
||||||
|
|
||||||
|
def _format_args_and_kwargs(args, kwargs):
|
||||||
|
"""Format function arguments and keyword arguments.
|
||||||
|
|
||||||
|
Special case for a single parameter: ('hello',) is formatted as ('hello').
|
||||||
|
"""
|
||||||
|
# use reprlib to limit the length of the output
|
||||||
|
items = []
|
||||||
|
if args:
|
||||||
|
items.extend(reprlib.repr(arg) for arg in args)
|
||||||
|
if kwargs:
|
||||||
|
items.extend(f'{k}={reprlib.repr(v)}' for k, v in kwargs.items())
|
||||||
|
return '({})'.format(', '.join(items))
|
||||||
|
|
||||||
|
|
||||||
|
def _format_callback(func, args, kwargs, suffix=''):
|
||||||
|
if isinstance(func, functools.partial):
|
||||||
|
suffix = _format_args_and_kwargs(args, kwargs) + suffix
|
||||||
|
return _format_callback(func.func, func.args, func.keywords, suffix)
|
||||||
|
|
||||||
|
if hasattr(func, '__qualname__'):
|
||||||
|
func_repr = getattr(func, '__qualname__')
|
||||||
|
elif hasattr(func, '__name__'):
|
||||||
|
func_repr = getattr(func, '__name__')
|
||||||
|
else:
|
||||||
|
func_repr = repr(func)
|
||||||
|
|
||||||
|
func_repr += _format_args_and_kwargs(args, kwargs)
|
||||||
|
if suffix:
|
||||||
|
func_repr += suffix
|
||||||
|
return func_repr
|
||||||
|
|
||||||
|
|
||||||
|
def extract_stack(f=None, limit=None):
|
||||||
|
"""Replacement for traceback.extract_stack() that only does the
|
||||||
|
necessary work for asyncio debug mode.
|
||||||
|
"""
|
||||||
|
if f is None:
|
||||||
|
f = sys._getframe().f_back
|
||||||
|
if limit is None:
|
||||||
|
# Limit the amount of work to a reasonable amount, as extract_stack()
|
||||||
|
# can be called for each coroutine and future in debug mode.
|
||||||
|
limit = constants.DEBUG_STACK_DEPTH
|
||||||
|
stack = traceback.StackSummary.extract(traceback.walk_stack(f),
|
||||||
|
limit=limit,
|
||||||
|
lookup_lines=False)
|
||||||
|
stack.reverse()
|
||||||
|
return stack
|
|
@ -11,6 +11,7 @@ import sys
|
||||||
|
|
||||||
from . import base_futures
|
from . import base_futures
|
||||||
from . import events
|
from . import events
|
||||||
|
from . import format_helpers
|
||||||
|
|
||||||
|
|
||||||
CancelledError = base_futures.CancelledError
|
CancelledError = base_futures.CancelledError
|
||||||
|
@ -79,7 +80,8 @@ class Future:
|
||||||
self._loop = loop
|
self._loop = loop
|
||||||
self._callbacks = []
|
self._callbacks = []
|
||||||
if self._loop.get_debug():
|
if self._loop.get_debug():
|
||||||
self._source_traceback = events.extract_stack(sys._getframe(1))
|
self._source_traceback = format_helpers.extract_stack(
|
||||||
|
sys._getframe(1))
|
||||||
|
|
||||||
_repr_info = base_futures._future_repr_info
|
_repr_info = base_futures._future_repr_info
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Support for tasks, coroutines and the scheduler."""
|
"""Support for tasks, coroutines and the scheduler."""
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Task',
|
'Task', 'create_task',
|
||||||
'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED',
|
'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED',
|
||||||
'wait', 'wait_for', 'as_completed', 'sleep',
|
'wait', 'wait_for', 'as_completed', 'sleep',
|
||||||
'gather', 'shield', 'ensure_future', 'run_coroutine_threadsafe',
|
'gather', 'shield', 'ensure_future', 'run_coroutine_threadsafe',
|
||||||
|
@ -67,13 +67,19 @@ class Task(futures.Future):
|
||||||
return {t for t in cls._all_tasks if t._loop is loop}
|
return {t for t in cls._all_tasks if t._loop is loop}
|
||||||
|
|
||||||
def __init__(self, coro, *, loop=None):
|
def __init__(self, coro, *, loop=None):
|
||||||
assert coroutines.iscoroutine(coro), repr(coro)
|
|
||||||
super().__init__(loop=loop)
|
super().__init__(loop=loop)
|
||||||
if self._source_traceback:
|
if self._source_traceback:
|
||||||
del self._source_traceback[-1]
|
del self._source_traceback[-1]
|
||||||
self._coro = coro
|
if not coroutines.iscoroutine(coro):
|
||||||
self._fut_waiter = None
|
# raise after Future.__init__(), attrs are required for __del__
|
||||||
|
# prevent logging for pending task in __del__
|
||||||
|
self._log_destroy_pending = False
|
||||||
|
raise TypeError(f"a coroutine was expected, got {coro!r}")
|
||||||
|
|
||||||
self._must_cancel = False
|
self._must_cancel = False
|
||||||
|
self._fut_waiter = None
|
||||||
|
self._coro = coro
|
||||||
|
|
||||||
self._loop.call_soon(self._step)
|
self._loop.call_soon(self._step)
|
||||||
self.__class__._all_tasks.add(self)
|
self.__class__._all_tasks.add(self)
|
||||||
|
|
||||||
|
@ -263,6 +269,15 @@ else:
|
||||||
Task = _CTask = _asyncio.Task
|
Task = _CTask = _asyncio.Task
|
||||||
|
|
||||||
|
|
||||||
|
def create_task(coro):
|
||||||
|
"""Schedule the execution of a coroutine object in a spawn task.
|
||||||
|
|
||||||
|
Return a Task object.
|
||||||
|
"""
|
||||||
|
loop = events.get_running_loop()
|
||||||
|
return loop.create_task(coro)
|
||||||
|
|
||||||
|
|
||||||
# wait() and as_completed() similar to those in PEP 3148.
|
# wait() and as_completed() similar to those in PEP 3148.
|
||||||
|
|
||||||
FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED
|
FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED
|
||||||
|
|
|
@ -2054,6 +2054,43 @@ class BaseTaskTests:
|
||||||
|
|
||||||
self.assertEqual(self.Task.all_tasks(self.loop), set())
|
self.assertEqual(self.Task.all_tasks(self.loop), set())
|
||||||
|
|
||||||
|
def test_create_task_with_noncoroutine(self):
|
||||||
|
with self.assertRaisesRegex(TypeError,
|
||||||
|
"a coroutine was expected, got 123"):
|
||||||
|
self.new_task(self.loop, 123)
|
||||||
|
|
||||||
|
def test_create_task_with_oldstyle_coroutine(self):
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def coro():
|
||||||
|
pass
|
||||||
|
|
||||||
|
task = self.new_task(self.loop, coro())
|
||||||
|
self.assertIsInstance(task, self.Task)
|
||||||
|
self.loop.run_until_complete(task)
|
||||||
|
|
||||||
|
def test_create_task_with_async_function(self):
|
||||||
|
|
||||||
|
async def coro():
|
||||||
|
pass
|
||||||
|
|
||||||
|
task = self.new_task(self.loop, coro())
|
||||||
|
self.assertIsInstance(task, self.Task)
|
||||||
|
self.loop.run_until_complete(task)
|
||||||
|
|
||||||
|
def test_bare_create_task(self):
|
||||||
|
|
||||||
|
async def inner():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
async def coro():
|
||||||
|
task = asyncio.create_task(inner())
|
||||||
|
self.assertIsInstance(task, self.Task)
|
||||||
|
ret = await task
|
||||||
|
self.assertEqual(1, ret)
|
||||||
|
|
||||||
|
self.loop.run_until_complete(coro())
|
||||||
|
|
||||||
|
|
||||||
def add_subclass_tests(cls):
|
def add_subclass_tests(cls):
|
||||||
BaseTask = cls.Task
|
BaseTask = cls.Task
|
||||||
|
|
|
@ -28,6 +28,7 @@ except ImportError: # pragma: no cover
|
||||||
|
|
||||||
from asyncio import base_events
|
from asyncio import base_events
|
||||||
from asyncio import events
|
from asyncio import events
|
||||||
|
from asyncio import format_helpers
|
||||||
from asyncio import futures
|
from asyncio import futures
|
||||||
from asyncio import tasks
|
from asyncio import tasks
|
||||||
from asyncio.log import logger
|
from asyncio.log import logger
|
||||||
|
@ -429,7 +430,7 @@ class MockPattern(str):
|
||||||
|
|
||||||
|
|
||||||
def get_function_source(func):
|
def get_function_source(func):
|
||||||
source = events._get_function_source(func)
|
source = format_helpers._get_function_source(func)
|
||||||
if source is None:
|
if source is None:
|
||||||
raise ValueError("unable to get the source of %r" % (func,))
|
raise ValueError("unable to get the source of %r" % (func,))
|
||||||
return source
|
return source
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Implement asyncio.create_task(coro) shortcut
|
|
@ -26,6 +26,7 @@ static PyObject *all_tasks;
|
||||||
static PyObject *current_tasks;
|
static PyObject *current_tasks;
|
||||||
static PyObject *traceback_extract_stack;
|
static PyObject *traceback_extract_stack;
|
||||||
static PyObject *asyncio_get_event_loop_policy;
|
static PyObject *asyncio_get_event_loop_policy;
|
||||||
|
static PyObject *asyncio_iscoroutine_func;
|
||||||
static PyObject *asyncio_future_repr_info_func;
|
static PyObject *asyncio_future_repr_info_func;
|
||||||
static PyObject *asyncio_task_repr_info_func;
|
static PyObject *asyncio_task_repr_info_func;
|
||||||
static PyObject *asyncio_task_get_stack_func;
|
static PyObject *asyncio_task_get_stack_func;
|
||||||
|
@ -1461,16 +1462,38 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
|
||||||
/*[clinic end generated code: output=9f24774c2287fc2f input=8d132974b049593e]*/
|
/*[clinic end generated code: output=9f24774c2287fc2f input=8d132974b049593e]*/
|
||||||
{
|
{
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
|
int tmp;
|
||||||
_Py_IDENTIFIER(add);
|
_Py_IDENTIFIER(add);
|
||||||
|
|
||||||
if (future_init((FutureObj*)self, loop)) {
|
if (future_init((FutureObj*)self, loop)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!PyCoro_CheckExact(coro)) {
|
||||||
|
// fastpath failed, perfom slow check
|
||||||
|
// raise after Future.__init__(), attrs are required for __del__
|
||||||
|
res = PyObject_CallFunctionObjArgs(asyncio_iscoroutine_func,
|
||||||
|
coro, NULL);
|
||||||
|
if (res == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
tmp = PyObject_Not(res);
|
||||||
|
Py_DECREF(res);
|
||||||
|
if (tmp < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tmp) {
|
||||||
|
self->task_log_destroy_pending = 0;
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"a coroutine was expected, got %R",
|
||||||
|
coro, NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self->task_fut_waiter = NULL;
|
self->task_fut_waiter = NULL;
|
||||||
self->task_must_cancel = 0;
|
self->task_must_cancel = 0;
|
||||||
self->task_log_destroy_pending = 1;
|
self->task_log_destroy_pending = 1;
|
||||||
|
|
||||||
Py_INCREF(coro);
|
Py_INCREF(coro);
|
||||||
self->task_coro = coro;
|
self->task_coro = coro;
|
||||||
|
|
||||||
|
@ -2604,6 +2627,7 @@ module_free(void *m)
|
||||||
Py_CLEAR(traceback_extract_stack);
|
Py_CLEAR(traceback_extract_stack);
|
||||||
Py_CLEAR(asyncio_get_event_loop_policy);
|
Py_CLEAR(asyncio_get_event_loop_policy);
|
||||||
Py_CLEAR(asyncio_future_repr_info_func);
|
Py_CLEAR(asyncio_future_repr_info_func);
|
||||||
|
Py_CLEAR(asyncio_iscoroutine_func);
|
||||||
Py_CLEAR(asyncio_task_repr_info_func);
|
Py_CLEAR(asyncio_task_repr_info_func);
|
||||||
Py_CLEAR(asyncio_task_get_stack_func);
|
Py_CLEAR(asyncio_task_get_stack_func);
|
||||||
Py_CLEAR(asyncio_task_print_stack_func);
|
Py_CLEAR(asyncio_task_print_stack_func);
|
||||||
|
@ -2645,6 +2669,9 @@ module_init(void)
|
||||||
GET_MOD_ATTR(asyncio_task_get_stack_func, "_task_get_stack")
|
GET_MOD_ATTR(asyncio_task_get_stack_func, "_task_get_stack")
|
||||||
GET_MOD_ATTR(asyncio_task_print_stack_func, "_task_print_stack")
|
GET_MOD_ATTR(asyncio_task_print_stack_func, "_task_print_stack")
|
||||||
|
|
||||||
|
WITH_MOD("asyncio.coroutines")
|
||||||
|
GET_MOD_ATTR(asyncio_iscoroutine_func, "iscoroutine")
|
||||||
|
|
||||||
WITH_MOD("inspect")
|
WITH_MOD("inspect")
|
||||||
GET_MOD_ATTR(inspect_isgenerator, "isgenerator")
|
GET_MOD_ATTR(inspect_isgenerator, "isgenerator")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue