summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/asyncio/base_events.py2
-rw-r--r--Lib/asyncio/base_futures.py70
-rw-r--r--Lib/asyncio/base_tasks.py76
-rw-r--r--Lib/asyncio/coroutines.py4
-rw-r--r--Lib/asyncio/futures.py89
-rw-r--r--Lib/asyncio/tasks.py80
-rw-r--r--Lib/test/test_asyncio/test_tasks.py439
-rw-r--r--Modules/_asynciomodule.c1976
-rw-r--r--Modules/clinic/_asynciomodule.c.h520
9 files changed, 2707 insertions, 549 deletions
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 5880061..cc9994d 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -57,7 +57,7 @@ _FATAL_ERROR_IGNORE = (BrokenPipeError,
def _format_handle(handle):
cb = handle._callback
- if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task):
+ if isinstance(getattr(cb, '__self__', None), tasks.Task):
# format the task
return repr(cb.__self__)
else:
diff --git a/Lib/asyncio/base_futures.py b/Lib/asyncio/base_futures.py
new file mode 100644
index 0000000..64f7845
--- /dev/null
+++ b/Lib/asyncio/base_futures.py
@@ -0,0 +1,70 @@
+__all__ = []
+
+import concurrent.futures._base
+import reprlib
+
+from . import events
+
+Error = concurrent.futures._base.Error
+CancelledError = concurrent.futures.CancelledError
+TimeoutError = concurrent.futures.TimeoutError
+
+
+class InvalidStateError(Error):
+ """The operation is not allowed in this state."""
+
+
+# States for Future.
+_PENDING = 'PENDING'
+_CANCELLED = 'CANCELLED'
+_FINISHED = 'FINISHED'
+
+
+def isfuture(obj):
+ """Check for a Future.
+
+ This returns True when obj is a Future instance or is advertising
+ itself as duck-type compatible by setting _asyncio_future_blocking.
+ See comment in Future for more details.
+ """
+ return getattr(obj, '_asyncio_future_blocking', None) is not None
+
+
+def _format_callbacks(cb):
+ """helper function for Future.__repr__"""
+ size = len(cb)
+ if not size:
+ cb = ''
+
+ def format_cb(callback):
+ return events._format_callback_source(callback, ())
+
+ if size == 1:
+ cb = format_cb(cb[0])
+ elif size == 2:
+ cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
+ elif size > 2:
+ cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
+ size - 2,
+ format_cb(cb[-1]))
+ return 'cb=[%s]' % cb
+
+
+def _future_repr_info(future):
+ # (Future) -> str
+ """helper function for Future.__repr__"""
+ info = [future._state.lower()]
+ if future._state == _FINISHED:
+ if future._exception is not None:
+ info.append('exception={!r}'.format(future._exception))
+ else:
+ # use reprlib to limit the length of the output, especially
+ # for very long strings
+ result = reprlib.repr(future._result)
+ info.append('result={}'.format(result))
+ if future._callbacks:
+ info.append(_format_callbacks(future._callbacks))
+ if future._source_traceback:
+ frame = future._source_traceback[-1]
+ info.append('created at %s:%s' % (frame[0], frame[1]))
+ return info
diff --git a/Lib/asyncio/base_tasks.py b/Lib/asyncio/base_tasks.py
new file mode 100644
index 0000000..5f34434
--- /dev/null
+++ b/Lib/asyncio/base_tasks.py
@@ -0,0 +1,76 @@
+import linecache
+import traceback
+
+from . import base_futures
+from . import coroutines
+
+
+def _task_repr_info(task):
+ info = base_futures._future_repr_info(task)
+
+ if task._must_cancel:
+ # replace status
+ info[0] = 'cancelling'
+
+ coro = coroutines._format_coroutine(task._coro)
+ info.insert(1, 'coro=<%s>' % coro)
+
+ if task._fut_waiter is not None:
+ info.insert(2, 'wait_for=%r' % task._fut_waiter)
+ return info
+
+
+def _task_get_stack(task, limit):
+ frames = []
+ try:
+ # 'async def' coroutines
+ f = task._coro.cr_frame
+ except AttributeError:
+ f = task._coro.gi_frame
+ if f is not None:
+ while f is not None:
+ if limit is not None:
+ if limit <= 0:
+ break
+ limit -= 1
+ frames.append(f)
+ f = f.f_back
+ frames.reverse()
+ elif task._exception is not None:
+ tb = task._exception.__traceback__
+ while tb is not None:
+ if limit is not None:
+ if limit <= 0:
+ break
+ limit -= 1
+ frames.append(tb.tb_frame)
+ tb = tb.tb_next
+ return frames
+
+
+def _task_print_stack(task, limit, file):
+ extracted_list = []
+ checked = set()
+ for f in task.get_stack(limit=limit):
+ lineno = f.f_lineno
+ co = f.f_code
+ filename = co.co_filename
+ name = co.co_name
+ if filename not in checked:
+ checked.add(filename)
+ linecache.checkcache(filename)
+ line = linecache.getline(filename, lineno, f.f_globals)
+ extracted_list.append((filename, lineno, name, line))
+ exc = task._exception
+ if not extracted_list:
+ print('No stack for %r' % task, file=file)
+ elif exc is not None:
+ print('Traceback for %r (most recent call last):' % task,
+ file=file)
+ else:
+ print('Stack for %r (most recent call last):' % task,
+ file=file)
+ traceback.print_list(extracted_list, file=file)
+ if exc is not None:
+ for line in traceback.format_exception_only(exc.__class__, exc):
+ print(line, file=file, end='')
diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py
index 1db7030..167c1e4 100644
--- a/Lib/asyncio/coroutines.py
+++ b/Lib/asyncio/coroutines.py
@@ -11,7 +11,7 @@ import types
from . import compat
from . import events
-from . import futures
+from . import base_futures
from .log import logger
@@ -204,7 +204,7 @@ def coroutine(func):
@functools.wraps(func)
def coro(*args, **kw):
res = func(*args, **kw)
- if (futures.isfuture(res) or inspect.isgenerator(res) or
+ if (base_futures.isfuture(res) or inspect.isgenerator(res) or
isinstance(res, CoroWrapper)):
res = yield from res
elif _AwaitableABC is not None:
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index b571130..d11d289 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -1,33 +1,30 @@
"""A Future class similar to the one in PEP 3148."""
-__all__ = ['CancelledError', 'TimeoutError',
- 'InvalidStateError',
- 'Future', 'wrap_future', 'isfuture'
- ]
+__all__ = ['CancelledError', 'TimeoutError', 'InvalidStateError',
+ 'Future', 'wrap_future', 'isfuture']
-import concurrent.futures._base
+import concurrent.futures
import logging
-import reprlib
import sys
import traceback
+from . import base_futures
from . import compat
from . import events
-# States for Future.
-_PENDING = 'PENDING'
-_CANCELLED = 'CANCELLED'
-_FINISHED = 'FINISHED'
-Error = concurrent.futures._base.Error
-CancelledError = concurrent.futures.CancelledError
-TimeoutError = concurrent.futures.TimeoutError
+CancelledError = base_futures.CancelledError
+InvalidStateError = base_futures.InvalidStateError
+TimeoutError = base_futures.TimeoutError
+isfuture = base_futures.isfuture
-STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
+
+_PENDING = base_futures._PENDING
+_CANCELLED = base_futures._CANCELLED
+_FINISHED = base_futures._FINISHED
-class InvalidStateError(Error):
- """The operation is not allowed in this state."""
+STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
class _TracebackLogger:
@@ -110,56 +107,6 @@ class _TracebackLogger:
self.loop.call_exception_handler({'message': msg})
-def isfuture(obj):
- """Check for a Future.
-
- This returns True when obj is a Future instance or is advertising
- itself as duck-type compatible by setting _asyncio_future_blocking.
- See comment in Future for more details.
- """
- return getattr(obj, '_asyncio_future_blocking', None) is not None
-
-
-def _format_callbacks(cb):
- """helper function for Future.__repr__"""
- size = len(cb)
- if not size:
- cb = ''
-
- def format_cb(callback):
- return events._format_callback_source(callback, ())
-
- if size == 1:
- cb = format_cb(cb[0])
- elif size == 2:
- cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
- elif size > 2:
- cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
- size-2,
- format_cb(cb[-1]))
- return 'cb=[%s]' % cb
-
-
-def _future_repr_info(future):
- # (Future) -> str
- """helper function for Future.__repr__"""
- info = [future._state.lower()]
- if future._state == _FINISHED:
- if future._exception is not None:
- info.append('exception={!r}'.format(future._exception))
- else:
- # use reprlib to limit the length of the output, especially
- # for very long strings
- result = reprlib.repr(future._result)
- info.append('result={}'.format(result))
- if future._callbacks:
- info.append(_format_callbacks(future._callbacks))
- if future._source_traceback:
- frame = future._source_traceback[-1]
- info.append('created at %s:%s' % (frame[0], frame[1]))
- return info
-
-
class Future:
"""This class is *almost* compatible with concurrent.futures.Future.
@@ -212,7 +159,7 @@ class Future:
if self._loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1))
- _repr_info = _future_repr_info
+ _repr_info = base_futures._future_repr_info
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
@@ -247,10 +194,10 @@ class Future:
if self._state != _PENDING:
return False
self._state = _CANCELLED
- self.__schedule_callbacks()
+ self._schedule_callbacks()
return True
- def __schedule_callbacks(self):
+ def _schedule_callbacks(self):
"""Internal: Ask the event loop to call all callbacks.
The callbacks are scheduled to be called as soon as possible. Also
@@ -352,7 +299,7 @@ class Future:
raise InvalidStateError('{}: {!r}'.format(self._state, self))
self._result = result
self._state = _FINISHED
- self.__schedule_callbacks()
+ self._schedule_callbacks()
def set_exception(self, exception):
"""Mark the future done and set an exception.
@@ -369,7 +316,7 @@ class Future:
"and cannot be raised into a Future")
self._exception = exception
self._state = _FINISHED
- self.__schedule_callbacks()
+ self._schedule_callbacks()
if compat.PY34:
self._log_traceback = True
else:
diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index 8852aa5..5a43ef2 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -9,11 +9,10 @@ __all__ = ['Task',
import concurrent.futures
import functools
import inspect
-import linecache
-import traceback
import warnings
import weakref
+from . import base_tasks
from . import compat
from . import coroutines
from . import events
@@ -93,18 +92,7 @@ class Task(futures.Future):
futures.Future.__del__(self)
def _repr_info(self):
- info = super()._repr_info()
-
- if self._must_cancel:
- # replace status
- info[0] = 'cancelling'
-
- coro = coroutines._format_coroutine(self._coro)
- info.insert(1, 'coro=<%s>' % coro)
-
- if self._fut_waiter is not None:
- info.insert(2, 'wait_for=%r' % self._fut_waiter)
- return info
+ return base_tasks._task_repr_info(self)
def get_stack(self, *, limit=None):
"""Return the list of stack frames for this task's coroutine.
@@ -127,31 +115,7 @@ class Task(futures.Future):
For reasons beyond our control, only one stack frame is
returned for a suspended coroutine.
"""
- frames = []
- try:
- # 'async def' coroutines
- f = self._coro.cr_frame
- except AttributeError:
- f = self._coro.gi_frame
- if f is not None:
- while f is not None:
- if limit is not None:
- if limit <= 0:
- break
- limit -= 1
- frames.append(f)
- f = f.f_back
- frames.reverse()
- elif self._exception is not None:
- tb = self._exception.__traceback__
- while tb is not None:
- if limit is not None:
- if limit <= 0:
- break
- limit -= 1
- frames.append(tb.tb_frame)
- tb = tb.tb_next
- return frames
+ return base_tasks._task_get_stack(self, limit)
def print_stack(self, *, limit=None, file=None):
"""Print the stack or traceback for this task's coroutine.
@@ -162,31 +126,7 @@ class Task(futures.Future):
to which the output is written; by default output is written
to sys.stderr.
"""
- extracted_list = []
- checked = set()
- for f in self.get_stack(limit=limit):
- lineno = f.f_lineno
- co = f.f_code
- filename = co.co_filename
- name = co.co_name
- if filename not in checked:
- checked.add(filename)
- linecache.checkcache(filename)
- line = linecache.getline(filename, lineno, f.f_globals)
- extracted_list.append((filename, lineno, name, line))
- exc = self._exception
- if not extracted_list:
- print('No stack for %r' % self, file=file)
- elif exc is not None:
- print('Traceback for %r (most recent call last):' % self,
- file=file)
- else:
- print('Stack for %r (most recent call last):' % self,
- file=file)
- traceback.print_list(extracted_list, file=file)
- if exc is not None:
- for line in traceback.format_exception_only(exc.__class__, exc):
- print(line, file=file, end='')
+ return base_tasks._task_print_stack(self, limit, file)
def cancel(self):
"""Request that this task cancel itself.
@@ -316,6 +256,18 @@ class Task(futures.Future):
self = None # Needed to break cycles when an exception occurs.
+_PyTask = Task
+
+
+try:
+ import _asyncio
+except ImportError:
+ pass
+else:
+ # _CTask is needed for tests.
+ Task = _CTask = _asyncio.Task
+
+
# wait() and as_completed() similar to those in PEP 3148.
FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index 1ceb9b2..d8862fc 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -1,5 +1,6 @@
"""Tests for tasks.py."""
+import collections
import contextlib
import functools
import io
@@ -14,6 +15,8 @@ from unittest import mock
import asyncio
from asyncio import coroutines
+from asyncio import futures
+from asyncio import tasks
from asyncio import test_utils
try:
from test import support
@@ -72,14 +75,25 @@ class Dummy:
pass
-class TaskTests(test_utils.TestCase):
+class BaseTaskTests:
+
+ Task = None
+ Future = None
+
+ def new_task(self, loop, coro):
+ return self.__class__.Task(coro, loop=loop)
+
+ def new_future(self, loop):
+ return self.__class__.Future(loop=loop)
def setUp(self):
self.loop = self.new_test_loop()
+ self.loop.set_task_factory(self.new_task)
+ self.loop.create_future = lambda: self.new_future(self.loop)
def test_other_loop_future(self):
other_loop = asyncio.new_event_loop()
- fut = asyncio.Future(loop=other_loop)
+ fut = self.new_future(other_loop)
@asyncio.coroutine
def run(fut):
@@ -107,7 +121,7 @@ class TaskTests(test_utils.TestCase):
@asyncio.coroutine
def notmuch():
return 'ok'
- t = asyncio.Task(notmuch(), loop=self.loop)
+ t = self.new_task(self.loop, notmuch())
self.loop.run_until_complete(t)
self.assertTrue(t.done())
self.assertEqual(t.result(), 'ok')
@@ -115,7 +129,7 @@ class TaskTests(test_utils.TestCase):
loop = asyncio.new_event_loop()
self.set_event_loop(loop)
- t = asyncio.Task(notmuch(), loop=loop)
+ t = self.new_task(loop, notmuch())
self.assertIs(t._loop, loop)
loop.run_until_complete(t)
loop.close()
@@ -138,7 +152,7 @@ class TaskTests(test_utils.TestCase):
loop.close()
def test_ensure_future_future(self):
- f_orig = asyncio.Future(loop=self.loop)
+ f_orig = self.new_future(self.loop)
f_orig.set_result('ko')
f = asyncio.ensure_future(f_orig)
@@ -162,7 +176,7 @@ class TaskTests(test_utils.TestCase):
@asyncio.coroutine
def notmuch():
return 'ok'
- t_orig = asyncio.Task(notmuch(), loop=self.loop)
+ t_orig = self.new_task(self.loop, notmuch())
t = asyncio.ensure_future(t_orig)
self.loop.run_until_complete(t)
self.assertTrue(t.done())
@@ -203,7 +217,7 @@ class TaskTests(test_utils.TestCase):
asyncio.ensure_future('ok')
def test_async_warning(self):
- f = asyncio.Future(loop=self.loop)
+ f = self.new_future(self.loop)
with self.assertWarnsRegex(DeprecationWarning,
'function is deprecated, use ensure_'):
self.assertIs(f, asyncio.async(f))
@@ -250,8 +264,8 @@ class TaskTests(test_utils.TestCase):
# test coroutine function
self.assertEqual(notmuch.__name__, 'notmuch')
if PY35:
- self.assertEqual(notmuch.__qualname__,
- 'TaskTests.test_task_repr.<locals>.notmuch')
+ self.assertRegex(notmuch.__qualname__,
+ r'\w+.test_task_repr.<locals>.notmuch')
self.assertEqual(notmuch.__module__, __name__)
filename, lineno = test_utils.get_function_source(notmuch)
@@ -260,7 +274,7 @@ class TaskTests(test_utils.TestCase):
# test coroutine object
gen = notmuch()
if coroutines._DEBUG or PY35:
- coro_qualname = 'TaskTests.test_task_repr.<locals>.notmuch'
+ coro_qualname = 'BaseTaskTests.test_task_repr.<locals>.notmuch'
else:
coro_qualname = 'notmuch'
self.assertEqual(gen.__name__, 'notmuch')
@@ -269,7 +283,7 @@ class TaskTests(test_utils.TestCase):
coro_qualname)
# test pending Task
- t = asyncio.Task(gen, loop=self.loop)
+ t = self.new_task(self.loop, gen)
t.add_done_callback(Dummy())
coro = format_coroutine(coro_qualname, 'running', src,
@@ -291,7 +305,7 @@ class TaskTests(test_utils.TestCase):
'<Task cancelled %s>' % coro)
# test finished Task
- t = asyncio.Task(notmuch(), loop=self.loop)
+ t = self.new_task(self.loop, notmuch())
self.loop.run_until_complete(t)
coro = format_coroutine(coro_qualname, 'done', src,
t._source_traceback)
@@ -310,9 +324,9 @@ class TaskTests(test_utils.TestCase):
# test coroutine function
self.assertEqual(notmuch.__name__, 'notmuch')
if PY35:
- self.assertEqual(notmuch.__qualname__,
- 'TaskTests.test_task_repr_coro_decorator'
- '.<locals>.notmuch')
+ self.assertRegex(notmuch.__qualname__,
+ r'\w+.test_task_repr_coro_decorator'
+ r'\.<locals>\.notmuch')
self.assertEqual(notmuch.__module__, __name__)
# test coroutine object
@@ -322,7 +336,7 @@ class TaskTests(test_utils.TestCase):
# function, as expected, and have a qualified name (__qualname__
# attribute).
coro_name = 'notmuch'
- coro_qualname = ('TaskTests.test_task_repr_coro_decorator'
+ coro_qualname = ('BaseTaskTests.test_task_repr_coro_decorator'
'.<locals>.notmuch')
else:
# On Python < 3.5, generators inherit the name of the code, not of
@@ -350,7 +364,7 @@ class TaskTests(test_utils.TestCase):
self.assertEqual(repr(gen), '<CoroWrapper %s>' % coro)
# test pending Task
- t = asyncio.Task(gen, loop=self.loop)
+ t = self.new_task(self.loop, gen)
t.add_done_callback(Dummy())
# format the coroutine object
@@ -373,8 +387,8 @@ class TaskTests(test_utils.TestCase):
def wait_for(fut):
return (yield from fut)
- fut = asyncio.Future(loop=self.loop)
- task = asyncio.Task(wait_for(fut), loop=self.loop)
+ fut = self.new_future(self.loop)
+ task = self.new_task(self.loop, wait_for(fut))
test_utils.run_briefly(self.loop)
self.assertRegex(repr(task),
'<Task .* wait_for=%s>' % re.escape(repr(fut)))
@@ -400,10 +414,11 @@ class TaskTests(test_utils.TestCase):
self.addCleanup(task._coro.close)
coro_repr = repr(task._coro)
- expected = ('<CoroWrapper TaskTests.test_task_repr_partial_corowrapper'
- '.<locals>.func(1)() running, ')
- self.assertTrue(coro_repr.startswith(expected),
- coro_repr)
+ expected = (
+ r'<CoroWrapper \w+.test_task_repr_partial_corowrapper'
+ r'\.<locals>\.func\(1\)\(\) running, '
+ )
+ self.assertRegex(coro_repr, expected)
def test_task_basics(self):
@asyncio.coroutine
@@ -437,7 +452,7 @@ class TaskTests(test_utils.TestCase):
yield from asyncio.sleep(10.0, loop=loop)
return 12
- t = asyncio.Task(task(), loop=loop)
+ t = self.new_task(loop, task())
loop.call_soon(t.cancel)
with self.assertRaises(asyncio.CancelledError):
loop.run_until_complete(t)
@@ -452,7 +467,7 @@ class TaskTests(test_utils.TestCase):
yield
return 12
- t = asyncio.Task(task(), loop=self.loop)
+ t = self.new_task(self.loop, task())
test_utils.run_briefly(self.loop) # start coro
t.cancel()
self.assertRaises(
@@ -462,14 +477,14 @@ class TaskTests(test_utils.TestCase):
self.assertFalse(t.cancel())
def test_cancel_inner_future(self):
- f = asyncio.Future(loop=self.loop)
+ f = self.new_future(self.loop)
@asyncio.coroutine
def task():
yield from f
return 12
- t = asyncio.Task(task(), loop=self.loop)
+ t = self.new_task(self.loop, task())
test_utils.run_briefly(self.loop) # start task
f.cancel()
with self.assertRaises(asyncio.CancelledError):
@@ -478,14 +493,14 @@ class TaskTests(test_utils.TestCase):
self.assertTrue(t.cancelled())
def test_cancel_both_task_and_inner_future(self):
- f = asyncio.Future(loop=self.loop)
+ f = self.new_future(self.loop)
@asyncio.coroutine
def task():
yield from f
return 12
- t = asyncio.Task(task(), loop=self.loop)
+ t = self.new_task(self.loop, task())
test_utils.run_briefly(self.loop)
f.cancel()
@@ -499,8 +514,8 @@ class TaskTests(test_utils.TestCase):
self.assertTrue(t.cancelled())
def test_cancel_task_catching(self):
- fut1 = asyncio.Future(loop=self.loop)
- fut2 = asyncio.Future(loop=self.loop)
+ fut1 = self.new_future(self.loop)
+ fut2 = self.new_future(self.loop)
@asyncio.coroutine
def task():
@@ -510,7 +525,7 @@ class TaskTests(test_utils.TestCase):
except asyncio.CancelledError:
return 42
- t = asyncio.Task(task(), loop=self.loop)
+ t = self.new_task(self.loop, task())
test_utils.run_briefly(self.loop)
self.assertIs(t._fut_waiter, fut1) # White-box test.
fut1.set_result(None)
@@ -523,9 +538,9 @@ class TaskTests(test_utils.TestCase):
self.assertFalse(t.cancelled())
def test_cancel_task_ignoring(self):
- fut1 = asyncio.Future(loop=self.loop)
- fut2 = asyncio.Future(loop=self.loop)
- fut3 = asyncio.Future(loop=self.loop)
+ fut1 = self.new_future(self.loop)
+ fut2 = self.new_future(self.loop)
+ fut3 = self.new_future(self.loop)
@asyncio.coroutine
def task():
@@ -537,7 +552,7 @@ class TaskTests(test_utils.TestCase):
res = yield from fut3
return res
- t = asyncio.Task(task(), loop=self.loop)
+ t = self.new_task(self.loop, task())
test_utils.run_briefly(self.loop)
self.assertIs(t._fut_waiter, fut1) # White-box test.
fut1.set_result(None)
@@ -565,7 +580,7 @@ class TaskTests(test_utils.TestCase):
yield from asyncio.sleep(100, loop=loop)
return 12
- t = asyncio.Task(task(), loop=loop)
+ t = self.new_task(loop, task())
self.assertRaises(
asyncio.CancelledError, loop.run_until_complete, t)
self.assertTrue(t.done())
@@ -598,7 +613,7 @@ class TaskTests(test_utils.TestCase):
if x == 2:
loop.stop()
- t = asyncio.Task(task(), loop=loop)
+ t = self.new_task(loop, task())
with self.assertRaises(RuntimeError) as cm:
loop.run_until_complete(t)
self.assertEqual(str(cm.exception),
@@ -636,7 +651,7 @@ class TaskTests(test_utils.TestCase):
foo_running = False
return 'done'
- fut = asyncio.Task(foo(), loop=loop)
+ fut = self.new_task(loop, foo())
with self.assertRaises(asyncio.TimeoutError):
loop.run_until_complete(asyncio.wait_for(fut, 0.1, loop=loop))
@@ -676,7 +691,7 @@ class TaskTests(test_utils.TestCase):
asyncio.set_event_loop(loop)
try:
- fut = asyncio.Task(foo(), loop=loop)
+ fut = self.new_task(loop, foo())
with self.assertRaises(asyncio.TimeoutError):
loop.run_until_complete(asyncio.wait_for(fut, 0.01))
finally:
@@ -695,7 +710,7 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- fut = asyncio.Future(loop=loop)
+ fut = self.new_future(loop)
task = asyncio.wait_for(fut, timeout=0.2, loop=loop)
loop.call_later(0.1, fut.set_result, "ok")
res = loop.run_until_complete(task)
@@ -712,8 +727,8 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop)
- b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop)
+ a = self.new_task(loop, asyncio.sleep(0.1, loop=loop))
+ b = self.new_task(loop, asyncio.sleep(0.15, loop=loop))
@asyncio.coroutine
def foo():
@@ -722,12 +737,12 @@ class TaskTests(test_utils.TestCase):
self.assertEqual(pending, set())
return 42
- res = loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ res = loop.run_until_complete(self.new_task(loop, foo()))
self.assertEqual(res, 42)
self.assertAlmostEqual(0.15, loop.time())
# Doing it again should take no time and exercise a different path.
- res = loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ res = loop.run_until_complete(self.new_task(loop, foo()))
self.assertAlmostEqual(0.15, loop.time())
self.assertEqual(res, 42)
@@ -742,8 +757,8 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- a = asyncio.Task(asyncio.sleep(0.01, loop=loop), loop=loop)
- b = asyncio.Task(asyncio.sleep(0.015, loop=loop), loop=loop)
+ a = self.new_task(loop, asyncio.sleep(0.01, loop=loop))
+ b = self.new_task(loop, asyncio.sleep(0.015, loop=loop))
@asyncio.coroutine
def foo():
@@ -754,7 +769,7 @@ class TaskTests(test_utils.TestCase):
asyncio.set_event_loop(loop)
res = loop.run_until_complete(
- asyncio.Task(foo(), loop=loop))
+ self.new_task(loop, foo()))
self.assertEqual(res, 42)
@@ -764,9 +779,9 @@ class TaskTests(test_utils.TestCase):
return s
c = coro('test')
- task = asyncio.Task(
- asyncio.wait([c, c, coro('spam')], loop=self.loop),
- loop=self.loop)
+ task =self.new_task(
+ self.loop,
+ asyncio.wait([c, c, coro('spam')], loop=self.loop))
done, pending = self.loop.run_until_complete(task)
@@ -797,12 +812,12 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop)
- b = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop)
- task = asyncio.Task(
+ a = self.new_task(loop, asyncio.sleep(10.0, loop=loop))
+ b = self.new_task(loop, asyncio.sleep(0.1, loop=loop))
+ task = self.new_task(
+ loop,
asyncio.wait([b, a], return_when=asyncio.FIRST_COMPLETED,
- loop=loop),
- loop=loop)
+ loop=loop))
done, pending = loop.run_until_complete(task)
self.assertEqual({b}, done)
@@ -829,12 +844,12 @@ class TaskTests(test_utils.TestCase):
yield
yield
- a = asyncio.Task(coro1(), loop=self.loop)
- b = asyncio.Task(coro2(), loop=self.loop)
- task = asyncio.Task(
+ a = self.new_task(self.loop, coro1())
+ b = self.new_task(self.loop, coro2())
+ task = self.new_task(
+ self.loop,
asyncio.wait([b, a], return_when=asyncio.FIRST_COMPLETED,
- loop=self.loop),
- loop=self.loop)
+ loop=self.loop))
done, pending = self.loop.run_until_complete(task)
self.assertEqual({a, b}, done)
@@ -853,17 +868,17 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
# first_exception, task already has exception
- a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop)
+ a = self.new_task(loop, asyncio.sleep(10.0, loop=loop))
@asyncio.coroutine
def exc():
raise ZeroDivisionError('err')
- b = asyncio.Task(exc(), loop=loop)
- task = asyncio.Task(
+ b = self.new_task(loop, exc())
+ task = self.new_task(
+ loop,
asyncio.wait([b, a], return_when=asyncio.FIRST_EXCEPTION,
- loop=loop),
- loop=loop)
+ loop=loop))
done, pending = loop.run_until_complete(task)
self.assertEqual({b}, done)
@@ -886,14 +901,14 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
# first_exception, exception during waiting
- a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop)
+ a = self.new_task(loop, asyncio.sleep(10.0, loop=loop))
@asyncio.coroutine
def exc():
yield from asyncio.sleep(0.01, loop=loop)
raise ZeroDivisionError('err')
- b = asyncio.Task(exc(), loop=loop)
+ b = self.new_task(loop, exc())
task = asyncio.wait([b, a], return_when=asyncio.FIRST_EXCEPTION,
loop=loop)
@@ -917,14 +932,14 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop)
+ a = self.new_task(loop, asyncio.sleep(0.1, loop=loop))
@asyncio.coroutine
def sleeper():
yield from asyncio.sleep(0.15, loop=loop)
raise ZeroDivisionError('really')
- b = asyncio.Task(sleeper(), loop=loop)
+ b = self.new_task(loop, sleeper())
@asyncio.coroutine
def foo():
@@ -934,10 +949,10 @@ class TaskTests(test_utils.TestCase):
errors = set(f for f in done if f.exception() is not None)
self.assertEqual(len(errors), 1)
- loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ loop.run_until_complete(self.new_task(loop, foo()))
self.assertAlmostEqual(0.15, loop.time())
- loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ loop.run_until_complete(self.new_task(loop, foo()))
self.assertAlmostEqual(0.15, loop.time())
def test_wait_with_timeout(self):
@@ -953,8 +968,8 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop)
- b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop)
+ a = self.new_task(loop, asyncio.sleep(0.1, loop=loop))
+ b = self.new_task(loop, asyncio.sleep(0.15, loop=loop))
@asyncio.coroutine
def foo():
@@ -963,7 +978,7 @@ class TaskTests(test_utils.TestCase):
self.assertEqual(done, set([a]))
self.assertEqual(pending, set([b]))
- loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ loop.run_until_complete(self.new_task(loop, foo()))
self.assertAlmostEqual(0.11, loop.time())
# move forward to close generator
@@ -983,8 +998,8 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop)
- b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop)
+ a = self.new_task(loop, asyncio.sleep(0.1, loop=loop))
+ b = self.new_task(loop, asyncio.sleep(0.15, loop=loop))
done, pending = loop.run_until_complete(
asyncio.wait([b, a], timeout=0.1, loop=loop))
@@ -1032,14 +1047,14 @@ class TaskTests(test_utils.TestCase):
values.append((yield from f))
return values
- res = loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ res = loop.run_until_complete(self.new_task(loop, foo()))
self.assertAlmostEqual(0.15, loop.time())
self.assertTrue('a' in res[:2])
self.assertTrue('b' in res[:2])
self.assertEqual(res[2], 'c')
# Doing it again should take no time and exercise a different path.
- res = loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ res = loop.run_until_complete(self.new_task(loop, foo()))
self.assertAlmostEqual(0.15, loop.time())
def test_as_completed_with_timeout(self):
@@ -1068,7 +1083,7 @@ class TaskTests(test_utils.TestCase):
values.append((2, exc))
return values
- res = loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ res = loop.run_until_complete(self.new_task(loop, foo()))
self.assertEqual(len(res), 2, res)
self.assertEqual(res[0], (1, 'a'))
self.assertEqual(res[1][0], 2)
@@ -1096,7 +1111,7 @@ class TaskTests(test_utils.TestCase):
v = yield from f
self.assertEqual(v, 'a')
- loop.run_until_complete(asyncio.Task(foo(), loop=loop))
+ loop.run_until_complete(self.new_task(loop, foo()))
def test_as_completed_reverse_wait(self):
@@ -1156,7 +1171,7 @@ class TaskTests(test_utils.TestCase):
result.append((yield from f))
return result
- fut = asyncio.Task(runner(), loop=self.loop)
+ fut = self.new_task(self.loop, runner())
self.loop.run_until_complete(fut)
result = fut.result()
self.assertEqual(set(result), {'ham', 'spam'})
@@ -1179,7 +1194,7 @@ class TaskTests(test_utils.TestCase):
res = yield from asyncio.sleep(dt/2, arg, loop=loop)
return res
- t = asyncio.Task(sleeper(0.1, 'yeah'), loop=loop)
+ t = self.new_task(loop, sleeper(0.1, 'yeah'))
loop.run_until_complete(t)
self.assertTrue(t.done())
self.assertEqual(t.result(), 'yeah')
@@ -1194,8 +1209,7 @@ class TaskTests(test_utils.TestCase):
loop = self.new_test_loop(gen)
- t = asyncio.Task(asyncio.sleep(10.0, 'yeah', loop=loop),
- loop=loop)
+ t = self.new_task(loop, asyncio.sleep(10.0, 'yeah', loop=loop))
handle = None
orig_call_later = loop.call_later
@@ -1231,7 +1245,7 @@ class TaskTests(test_utils.TestCase):
@asyncio.coroutine
def doit():
- sleeper = asyncio.Task(sleep(5000), loop=loop)
+ sleeper = self.new_task(loop, sleep(5000))
loop.call_later(0.1, sleeper.cancel)
try:
yield from sleeper
@@ -1245,13 +1259,13 @@ class TaskTests(test_utils.TestCase):
self.assertAlmostEqual(0.1, loop.time())
def test_task_cancel_waiter_future(self):
- fut = asyncio.Future(loop=self.loop)
+ fut = self.new_future(self.loop)
@asyncio.coroutine
def coro():
yield from fut
- task = asyncio.Task(coro(), loop=self.loop)
+ task = self.new_task(self.loop, coro())
test_utils.run_briefly(self.loop)
self.assertIs(task._fut_waiter, fut)
@@ -1268,7 +1282,7 @@ class TaskTests(test_utils.TestCase):
return 'ko'
gen = notmuch()
- task = asyncio.Task(gen, loop=self.loop)
+ task = self.new_task(self.loop, gen)
task.set_result('ok')
self.assertRaises(AssertionError, task._step)
@@ -1304,7 +1318,7 @@ class TaskTests(test_utils.TestCase):
nonlocal result
result = yield from fut
- t = asyncio.Task(wait_for_future(), loop=self.loop)
+ t = self.new_task(self.loop, wait_for_future())
test_utils.run_briefly(self.loop)
self.assertTrue(fut.cb_added)
@@ -1320,7 +1334,7 @@ class TaskTests(test_utils.TestCase):
def notmutch():
raise BaseException()
- task = asyncio.Task(notmutch(), loop=self.loop)
+ task = self.new_task(self.loop, notmutch())
self.assertRaises(BaseException, task._step)
self.assertTrue(task.done())
@@ -1348,7 +1362,7 @@ class TaskTests(test_utils.TestCase):
except asyncio.CancelledError:
raise base_exc
- task = asyncio.Task(notmutch(), loop=loop)
+ task = self.new_task(loop, notmutch())
test_utils.run_briefly(loop)
task.cancel()
@@ -1376,7 +1390,7 @@ class TaskTests(test_utils.TestCase):
self.assertTrue(asyncio.iscoroutinefunction(fn2))
def test_yield_vs_yield_from(self):
- fut = asyncio.Future(loop=self.loop)
+ fut = self.new_future(self.loop)
@asyncio.coroutine
def wait_for_future():
@@ -1420,7 +1434,7 @@ class TaskTests(test_utils.TestCase):
self.assertEqual(res, 'test')
def test_coroutine_non_gen_function_return_future(self):
- fut = asyncio.Future(loop=self.loop)
+ fut = self.new_future(self.loop)
@asyncio.coroutine
def func():
@@ -1430,49 +1444,53 @@ class TaskTests(test_utils.TestCase):
def coro():
fut.set_result('test')
- t1 = asyncio.Task(func(), loop=self.loop)
- t2 = asyncio.Task(coro(), loop=self.loop)
+ t1 = self.new_task(self.loop, func())
+ t2 = self.new_task(self.loop, coro())
res = self.loop.run_until_complete(t1)
self.assertEqual(res, 'test')
self.assertIsNone(t2.result())
def test_current_task(self):
- self.assertIsNone(asyncio.Task.current_task(loop=self.loop))
+ Task = self.__class__.Task
+
+ self.assertIsNone(Task.current_task(loop=self.loop))
@asyncio.coroutine
def coro(loop):
- self.assertTrue(asyncio.Task.current_task(loop=loop) is task)
+ self.assertTrue(Task.current_task(loop=loop) is task)
- task = asyncio.Task(coro(self.loop), loop=self.loop)
+ task = self.new_task(self.loop, coro(self.loop))
self.loop.run_until_complete(task)
- self.assertIsNone(asyncio.Task.current_task(loop=self.loop))
+ self.assertIsNone(Task.current_task(loop=self.loop))
def test_current_task_with_interleaving_tasks(self):
- self.assertIsNone(asyncio.Task.current_task(loop=self.loop))
+ Task = self.__class__.Task
+
+ self.assertIsNone(Task.current_task(loop=self.loop))
- fut1 = asyncio.Future(loop=self.loop)
- fut2 = asyncio.Future(loop=self.loop)
+ fut1 = self.new_future(self.loop)
+ fut2 = self.new_future(self.loop)
@asyncio.coroutine
def coro1(loop):
- self.assertTrue(asyncio.Task.current_task(loop=loop) is task1)
+ self.assertTrue(Task.current_task(loop=loop) is task1)
yield from fut1
- self.assertTrue(asyncio.Task.current_task(loop=loop) is task1)
+ self.assertTrue(Task.current_task(loop=loop) is task1)
fut2.set_result(True)
@asyncio.coroutine
def coro2(loop):
- self.assertTrue(asyncio.Task.current_task(loop=loop) is task2)
+ self.assertTrue(Task.current_task(loop=loop) is task2)
fut1.set_result(True)
yield from fut2
- self.assertTrue(asyncio.Task.current_task(loop=loop) is task2)
+ self.assertTrue(Task.current_task(loop=loop) is task2)
- task1 = asyncio.Task(coro1(self.loop), loop=self.loop)
- task2 = asyncio.Task(coro2(self.loop), loop=self.loop)
+ task1 = self.new_task(self.loop, coro1(self.loop))
+ task2 = self.new_task(self.loop, coro2(self.loop))
self.loop.run_until_complete(asyncio.wait((task1, task2),
loop=self.loop))
- self.assertIsNone(asyncio.Task.current_task(loop=self.loop))
+ self.assertIsNone(Task.current_task(loop=self.loop))
# Some thorough tests for cancellation propagation through
# coroutines, tasks and wait().
@@ -1480,7 +1498,7 @@ class TaskTests(test_utils.TestCase):
def test_yield_future_passes_cancel(self):
# Cancelling outer() cancels inner() cancels waiter.
proof = 0
- waiter = asyncio.Future(loop=self.loop)
+ waiter = self.new_future(self.loop)
@asyncio.coroutine
def inner():
@@ -1514,7 +1532,7 @@ class TaskTests(test_utils.TestCase):
# Cancelling outer() makes wait() return early, leaves inner()
# running.
proof = 0
- waiter = asyncio.Future(loop=self.loop)
+ waiter = self.new_future(self.loop)
@asyncio.coroutine
def inner():
@@ -1538,14 +1556,14 @@ class TaskTests(test_utils.TestCase):
self.assertEqual(proof, 1)
def test_shield_result(self):
- inner = asyncio.Future(loop=self.loop)
+ inner = self.new_future(self.loop)
outer = asyncio.shield(inner)
inner.set_result(42)
res = self.loop.run_until_complete(outer)
self.assertEqual(res, 42)
def test_shield_exception(self):
- inner = asyncio.Future(loop=self.loop)
+ inner = self.new_future(self.loop)
outer = asyncio.shield(inner)
test_utils.run_briefly(self.loop)
exc = RuntimeError('expected')
@@ -1554,7 +1572,7 @@ class TaskTests(test_utils.TestCase):
self.assertIs(outer.exception(), exc)
def test_shield_cancel(self):
- inner = asyncio.Future(loop=self.loop)
+ inner = self.new_future(self.loop)
outer = asyncio.shield(inner)
test_utils.run_briefly(self.loop)
inner.cancel()
@@ -1562,7 +1580,7 @@ class TaskTests(test_utils.TestCase):
self.assertTrue(outer.cancelled())
def test_shield_shortcut(self):
- fut = asyncio.Future(loop=self.loop)
+ fut = self.new_future(self.loop)
fut.set_result(42)
res = self.loop.run_until_complete(asyncio.shield(fut))
self.assertEqual(res, 42)
@@ -1570,7 +1588,7 @@ class TaskTests(test_utils.TestCase):
def test_shield_effect(self):
# Cancelling outer() does not affect inner().
proof = 0
- waiter = asyncio.Future(loop=self.loop)
+ waiter = self.new_future(self.loop)
@asyncio.coroutine
def inner():
@@ -1594,8 +1612,8 @@ class TaskTests(test_utils.TestCase):
self.assertEqual(proof, 1)
def test_shield_gather(self):
- child1 = asyncio.Future(loop=self.loop)
- child2 = asyncio.Future(loop=self.loop)
+ child1 = self.new_future(self.loop)
+ child2 = self.new_future(self.loop)
parent = asyncio.gather(child1, child2, loop=self.loop)
outer = asyncio.shield(parent, loop=self.loop)
test_utils.run_briefly(self.loop)
@@ -1608,8 +1626,8 @@ class TaskTests(test_utils.TestCase):
self.assertEqual(parent.result(), [1, 2])
def test_gather_shield(self):
- child1 = asyncio.Future(loop=self.loop)
- child2 = asyncio.Future(loop=self.loop)
+ child1 = self.new_future(self.loop)
+ child2 = self.new_future(self.loop)
inner1 = asyncio.shield(child1, loop=self.loop)
inner2 = asyncio.shield(child2, loop=self.loop)
parent = asyncio.gather(inner1, inner2, loop=self.loop)
@@ -1625,7 +1643,7 @@ class TaskTests(test_utils.TestCase):
test_utils.run_briefly(self.loop)
def test_as_completed_invalid_args(self):
- fut = asyncio.Future(loop=self.loop)
+ fut = self.new_future(self.loop)
# as_completed() expects a list of futures, not a future instance
self.assertRaises(TypeError, self.loop.run_until_complete,
@@ -1636,7 +1654,7 @@ class TaskTests(test_utils.TestCase):
coro.close()
def test_wait_invalid_args(self):
- fut = asyncio.Future(loop=self.loop)
+ fut = self.new_future(self.loop)
# wait() expects a list of futures, not a future instance
self.assertRaises(TypeError, self.loop.run_until_complete,
@@ -1663,7 +1681,7 @@ class TaskTests(test_utils.TestCase):
yield from fut
# A completed Future used to run the coroutine.
- fut = asyncio.Future(loop=self.loop)
+ fut = self.new_future(self.loop)
fut.set_result(None)
# Call the coroutine.
@@ -1697,15 +1715,15 @@ class TaskTests(test_utils.TestCase):
@asyncio.coroutine
def t2():
- f = asyncio.Future(loop=self.loop)
- asyncio.Task(t3(f), loop=self.loop)
+ f = self.new_future(self.loop)
+ self.new_task(self.loop, t3(f))
return (yield from f)
@asyncio.coroutine
def t3(f):
f.set_result((1, 2, 3))
- task = asyncio.Task(t1(), loop=self.loop)
+ task = self.new_task(self.loop, t1())
val = self.loop.run_until_complete(task)
self.assertEqual(val, (1, 2, 3))
@@ -1768,9 +1786,11 @@ class TaskTests(test_utils.TestCase):
@unittest.skipUnless(PY34,
'need python 3.4 or later')
def test_log_destroyed_pending_task(self):
+ Task = self.__class__.Task
+
@asyncio.coroutine
def kill_me(loop):
- future = asyncio.Future(loop=loop)
+ future = self.new_future(loop)
yield from future
# at this point, the only reference to kill_me() task is
# the Task._wakeup() method in future._callbacks
@@ -1783,7 +1803,7 @@ class TaskTests(test_utils.TestCase):
# schedule the task
coro = kill_me(self.loop)
task = asyncio.ensure_future(coro, loop=self.loop)
- self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), {task})
+ self.assertEqual(Task.all_tasks(loop=self.loop), {task})
# execute the task so it waits for future
self.loop._run_once()
@@ -1798,7 +1818,7 @@ class TaskTests(test_utils.TestCase):
# no more reference to kill_me() task: the task is destroyed by the GC
support.gc_collect()
- self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), set())
+ self.assertEqual(Task.all_tasks(loop=self.loop), set())
mock_handler.assert_called_with(self.loop, {
'message': 'Task was destroyed but it is pending!',
@@ -1863,10 +1883,10 @@ class TaskTests(test_utils.TestCase):
def test_task_source_traceback(self):
self.loop.set_debug(True)
- task = asyncio.Task(coroutine_function(), loop=self.loop)
+ task = self.new_task(self.loop, coroutine_function())
lineno = sys._getframe().f_lineno - 1
self.assertIsInstance(task._source_traceback, list)
- self.assertEqual(task._source_traceback[-1][:3],
+ self.assertEqual(task._source_traceback[-2][:3],
(__file__,
lineno,
'test_task_source_traceback'))
@@ -1878,7 +1898,7 @@ class TaskTests(test_utils.TestCase):
@asyncio.coroutine
def blocking_coroutine():
- fut = asyncio.Future(loop=loop)
+ fut = self.new_future(loop)
# Block: fut result is never set
yield from fut
@@ -1905,7 +1925,7 @@ class TaskTests(test_utils.TestCase):
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)
- fut = asyncio.Future(loop=loop)
+ fut = self.new_future(loop)
# The indirection fut->child_coro is needed since otherwise the
# gathering task is done at the same time as the child future
def child_coro():
@@ -1929,6 +1949,157 @@ class TaskTests(test_utils.TestCase):
self.assertFalse(gather_task.cancelled())
self.assertEqual(gather_task.result(), [42])
+ @mock.patch('asyncio.base_events.logger')
+ def test_error_in_call_soon(self, m_log):
+ def call_soon(callback, *args):
+ raise ValueError
+ self.loop.call_soon = call_soon
+
+ @asyncio.coroutine
+ def coro():
+ pass
+
+ self.assertFalse(m_log.error.called)
+
+ with self.assertRaises(ValueError):
+ self.new_task(self.loop, coro())
+
+ self.assertTrue(m_log.error.called)
+ message = m_log.error.call_args[0][0]
+ self.assertIn('Task was destroyed but it is pending', message)
+
+ self.assertEqual(self.Task.all_tasks(self.loop), set())
+
+
+def add_subclass_tests(cls):
+ BaseTask = cls.Task
+ BaseFuture = cls.Future
+
+ if BaseTask is None or BaseFuture is None:
+ return cls
+
+ class CommonFuture:
+ def __init__(self, *args, **kwargs):
+ self.calls = collections.defaultdict(lambda: 0)
+ super().__init__(*args, **kwargs)
+
+ def _schedule_callbacks(self):
+ self.calls['_schedule_callbacks'] += 1
+ return super()._schedule_callbacks()
+
+ def add_done_callback(self, *args):
+ self.calls['add_done_callback'] += 1
+ return super().add_done_callback(*args)
+
+ class Task(CommonFuture, BaseTask):
+ def _step(self, *args):
+ self.calls['_step'] += 1
+ return super()._step(*args)
+
+ def _wakeup(self, *args):
+ self.calls['_wakeup'] += 1
+ return super()._wakeup(*args)
+
+ class Future(CommonFuture, BaseFuture):
+ pass
+
+ def test_subclasses_ctask_cfuture(self):
+ fut = self.Future(loop=self.loop)
+
+ async def func():
+ self.loop.call_soon(lambda: fut.set_result('spam'))
+ return await fut
+
+ task = self.Task(func(), loop=self.loop)
+
+ result = self.loop.run_until_complete(task)
+
+ self.assertEqual(result, 'spam')
+
+ self.assertEqual(
+ dict(task.calls),
+ {'_step': 2, '_wakeup': 1, 'add_done_callback': 1,
+ '_schedule_callbacks': 1})
+
+ self.assertEqual(
+ dict(fut.calls),
+ {'add_done_callback': 1, '_schedule_callbacks': 1})
+
+ # Add patched Task & Future back to the test case
+ cls.Task = Task
+ cls.Future = Future
+
+ # Add an extra unit-test
+ cls.test_subclasses_ctask_cfuture = test_subclasses_ctask_cfuture
+
+ # Disable the "test_task_source_traceback" test
+ # (the test is hardcoded for a particular call stack, which
+ # is slightly different for Task subclasses)
+ cls.test_task_source_traceback = None
+
+ return cls
+
+
+@unittest.skipUnless(hasattr(futures, '_CFuture'),
+ 'requires the C _asyncio module')
+class CTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase):
+ Task = getattr(tasks, '_CTask', None)
+ Future = getattr(futures, '_CFuture', None)
+
+
+@unittest.skipUnless(hasattr(futures, '_CFuture'),
+ 'requires the C _asyncio module')
+@add_subclass_tests
+class CTask_CFuture_SubclassTests(BaseTaskTests, test_utils.TestCase):
+ Task = getattr(tasks, '_CTask', None)
+ Future = getattr(futures, '_CFuture', None)
+
+
+@unittest.skipUnless(hasattr(futures, '_CFuture'),
+ 'requires the C _asyncio module')
+class CTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
+ Task = getattr(tasks, '_CTask', None)
+ Future = futures._PyFuture
+
+
+@unittest.skipUnless(hasattr(futures, '_CFuture'),
+ 'requires the C _asyncio module')
+class PyTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase):
+ Task = tasks._PyTask
+ Future = getattr(futures, '_CFuture', None)
+
+
+class PyTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
+ Task = tasks._PyTask
+ Future = futures._PyFuture
+
+
+@add_subclass_tests
+class PyTask_PyFuture_SubclassTests(BaseTaskTests, test_utils.TestCase):
+ Task = tasks._PyTask
+ Future = futures._PyFuture
+
+
+class GenericTaskTests(test_utils.TestCase):
+
+ def test_future_subclass(self):
+ self.assertTrue(issubclass(asyncio.Task, asyncio.Future))
+
+ def test_asyncio_module_compiled(self):
+ # Because of circular imports it's easy to make _asyncio
+ # module non-importable. This is a simple test that will
+ # fail on systems where C modules were successfully compiled
+ # (hence the test for _functools), but _asyncio somehow didn't.
+ try:
+ import _functools
+ except ImportError:
+ pass
+ else:
+ try:
+ import _asyncio
+ except ImportError:
+ self.fail('_asyncio module is missing')
+
class GatherTestsBase:
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index a3c96c8..d9419df 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -2,20 +2,35 @@
#include "structmember.h"
+/*[clinic input]
+module _asyncio
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=8fd17862aa989c69]*/
+
+
/* identifiers used from some functions */
+_Py_IDENTIFIER(add_done_callback);
_Py_IDENTIFIER(call_soon);
+_Py_IDENTIFIER(cancel);
+_Py_IDENTIFIER(send);
+_Py_IDENTIFIER(throw);
+_Py_IDENTIFIER(_step);
+_Py_IDENTIFIER(_schedule_callbacks);
+_Py_IDENTIFIER(_wakeup);
/* State of the _asyncio module */
+static PyObject *all_tasks;
+static PyDictObject *current_tasks;
static PyObject *traceback_extract_stack;
static PyObject *asyncio_get_event_loop;
-static PyObject *asyncio_repr_info_func;
+static PyObject *asyncio_future_repr_info_func;
+static PyObject *asyncio_task_repr_info_func;
+static PyObject *asyncio_task_get_stack_func;
+static PyObject *asyncio_task_print_stack_func;
static PyObject *asyncio_InvalidStateError;
static PyObject *asyncio_CancelledError;
-
-
-/* Get FutureIter from Future */
-static PyObject* new_future_iter(PyObject *fut);
+static PyObject *inspect_isgenerator;
typedef enum {
@@ -24,24 +39,57 @@ typedef enum {
STATE_FINISHED
} fut_state;
+#define FutureObj_HEAD(prefix) \
+ PyObject_HEAD \
+ PyObject *prefix##_loop; \
+ PyObject *prefix##_callbacks; \
+ PyObject *prefix##_exception; \
+ PyObject *prefix##_result; \
+ PyObject *prefix##_source_tb; \
+ fut_state prefix##_state; \
+ int prefix##_log_tb; \
+ int prefix##_blocking; \
+ PyObject *dict; \
+ PyObject *prefix##_weakreflist;
typedef struct {
- PyObject_HEAD
- PyObject *fut_loop;
- PyObject *fut_callbacks;
- PyObject *fut_exception;
- PyObject *fut_result;
- PyObject *fut_source_tb;
- fut_state fut_state;
- int fut_log_tb;
- int fut_blocking;
- PyObject *dict;
- PyObject *fut_weakreflist;
+ FutureObj_HEAD(fut)
} FutureObj;
+typedef struct {
+ FutureObj_HEAD(task)
+ PyObject *task_fut_waiter;
+ PyObject *task_coro;
+ int task_must_cancel;
+ int task_log_destroy_pending;
+} TaskObj;
+
+typedef struct {
+ PyObject_HEAD
+ TaskObj *sw_task;
+ PyObject *sw_arg;
+} TaskSendMethWrapper;
+
+typedef struct {
+ PyObject_HEAD
+ TaskObj *ww_task;
+} TaskWakeupMethWrapper;
+
+
+#include "clinic/_asynciomodule.c.h"
+
+
+/*[clinic input]
+class _asyncio.Future "FutureObj *" "&Future_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=00d3e4abca711e0f]*/
+
+/* Get FutureIter from Future */
+static PyObject* future_new_iter(PyObject *);
+static inline int future_call_schedule_callbacks(FutureObj *);
static int
-_schedule_callbacks(FutureObj *fut)
+future_schedule_callbacks(FutureObj *fut)
{
Py_ssize_t len;
PyObject* iters;
@@ -87,16 +135,11 @@ _schedule_callbacks(FutureObj *fut)
}
static int
-FutureObj_init(FutureObj *fut, PyObject *args, PyObject *kwds)
+future_init(FutureObj *fut, PyObject *loop)
{
- static char *kwlist[] = {"loop", NULL};
- PyObject *loop = NULL;
PyObject *res = NULL;
_Py_IDENTIFIER(get_debug);
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$O", kwlist, &loop)) {
- return -1;
- }
if (loop == NULL || loop == Py_None) {
loop = PyObject_CallObject(asyncio_get_event_loop, NULL);
if (loop == NULL) {
@@ -128,106 +171,12 @@ FutureObj_init(FutureObj *fut, PyObject *args, PyObject *kwds)
if (fut->fut_callbacks == NULL) {
return -1;
}
- return 0;
-}
-
-static int
-FutureObj_clear(FutureObj *fut)
-{
- Py_CLEAR(fut->fut_loop);
- Py_CLEAR(fut->fut_callbacks);
- Py_CLEAR(fut->fut_result);
- Py_CLEAR(fut->fut_exception);
- Py_CLEAR(fut->fut_source_tb);
- Py_CLEAR(fut->dict);
- return 0;
-}
-static int
-FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
-{
- Py_VISIT(fut->fut_loop);
- Py_VISIT(fut->fut_callbacks);
- Py_VISIT(fut->fut_result);
- Py_VISIT(fut->fut_exception);
- Py_VISIT(fut->fut_source_tb);
- Py_VISIT(fut->dict);
return 0;
}
-PyDoc_STRVAR(pydoc_result,
- "Return the result this future represents.\n"
- "\n"
- "If the future has been cancelled, raises CancelledError. If the\n"
- "future's result isn't yet available, raises InvalidStateError. If\n"
- "the future is done and has an exception set, this exception is raised."
-);
-
-static PyObject *
-FutureObj_result(FutureObj *fut, PyObject *arg)
-{
- if (fut->fut_state == STATE_CANCELLED) {
- PyErr_SetString(asyncio_CancelledError, "");
- return NULL;
- }
-
- if (fut->fut_state != STATE_FINISHED) {
- PyErr_SetString(asyncio_InvalidStateError, "Result is not ready.");
- return NULL;
- }
-
- fut->fut_log_tb = 0;
- if (fut->fut_exception != NULL) {
- PyObject *type = NULL;
- type = PyExceptionInstance_Class(fut->fut_exception);
- PyErr_SetObject(type, fut->fut_exception);
- return NULL;
- }
-
- Py_INCREF(fut->fut_result);
- return fut->fut_result;
-}
-
-PyDoc_STRVAR(pydoc_exception,
- "Return the exception that was set on this future.\n"
- "\n"
- "The exception (or None if no exception was set) is returned only if\n"
- "the future is done. If the future has been cancelled, raises\n"
- "CancelledError. If the future isn't done yet, raises\n"
- "InvalidStateError."
-);
-
-static PyObject *
-FutureObj_exception(FutureObj *fut, PyObject *arg)
-{
- if (fut->fut_state == STATE_CANCELLED) {
- PyErr_SetString(asyncio_CancelledError, "");
- return NULL;
- }
-
- if (fut->fut_state != STATE_FINISHED) {
- PyErr_SetString(asyncio_InvalidStateError, "Result is not ready.");
- return NULL;
- }
-
- if (fut->fut_exception != NULL) {
- fut->fut_log_tb = 0;
- Py_INCREF(fut->fut_exception);
- return fut->fut_exception;
- }
-
- Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(pydoc_set_result,
- "Mark the future done and set its result.\n"
- "\n"
- "If the future is already done when this method is called, raises\n"
- "InvalidStateError."
-);
-
static PyObject *
-FutureObj_set_result(FutureObj *fut, PyObject *res)
+future_set_result(FutureObj *fut, PyObject *res)
{
if (fut->fut_state != STATE_PENDING) {
PyErr_SetString(asyncio_InvalidStateError, "invalid state");
@@ -238,21 +187,14 @@ FutureObj_set_result(FutureObj *fut, PyObject *res)
fut->fut_result = res;
fut->fut_state = STATE_FINISHED;
- if (_schedule_callbacks(fut) == -1) {
+ if (future_call_schedule_callbacks(fut) == -1) {
return NULL;
}
Py_RETURN_NONE;
}
-PyDoc_STRVAR(pydoc_set_exception,
- "Mark the future done and set an exception.\n"
- "\n"
- "If the future is already done when this method is called, raises\n"
- "InvalidStateError."
-);
-
static PyObject *
-FutureObj_set_exception(FutureObj *fut, PyObject *exc)
+future_set_exception(FutureObj *fut, PyObject *exc)
{
PyObject *exc_val = NULL;
@@ -287,7 +229,7 @@ FutureObj_set_exception(FutureObj *fut, PyObject *exc)
fut->fut_exception = exc_val;
fut->fut_state = STATE_FINISHED;
- if (_schedule_callbacks(fut) == -1) {
+ if (future_call_schedule_callbacks(fut) == -1) {
return NULL;
}
@@ -295,16 +237,50 @@ FutureObj_set_exception(FutureObj *fut, PyObject *exc)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(pydoc_add_done_callback,
- "Add a callback to be run when the future becomes done.\n"
- "\n"
- "The callback is called with a single argument - the future object. If\n"
- "the future is already done when this is called, the callback is\n"
- "scheduled with call_soon.";
-);
+static int
+future_get_result(FutureObj *fut, PyObject **result)
+{
+ PyObject *exc;
+
+ if (fut->fut_state == STATE_CANCELLED) {
+ exc = _PyObject_CallNoArg(asyncio_CancelledError);
+ if (exc == NULL) {
+ return -1;
+ }
+ *result = exc;
+ return 1;
+ }
+
+ if (fut->fut_state != STATE_FINISHED) {
+ PyObject *msg = PyUnicode_FromString("Result is not ready.");
+ if (msg == NULL) {
+ return -1;
+ }
+
+ exc = _PyObject_CallArg1(asyncio_InvalidStateError, msg);
+ Py_DECREF(msg);
+ if (exc == NULL) {
+ return -1;
+ }
+
+ *result = exc;
+ return 1;
+ }
+
+ fut->fut_log_tb = 0;
+ if (fut->fut_exception != NULL) {
+ Py_INCREF(fut->fut_exception);
+ *result = fut->fut_exception;
+ return 1;
+ }
+
+ Py_INCREF(fut->fut_result);
+ *result = fut->fut_result;
+ return 0;
+}
static PyObject *
-FutureObj_add_done_callback(FutureObj *fut, PyObject *arg)
+future_add_done_callback(FutureObj *fut, PyObject *arg)
{
if (fut->fut_state != STATE_PENDING) {
PyObject *handle = _PyObject_CallMethodId(
@@ -326,19 +302,216 @@ FutureObj_add_done_callback(FutureObj *fut, PyObject *arg)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(pydoc_remove_done_callback,
- "Remove all instances of a callback from the \"call when done\" list.\n"
- "\n"
- "Returns the number of callbacks removed."
-);
+static PyObject *
+future_cancel(FutureObj *fut)
+{
+ if (fut->fut_state != STATE_PENDING) {
+ Py_RETURN_FALSE;
+ }
+ fut->fut_state = STATE_CANCELLED;
+
+ if (future_call_schedule_callbacks(fut) == -1) {
+ return NULL;
+ }
+
+ Py_RETURN_TRUE;
+}
+
+/*[clinic input]
+_asyncio.Future.__init__
+
+ *
+ loop: 'O' = NULL
+
+This class is *almost* compatible with concurrent.futures.Future.
+
+ Differences:
+
+ - result() and exception() do not take a timeout argument and
+ raise an exception when the future isn't done yet.
+
+ - Callbacks registered with add_done_callback() are always called
+ via the event loop's call_soon_threadsafe().
+
+ - This class is not compatible with the wait() and as_completed()
+ methods in the concurrent.futures package.
+[clinic start generated code]*/
+
+static int
+_asyncio_Future___init___impl(FutureObj *self, PyObject *loop)
+/*[clinic end generated code: output=9ed75799eaccb5d6 input=8e1681f23605be2d]*/
+
+{
+ return future_init(self, loop);
+}
+
+static int
+FutureObj_clear(FutureObj *fut)
+{
+ Py_CLEAR(fut->fut_loop);
+ Py_CLEAR(fut->fut_callbacks);
+ Py_CLEAR(fut->fut_result);
+ Py_CLEAR(fut->fut_exception);
+ Py_CLEAR(fut->fut_source_tb);
+ Py_CLEAR(fut->dict);
+ return 0;
+}
+
+static int
+FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
+{
+ Py_VISIT(fut->fut_loop);
+ Py_VISIT(fut->fut_callbacks);
+ Py_VISIT(fut->fut_result);
+ Py_VISIT(fut->fut_exception);
+ Py_VISIT(fut->fut_source_tb);
+ Py_VISIT(fut->dict);
+ return 0;
+}
+
+/*[clinic input]
+_asyncio.Future.result
+
+Return the result this future represents.
+
+If the future has been cancelled, raises CancelledError. If the
+future's result isn't yet available, raises InvalidStateError. If
+the future is done and has an exception set, this exception is raised.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Future_result_impl(FutureObj *self)
+/*[clinic end generated code: output=f35f940936a4b1e5 input=49ecf9cf5ec50dc5]*/
+{
+ PyObject *result;
+ int res = future_get_result(self, &result);
+
+ if (res == -1) {
+ return NULL;
+ }
+
+ if (res == 0) {
+ return result;
+ }
+
+ assert(res == 1);
+
+ PyErr_SetObject(PyExceptionInstance_Class(result), result);
+ Py_DECREF(result);
+ return NULL;
+}
+
+/*[clinic input]
+_asyncio.Future.exception
+
+Return the exception that was set on this future.
+
+The exception (or None if no exception was set) is returned only if
+the future is done. If the future has been cancelled, raises
+CancelledError. If the future isn't done yet, raises
+InvalidStateError.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Future_exception_impl(FutureObj *self)
+/*[clinic end generated code: output=88b20d4f855e0710 input=733547a70c841c68]*/
+{
+ if (self->fut_state == STATE_CANCELLED) {
+ PyErr_SetString(asyncio_CancelledError, "");
+ return NULL;
+ }
+
+ if (self->fut_state != STATE_FINISHED) {
+ PyErr_SetString(asyncio_InvalidStateError, "Result is not ready.");
+ return NULL;
+ }
+
+ if (self->fut_exception != NULL) {
+ self->fut_log_tb = 0;
+ Py_INCREF(self->fut_exception);
+ return self->fut_exception;
+ }
+
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+_asyncio.Future.set_result
+
+ res: 'O'
+ /
+
+Mark the future done and set its result.
+
+If the future is already done when this method is called, raises
+InvalidStateError.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Future_set_result(FutureObj *self, PyObject *res)
+/*[clinic end generated code: output=a620abfc2796bfb6 input=8619565e0503357e]*/
+{
+ return future_set_result(self, res);
+}
+
+/*[clinic input]
+_asyncio.Future.set_exception
+
+ exception: 'O'
+ /
+
+Mark the future done and set an exception.
+
+If the future is already done when this method is called, raises
+InvalidStateError.
+[clinic start generated code]*/
static PyObject *
-FutureObj_remove_done_callback(FutureObj *fut, PyObject *arg)
+_asyncio_Future_set_exception(FutureObj *self, PyObject *exception)
+/*[clinic end generated code: output=f1c1b0cd321be360 input=1377dbe15e6ea186]*/
+{
+ return future_set_exception(self, exception);
+}
+
+/*[clinic input]
+_asyncio.Future.add_done_callback
+
+ fn: 'O'
+ /
+
+Add a callback to be run when the future becomes done.
+
+The callback is called with a single argument - the future object. If
+the future is already done when this is called, the callback is
+scheduled with call_soon.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Future_add_done_callback(FutureObj *self, PyObject *fn)
+/*[clinic end generated code: output=819e09629b2ec2b5 input=8cce187e32cec6a8]*/
+{
+ return future_add_done_callback(self, fn);
+}
+
+/*[clinic input]
+_asyncio.Future.remove_done_callback
+
+ fn: 'O'
+ /
+
+Remove all instances of a callback from the "call when done" list.
+
+Returns the number of callbacks removed.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
+/*[clinic end generated code: output=5ab1fb52b24ef31f input=3fedb73e1409c31c]*/
{
PyObject *newlist;
Py_ssize_t len, i, j=0;
- len = PyList_GET_SIZE(fut->fut_callbacks);
+ len = PyList_GET_SIZE(self->fut_callbacks);
if (len == 0) {
return PyLong_FromSsize_t(0);
}
@@ -350,9 +523,9 @@ FutureObj_remove_done_callback(FutureObj *fut, PyObject *arg)
for (i = 0; i < len; i++) {
int ret;
- PyObject *item = PyList_GET_ITEM(fut->fut_callbacks, i);
+ PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
- if ((ret = PyObject_RichCompareBool(arg, item, Py_EQ)) < 0) {
+ if ((ret = PyObject_RichCompareBool(fn, item, Py_EQ)) < 0) {
goto fail;
}
if (ret == 0) {
@@ -365,7 +538,7 @@ FutureObj_remove_done_callback(FutureObj *fut, PyObject *arg)
if (PyList_SetSlice(newlist, j, len, NULL) < 0) {
goto fail;
}
- if (PyList_SetSlice(fut->fut_callbacks, 0, len, newlist) < 0) {
+ if (PyList_SetSlice(self->fut_callbacks, 0, len, newlist) < 0) {
goto fail;
}
Py_DECREF(newlist);
@@ -376,35 +549,34 @@ fail:
return NULL;
}
-PyDoc_STRVAR(pydoc_cancel,
- "Cancel the future and schedule callbacks.\n"
- "\n"
- "If the future is already done or cancelled, return False. Otherwise,\n"
- "change the future's state to cancelled, schedule the callbacks and\n"
- "return True."
-);
+/*[clinic input]
+_asyncio.Future.cancel
-static PyObject *
-FutureObj_cancel(FutureObj *fut, PyObject *arg)
-{
- if (fut->fut_state != STATE_PENDING) {
- Py_RETURN_FALSE;
- }
- fut->fut_state = STATE_CANCELLED;
+Cancel the future and schedule callbacks.
- if (_schedule_callbacks(fut) == -1) {
- return NULL;
- }
+If the future is already done or cancelled, return False. Otherwise,
+change the future's state to cancelled, schedule the callbacks and
+return True.
+[clinic start generated code]*/
- Py_RETURN_TRUE;
+static PyObject *
+_asyncio_Future_cancel_impl(FutureObj *self)
+/*[clinic end generated code: output=e45b932ba8bd68a1 input=515709a127995109]*/
+{
+ return future_cancel(self);
}
-PyDoc_STRVAR(pydoc_cancelled, "Return True if the future was cancelled.");
+/*[clinic input]
+_asyncio.Future.cancelled
+
+Return True if the future was cancelled.
+[clinic start generated code]*/
static PyObject *
-FutureObj_cancelled(FutureObj *fut, PyObject *arg)
+_asyncio_Future_cancelled_impl(FutureObj *self)
+/*[clinic end generated code: output=145197ced586357d input=943ab8b7b7b17e45]*/
{
- if (fut->fut_state == STATE_CANCELLED) {
+ if (self->fut_state == STATE_CANCELLED) {
Py_RETURN_TRUE;
}
else {
@@ -412,17 +584,20 @@ FutureObj_cancelled(FutureObj *fut, PyObject *arg)
}
}
-PyDoc_STRVAR(pydoc_done,
- "Return True if the future is done.\n"
- "\n"
- "Done means either that a result / exception are available, or that the\n"
- "future was cancelled."
-);
+/*[clinic input]
+_asyncio.Future.done
+
+Return True if the future is done.
+
+Done means either that a result / exception are available, or that the
+future was cancelled.
+[clinic start generated code]*/
static PyObject *
-FutureObj_done(FutureObj *fut, PyObject *arg)
+_asyncio_Future_done_impl(FutureObj *self)
+/*[clinic end generated code: output=244c5ac351145096 input=28d7b23fdb65d2ac]*/
{
- if (fut->fut_state == STATE_PENDING) {
+ if (self->fut_state == STATE_PENDING) {
Py_RETURN_FALSE;
}
else {
@@ -538,13 +713,31 @@ FutureObj_get_state(FutureObj *fut)
return ret;
}
-static PyObject*
-FutureObj__repr_info(FutureObj *fut)
+/*[clinic input]
+_asyncio.Future._repr_info
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Future__repr_info_impl(FutureObj *self)
+/*[clinic end generated code: output=fa69e901bd176cfb input=f21504d8e2ae1ca2]*/
+{
+ return PyObject_CallFunctionObjArgs(
+ asyncio_future_repr_info_func, self, NULL);
+}
+
+/*[clinic input]
+_asyncio.Future._schedule_callbacks
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Future__schedule_callbacks_impl(FutureObj *self)
+/*[clinic end generated code: output=5e8958d89ea1c5dc input=4f5f295f263f4a88]*/
{
- if (asyncio_repr_info_func == NULL) {
- return PyList_New(0);
+ int ret = future_schedule_callbacks(self);
+ if (ret == -1) {
+ return NULL;
}
- return PyObject_CallFunctionObjArgs(asyncio_repr_info_func, fut, NULL);
+ Py_RETURN_NONE;
}
static PyObject *
@@ -661,43 +854,39 @@ finally:
static PyAsyncMethods FutureType_as_async = {
- (unaryfunc)new_future_iter, /* am_await */
+ (unaryfunc)future_new_iter, /* am_await */
0, /* am_aiter */
0 /* am_anext */
};
static PyMethodDef FutureType_methods[] = {
- {"_repr_info", (PyCFunction)FutureObj__repr_info, METH_NOARGS, NULL},
- {"add_done_callback",
- (PyCFunction)FutureObj_add_done_callback,
- METH_O, pydoc_add_done_callback},
- {"remove_done_callback",
- (PyCFunction)FutureObj_remove_done_callback,
- METH_O, pydoc_remove_done_callback},
- {"set_result",
- (PyCFunction)FutureObj_set_result, METH_O, pydoc_set_result},
- {"set_exception",
- (PyCFunction)FutureObj_set_exception, METH_O, pydoc_set_exception},
- {"cancel", (PyCFunction)FutureObj_cancel, METH_NOARGS, pydoc_cancel},
- {"cancelled",
- (PyCFunction)FutureObj_cancelled, METH_NOARGS, pydoc_cancelled},
- {"done", (PyCFunction)FutureObj_done, METH_NOARGS, pydoc_done},
- {"result", (PyCFunction)FutureObj_result, METH_NOARGS, pydoc_result},
- {"exception",
- (PyCFunction)FutureObj_exception, METH_NOARGS, pydoc_exception},
+ _ASYNCIO_FUTURE_RESULT_METHODDEF
+ _ASYNCIO_FUTURE_EXCEPTION_METHODDEF
+ _ASYNCIO_FUTURE_SET_RESULT_METHODDEF
+ _ASYNCIO_FUTURE_SET_EXCEPTION_METHODDEF
+ _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF
+ _ASYNCIO_FUTURE_REMOVE_DONE_CALLBACK_METHODDEF
+ _ASYNCIO_FUTURE_CANCEL_METHODDEF
+ _ASYNCIO_FUTURE_CANCELLED_METHODDEF
+ _ASYNCIO_FUTURE_DONE_METHODDEF
+ _ASYNCIO_FUTURE__REPR_INFO_METHODDEF
+ _ASYNCIO_FUTURE__SCHEDULE_CALLBACKS_METHODDEF
{NULL, NULL} /* Sentinel */
};
-static PyGetSetDef FutureType_getsetlist[] = {
- {"_state", (getter)FutureObj_get_state, NULL, NULL},
- {"_asyncio_future_blocking", (getter)FutureObj_get_blocking,
- (setter)FutureObj_set_blocking, NULL},
- {"_loop", (getter)FutureObj_get_loop, NULL, NULL},
- {"_callbacks", (getter)FutureObj_get_callbacks, NULL, NULL},
- {"_result", (getter)FutureObj_get_result, NULL, NULL},
- {"_exception", (getter)FutureObj_get_exception, NULL, NULL},
- {"_log_traceback", (getter)FutureObj_get_log_traceback, NULL, NULL},
+#define FUTURE_COMMON_GETSETLIST \
+ {"_state", (getter)FutureObj_get_state, NULL, NULL}, \
+ {"_asyncio_future_blocking", (getter)FutureObj_get_blocking, \
+ (setter)FutureObj_set_blocking, NULL}, \
+ {"_loop", (getter)FutureObj_get_loop, NULL, NULL}, \
+ {"_callbacks", (getter)FutureObj_get_callbacks, NULL, NULL}, \
+ {"_result", (getter)FutureObj_get_result, NULL, NULL}, \
+ {"_exception", (getter)FutureObj_get_exception, NULL, NULL}, \
+ {"_log_traceback", (getter)FutureObj_get_log_traceback, NULL, NULL}, \
{"_source_traceback", (getter)FutureObj_get_source_traceback, NULL, NULL},
+
+static PyGetSetDef FutureType_getsetlist[] = {
+ FUTURE_COMMON_GETSETLIST
{NULL} /* Sentinel */
};
@@ -712,25 +901,46 @@ static PyTypeObject FutureType = {
.tp_repr = (reprfunc)FutureObj_repr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_FINALIZE,
- .tp_doc = "Fast asyncio.Future implementation.",
+ .tp_doc = _asyncio_Future___init____doc__,
.tp_traverse = (traverseproc)FutureObj_traverse,
.tp_clear = (inquiry)FutureObj_clear,
.tp_weaklistoffset = offsetof(FutureObj, fut_weakreflist),
- .tp_iter = (getiterfunc)new_future_iter,
+ .tp_iter = (getiterfunc)future_new_iter,
.tp_methods = FutureType_methods,
.tp_getset = FutureType_getsetlist,
.tp_dictoffset = offsetof(FutureObj, dict),
- .tp_init = (initproc)FutureObj_init,
+ .tp_init = (initproc)_asyncio_Future___init__,
.tp_new = PyType_GenericNew,
.tp_finalize = (destructor)FutureObj_finalize,
};
+#define Future_CheckExact(obj) (Py_TYPE(obj) == &FutureType)
+
+static inline int
+future_call_schedule_callbacks(FutureObj *fut)
+{
+ if (Future_CheckExact(fut)) {
+ return future_schedule_callbacks(fut);
+ }
+ else {
+ /* `fut` is a subclass of Future */
+ PyObject *ret = _PyObject_CallMethodId(
+ (PyObject*)fut, &PyId__schedule_callbacks, NULL);
+ if (ret == NULL) {
+ return -1;
+ }
+
+ Py_DECREF(ret);
+ return 0;
+ }
+}
+
static void
FutureObj_dealloc(PyObject *self)
{
FutureObj *fut = (FutureObj *)self;
- if (Py_TYPE(fut) == &FutureType) {
+ if (Future_CheckExact(fut)) {
/* When fut is subclass of Future, finalizer is called from
* subtype_dealloc.
*/
@@ -744,7 +954,7 @@ FutureObj_dealloc(PyObject *self)
PyObject_ClearWeakRefs(self);
}
- FutureObj_clear(fut);
+ (void)FutureObj_clear(fut);
Py_TYPE(fut)->tp_free(fut);
}
@@ -759,7 +969,7 @@ typedef struct {
static void
FutureIter_dealloc(futureiterobject *it)
{
- _PyObject_GC_UNTRACK(it);
+ PyObject_GC_UnTrack(it);
Py_XDECREF(it->future);
PyObject_GC_Del(it);
}
@@ -785,7 +995,7 @@ FutureIter_iternext(futureiterobject *it)
return NULL;
}
- res = FutureObj_result(fut, NULL);
+ res = _asyncio_Future_result_impl(fut);
if (res != NULL) {
/* The result of the Future is not an exception.
@@ -884,37 +1094,19 @@ static PyMethodDef FutureIter_methods[] = {
static PyTypeObject FutureIterType = {
PyVarObject_HEAD_INIT(0, 0)
"_asyncio.FutureIter",
- sizeof(futureiterobject), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor)FutureIter_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_as_async */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
- 0, /* tp_doc */
- (traverseproc)FutureIter_traverse, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- PyObject_SelfIter, /* tp_iter */
- (iternextfunc)FutureIter_iternext, /* tp_iternext */
- FutureIter_methods, /* tp_methods */
- 0, /* tp_members */
+ .tp_basicsize = sizeof(futureiterobject),
+ .tp_itemsize = 0,
+ .tp_dealloc = (destructor)FutureIter_dealloc,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)FutureIter_traverse,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)FutureIter_iternext,
+ .tp_methods = FutureIter_methods,
};
static PyObject *
-new_future_iter(PyObject *fut)
+future_new_iter(PyObject *fut)
{
futureiterobject *it;
@@ -932,68 +1124,1283 @@ new_future_iter(PyObject *fut)
return (PyObject*)it;
}
-/*********************** Module **************************/
+
+/*********************** Task **************************/
+
+
+/*[clinic input]
+class _asyncio.Task "TaskObj *" "&Task_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=719dcef0fcc03b37]*/
+
+static int task_call_step_soon(TaskObj *, PyObject *);
+static inline PyObject * task_call_wakeup(TaskObj *, PyObject *);
+static inline PyObject * task_call_step(TaskObj *, PyObject *);
+static PyObject * task_wakeup(TaskObj *, PyObject *);
+static PyObject * task_step(TaskObj *, PyObject *);
+
+/* ----- Task._step wrapper */
static int
-init_module(void)
+TaskSendMethWrapper_clear(TaskSendMethWrapper *o)
{
- PyObject *module = NULL;
+ Py_CLEAR(o->sw_task);
+ Py_CLEAR(o->sw_arg);
+ return 0;
+}
+
+static void
+TaskSendMethWrapper_dealloc(TaskSendMethWrapper *o)
+{
+ PyObject_GC_UnTrack(o);
+ (void)TaskSendMethWrapper_clear(o);
+ Py_TYPE(o)->tp_free(o);
+}
+
+static PyObject *
+TaskSendMethWrapper_call(TaskSendMethWrapper *o,
+ PyObject *args, PyObject *kwds)
+{
+ return task_call_step(o->sw_task, o->sw_arg);
+}
+
+static int
+TaskSendMethWrapper_traverse(TaskSendMethWrapper *o,
+ visitproc visit, void *arg)
+{
+ Py_VISIT(o->sw_task);
+ Py_VISIT(o->sw_arg);
+ return 0;
+}
+
+static PyObject *
+TaskSendMethWrapper_get___self__(TaskSendMethWrapper *o)
+{
+ if (o->sw_task) {
+ Py_INCREF(o->sw_task);
+ return (PyObject*)o->sw_task;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyGetSetDef TaskSendMethWrapper_getsetlist[] = {
+ {"__self__", (getter)TaskSendMethWrapper_get___self__, NULL, NULL},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject TaskSendMethWrapper_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "TaskSendMethWrapper",
+ .tp_basicsize = sizeof(TaskSendMethWrapper),
+ .tp_itemsize = 0,
+ .tp_getset = TaskSendMethWrapper_getsetlist,
+ .tp_dealloc = (destructor)TaskSendMethWrapper_dealloc,
+ .tp_call = (ternaryfunc)TaskSendMethWrapper_call,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)TaskSendMethWrapper_traverse,
+ .tp_clear = (inquiry)TaskSendMethWrapper_clear,
+};
+
+static PyObject *
+TaskSendMethWrapper_new(TaskObj *task, PyObject *arg)
+{
+ TaskSendMethWrapper *o;
+ o = PyObject_GC_New(TaskSendMethWrapper, &TaskSendMethWrapper_Type);
+ if (o == NULL) {
+ return NULL;
+ }
- module = PyImport_ImportModule("traceback");
- if (module == NULL) {
+ Py_INCREF(task);
+ o->sw_task = task;
+
+ Py_XINCREF(arg);
+ o->sw_arg = arg;
+
+ PyObject_GC_Track(o);
+ return (PyObject*) o;
+}
+
+/* ----- Task._wakeup wrapper */
+
+static PyObject *
+TaskWakeupMethWrapper_call(TaskWakeupMethWrapper *o,
+ PyObject *args, PyObject *kwds)
+{
+ PyObject *fut;
+
+ if (!PyArg_ParseTuple(args, "O|", &fut)) {
+ return NULL;
+ }
+
+ return task_call_wakeup(o->ww_task, fut);
+}
+
+static int
+TaskWakeupMethWrapper_clear(TaskWakeupMethWrapper *o)
+{
+ Py_CLEAR(o->ww_task);
+ return 0;
+}
+
+static int
+TaskWakeupMethWrapper_traverse(TaskWakeupMethWrapper *o,
+ visitproc visit, void *arg)
+{
+ Py_VISIT(o->ww_task);
+ return 0;
+}
+
+static void
+TaskWakeupMethWrapper_dealloc(TaskWakeupMethWrapper *o)
+{
+ PyObject_GC_UnTrack(o);
+ (void)TaskWakeupMethWrapper_clear(o);
+ Py_TYPE(o)->tp_free(o);
+}
+
+PyTypeObject TaskWakeupMethWrapper_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "TaskWakeupMethWrapper",
+ .tp_basicsize = sizeof(TaskWakeupMethWrapper),
+ .tp_itemsize = 0,
+ .tp_dealloc = (destructor)TaskWakeupMethWrapper_dealloc,
+ .tp_call = (ternaryfunc)TaskWakeupMethWrapper_call,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)TaskWakeupMethWrapper_traverse,
+ .tp_clear = (inquiry)TaskWakeupMethWrapper_clear,
+};
+
+static PyObject *
+TaskWakeupMethWrapper_new(TaskObj *task)
+{
+ TaskWakeupMethWrapper *o;
+ o = PyObject_GC_New(TaskWakeupMethWrapper, &TaskWakeupMethWrapper_Type);
+ if (o == NULL) {
+ return NULL;
+ }
+
+ Py_INCREF(task);
+ o->ww_task = task;
+
+ PyObject_GC_Track(o);
+ return (PyObject*) o;
+}
+
+/* ----- Task */
+
+/*[clinic input]
+_asyncio.Task.__init__
+
+ coro: 'O'
+ *
+ loop: 'O' = NULL
+
+A coroutine wrapped in a Future.
+[clinic start generated code]*/
+
+static int
+_asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
+/*[clinic end generated code: output=9f24774c2287fc2f input=71d8d28c201a18cd]*/
+{
+ PyObject *res;
+ _Py_IDENTIFIER(add);
+
+ if (future_init((FutureObj*)self, loop)) {
return -1;
}
- // new reference
- traceback_extract_stack = PyObject_GetAttrString(module, "extract_stack");
- if (traceback_extract_stack == NULL) {
- goto fail;
+
+ self->task_fut_waiter = NULL;
+ self->task_must_cancel = 0;
+ self->task_log_destroy_pending = 1;
+
+ Py_INCREF(coro);
+ self->task_coro = coro;
+
+ if (task_call_step_soon(self, NULL)) {
+ return -1;
+ }
+
+ res = _PyObject_CallMethodId(all_tasks, &PyId_add, "O", self, NULL);
+ if (res == NULL) {
+ return -1;
+ }
+ Py_DECREF(res);
+
+ return 0;
+}
+
+static int
+TaskObj_clear(TaskObj *task)
+{
+ (void)FutureObj_clear((FutureObj*) task);
+ Py_CLEAR(task->task_coro);
+ Py_CLEAR(task->task_fut_waiter);
+ return 0;
+}
+
+static int
+TaskObj_traverse(TaskObj *task, visitproc visit, void *arg)
+{
+ Py_VISIT(task->task_coro);
+ Py_VISIT(task->task_fut_waiter);
+ (void)FutureObj_traverse((FutureObj*) task, visit, arg);
+ return 0;
+}
+
+static PyObject *
+TaskObj_get_log_destroy_pending(TaskObj *task)
+{
+ if (task->task_log_destroy_pending) {
+ Py_RETURN_TRUE;
+ }
+ else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static int
+TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val)
+{
+ int is_true = PyObject_IsTrue(val);
+ if (is_true < 0) {
+ return -1;
+ }
+ task->task_log_destroy_pending = is_true;
+ return 0;
+}
+
+static PyObject *
+TaskObj_get_must_cancel(TaskObj *task)
+{
+ if (task->task_must_cancel) {
+ Py_RETURN_TRUE;
+ }
+ else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static PyObject *
+TaskObj_get_coro(TaskObj *task)
+{
+ if (task->task_coro) {
+ Py_INCREF(task->task_coro);
+ return task->task_coro;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+TaskObj_get_fut_waiter(TaskObj *task)
+{
+ if (task->task_fut_waiter) {
+ Py_INCREF(task->task_fut_waiter);
+ return task->task_fut_waiter;
+ }
+
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+@classmethod
+_asyncio.Task.current_task
+
+ loop: 'O' = NULL
+
+Return the currently running task in an event loop or None.
+
+By default the current task for the current event loop is returned.
+
+None is returned when called not in the context of a Task.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task_current_task_impl(PyTypeObject *type, PyObject *loop)
+/*[clinic end generated code: output=99fbe7332c516e03 input=cd784537f02cf833]*/
+{
+ PyObject *res;
+
+ if (loop == NULL) {
+ loop = PyObject_CallObject(asyncio_get_event_loop, NULL);
+ if (loop == NULL) {
+ return NULL;
+ }
+
+ res = PyDict_GetItem((PyObject*)current_tasks, loop);
+ Py_DECREF(loop);
+ }
+ else {
+ res = PyDict_GetItem((PyObject*)current_tasks, loop);
}
- Py_DECREF(module);
- module = PyImport_ImportModule("asyncio.events");
- if (module == NULL) {
+ if (res == NULL) {
+ Py_RETURN_NONE;
+ }
+ else {
+ Py_INCREF(res);
+ return res;
+ }
+}
+
+static PyObject *
+task_all_tasks(PyObject *loop)
+{
+ PyObject *task;
+ PyObject *task_loop;
+ PyObject *set;
+ PyObject *iter;
+
+ assert(loop != NULL);
+
+ set = PySet_New(NULL);
+ if (set == NULL) {
+ return NULL;
+ }
+
+ iter = PyObject_GetIter(all_tasks);
+ if (iter == NULL) {
goto fail;
}
- asyncio_get_event_loop = PyObject_GetAttrString(module, "get_event_loop");
- if (asyncio_get_event_loop == NULL) {
+
+ while ((task = PyIter_Next(iter))) {
+ task_loop = PyObject_GetAttrString(task, "_loop");
+ if (task_loop == NULL) {
+ Py_DECREF(task);
+ goto fail;
+ }
+ if (task_loop == loop) {
+ if (PySet_Add(set, task) == -1) {
+ Py_DECREF(task_loop);
+ Py_DECREF(task);
+ goto fail;
+ }
+ }
+ Py_DECREF(task_loop);
+ Py_DECREF(task);
+ }
+
+ Py_DECREF(iter);
+ return set;
+
+fail:
+ Py_XDECREF(set);
+ Py_XDECREF(iter);
+ return NULL;
+}
+
+/*[clinic input]
+@classmethod
+_asyncio.Task.all_tasks
+
+ loop: 'O' = NULL
+
+Return a set of all tasks for an event loop.
+
+By default all tasks for the current event loop are returned.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task_all_tasks_impl(PyTypeObject *type, PyObject *loop)
+/*[clinic end generated code: output=11f9b20749ccca5d input=cd64aa5f88bd5c49]*/
+{
+ PyObject *res;
+
+ if (loop == NULL) {
+ loop = PyObject_CallObject(asyncio_get_event_loop, NULL);
+ if (loop == NULL) {
+ return NULL;
+ }
+
+ res = task_all_tasks(loop);
+ Py_DECREF(loop);
+ }
+ else {
+ res = task_all_tasks(loop);
+ }
+
+ return res;
+}
+
+/*[clinic input]
+_asyncio.Task._repr_info
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task__repr_info_impl(TaskObj *self)
+/*[clinic end generated code: output=6a490eb66d5ba34b input=3c6d051ed3ddec8b]*/
+{
+ return PyObject_CallFunctionObjArgs(
+ asyncio_task_repr_info_func, self, NULL);
+}
+
+/*[clinic input]
+_asyncio.Task.cancel
+
+Request that this task cancel itself.
+
+This arranges for a CancelledError to be thrown into the
+wrapped coroutine on the next cycle through the event loop.
+The coroutine then has a chance to clean up or even deny
+the request using try/except/finally.
+
+Unlike Future.cancel, this does not guarantee that the
+task will be cancelled: the exception might be caught and
+acted upon, delaying cancellation of the task or preventing
+cancellation completely. The task may also return a value or
+raise a different exception.
+
+Immediately after this method is called, Task.cancelled() will
+not return True (unless the task was already cancelled). A
+task will be marked as cancelled when the wrapped coroutine
+terminates with a CancelledError exception (even if cancel()
+was not called).
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task_cancel_impl(TaskObj *self)
+/*[clinic end generated code: output=6bfc0479da9d5757 input=13f9bf496695cb52]*/
+{
+ if (self->task_state != STATE_PENDING) {
+ Py_RETURN_FALSE;
+ }
+
+ if (self->task_fut_waiter) {
+ PyObject *res;
+ int is_true;
+
+ res = _PyObject_CallMethodId(
+ self->task_fut_waiter, &PyId_cancel, NULL);
+ if (res == NULL) {
+ return NULL;
+ }
+
+ is_true = PyObject_IsTrue(res);
+ Py_DECREF(res);
+ if (is_true < 0) {
+ return NULL;
+ }
+
+ if (is_true) {
+ Py_RETURN_TRUE;
+ }
+ }
+
+ self->task_must_cancel = 1;
+ Py_RETURN_TRUE;
+}
+
+/*[clinic input]
+_asyncio.Task.get_stack
+
+ *
+ limit: 'O' = None
+
+Return the list of stack frames for this task's coroutine.
+
+If the coroutine is not done, this returns the stack where it is
+suspended. If the coroutine has completed successfully or was
+cancelled, this returns an empty list. If the coroutine was
+terminated by an exception, this returns the list of traceback
+frames.
+
+The frames are always ordered from oldest to newest.
+
+The optional limit gives the maximum number of frames to
+return; by default all available frames are returned. Its
+meaning differs depending on whether a stack or a traceback is
+returned: the newest frames of a stack are returned, but the
+oldest frames of a traceback are returned. (This matches the
+behavior of the traceback module.)
+
+For reasons beyond our control, only one stack frame is
+returned for a suspended coroutine.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task_get_stack_impl(TaskObj *self, PyObject *limit)
+/*[clinic end generated code: output=c9aeeeebd1e18118 input=b1920230a766d17a]*/
+{
+ return PyObject_CallFunctionObjArgs(
+ asyncio_task_get_stack_func, self, limit, NULL);
+}
+
+/*[clinic input]
+_asyncio.Task.print_stack
+
+ *
+ limit: 'O' = None
+ file: 'O' = None
+
+Print the stack or traceback for this task's coroutine.
+
+This produces output similar to that of the traceback module,
+for the frames retrieved by get_stack(). The limit argument
+is passed to get_stack(). The file argument is an I/O stream
+to which the output is written; by default output is written
+to sys.stderr.
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task_print_stack_impl(TaskObj *self, PyObject *limit,
+ PyObject *file)
+/*[clinic end generated code: output=7339e10314cd3f4d input=19f1e99ab5400bc3]*/
+{
+ return PyObject_CallFunctionObjArgs(
+ asyncio_task_print_stack_func, self, limit, file, NULL);
+}
+
+/*[clinic input]
+_asyncio.Task._step
+
+ exc: 'O' = NULL
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task__step_impl(TaskObj *self, PyObject *exc)
+/*[clinic end generated code: output=7ed23f0cefd5ae42 input=ada4b2324e5370af]*/
+{
+ return task_step(self, exc == Py_None ? NULL : exc);
+}
+
+/*[clinic input]
+_asyncio.Task._wakeup
+
+ fut: 'O'
+[clinic start generated code]*/
+
+static PyObject *
+_asyncio_Task__wakeup_impl(TaskObj *self, PyObject *fut)
+/*[clinic end generated code: output=75cb341c760fd071 input=11ee4918a5bdbf21]*/
+{
+ return task_wakeup(self, fut);
+}
+
+static void
+TaskObj_finalize(TaskObj *task)
+{
+ _Py_IDENTIFIER(call_exception_handler);
+ _Py_IDENTIFIER(task);
+ _Py_IDENTIFIER(message);
+ _Py_IDENTIFIER(source_traceback);
+
+ PyObject *message = NULL;
+ PyObject *context = NULL;
+ PyObject *func = NULL;
+ PyObject *res = NULL;
+
+ PyObject *error_type, *error_value, *error_traceback;
+
+ if (task->task_state != STATE_PENDING || !task->task_log_destroy_pending) {
+ goto done;
+ }
+
+ /* Save the current exception, if any. */
+ PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+ context = PyDict_New();
+ if (context == NULL) {
+ goto finally;
+ }
+
+ message = PyUnicode_FromString("Task was destroyed but it is pending!");
+ if (message == NULL) {
+ goto finally;
+ }
+
+ if (_PyDict_SetItemId(context, &PyId_message, message) < 0 ||
+ _PyDict_SetItemId(context, &PyId_task, (PyObject*)task) < 0)
+ {
+ goto finally;
+ }
+
+ if (task->task_source_tb != NULL) {
+ if (_PyDict_SetItemId(context, &PyId_source_traceback,
+ task->task_source_tb) < 0)
+ {
+ goto finally;
+ }
+ }
+
+ func = _PyObject_GetAttrId(task->task_loop, &PyId_call_exception_handler);
+ if (func != NULL) {
+ res = _PyObject_CallArg1(func, context);
+ if (res == NULL) {
+ PyErr_WriteUnraisable(func);
+ }
+ }
+
+finally:
+ Py_CLEAR(context);
+ Py_CLEAR(message);
+ Py_CLEAR(func);
+ Py_CLEAR(res);
+
+ /* Restore the saved exception. */
+ PyErr_Restore(error_type, error_value, error_traceback);
+
+done:
+ FutureObj_finalize((FutureObj*)task);
+}
+
+static void TaskObj_dealloc(PyObject *); /* Needs Task_CheckExact */
+
+static PyMethodDef TaskType_methods[] = {
+ _ASYNCIO_FUTURE_RESULT_METHODDEF
+ _ASYNCIO_FUTURE_EXCEPTION_METHODDEF
+ _ASYNCIO_FUTURE_SET_RESULT_METHODDEF
+ _ASYNCIO_FUTURE_SET_EXCEPTION_METHODDEF
+ _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF
+ _ASYNCIO_FUTURE_REMOVE_DONE_CALLBACK_METHODDEF
+ _ASYNCIO_FUTURE_CANCELLED_METHODDEF
+ _ASYNCIO_FUTURE_DONE_METHODDEF
+ _ASYNCIO_TASK_CURRENT_TASK_METHODDEF
+ _ASYNCIO_TASK_ALL_TASKS_METHODDEF
+ _ASYNCIO_TASK_CANCEL_METHODDEF
+ _ASYNCIO_TASK_GET_STACK_METHODDEF
+ _ASYNCIO_TASK_PRINT_STACK_METHODDEF
+ _ASYNCIO_TASK__WAKEUP_METHODDEF
+ _ASYNCIO_TASK__STEP_METHODDEF
+ _ASYNCIO_TASK__REPR_INFO_METHODDEF
+ {NULL, NULL} /* Sentinel */
+};
+
+static PyGetSetDef TaskType_getsetlist[] = {
+ FUTURE_COMMON_GETSETLIST
+ {"_log_destroy_pending", (getter)TaskObj_get_log_destroy_pending,
+ (setter)TaskObj_set_log_destroy_pending, NULL},
+ {"_must_cancel", (getter)TaskObj_get_must_cancel, NULL, NULL},
+ {"_coro", (getter)TaskObj_get_coro, NULL, NULL},
+ {"_fut_waiter", (getter)TaskObj_get_fut_waiter, NULL, NULL},
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject TaskType = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "_asyncio.Task",
+ sizeof(TaskObj), /* tp_basicsize */
+ .tp_base = &FutureType,
+ .tp_dealloc = TaskObj_dealloc,
+ .tp_as_async = &FutureType_as_async,
+ .tp_repr = (reprfunc)FutureObj_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE
+ | Py_TPFLAGS_HAVE_FINALIZE,
+ .tp_doc = _asyncio_Task___init____doc__,
+ .tp_traverse = (traverseproc)TaskObj_traverse,
+ .tp_clear = (inquiry)TaskObj_clear,
+ .tp_weaklistoffset = offsetof(TaskObj, task_weakreflist),
+ .tp_iter = (getiterfunc)future_new_iter,
+ .tp_methods = TaskType_methods,
+ .tp_getset = TaskType_getsetlist,
+ .tp_dictoffset = offsetof(TaskObj, dict),
+ .tp_init = (initproc)_asyncio_Task___init__,
+ .tp_new = PyType_GenericNew,
+ .tp_finalize = (destructor)TaskObj_finalize,
+};
+
+#define Task_CheckExact(obj) (Py_TYPE(obj) == &TaskType)
+
+static void
+TaskObj_dealloc(PyObject *self)
+{
+ TaskObj *task = (TaskObj *)self;
+
+ if (Task_CheckExact(self)) {
+ /* When fut is subclass of Task, finalizer is called from
+ * subtype_dealloc.
+ */
+ if (PyObject_CallFinalizerFromDealloc(self) < 0) {
+ // resurrected.
+ return;
+ }
+ }
+
+ if (task->task_weakreflist != NULL) {
+ PyObject_ClearWeakRefs(self);
+ }
+
+ (void)TaskObj_clear(task);
+ Py_TYPE(task)->tp_free(task);
+}
+
+static inline PyObject *
+task_call_wakeup(TaskObj *task, PyObject *fut)
+{
+ if (Task_CheckExact(task)) {
+ return task_wakeup(task, fut);
+ }
+ else {
+ /* `task` is a subclass of Task */
+ return _PyObject_CallMethodId(
+ (PyObject*)task, &PyId__wakeup, "O", fut, NULL);
+ }
+}
+
+static inline PyObject *
+task_call_step(TaskObj *task, PyObject *arg)
+{
+ if (Task_CheckExact(task)) {
+ return task_step(task, arg);
+ }
+ else {
+ /* `task` is a subclass of Task */
+ if (arg == NULL) {
+ arg = Py_None;
+ }
+ return _PyObject_CallMethodId(
+ (PyObject*)task, &PyId__step, "O", arg, NULL);
+ }
+}
+
+static int
+task_call_step_soon(TaskObj *task, PyObject *arg)
+{
+ PyObject *handle;
+
+ PyObject *cb = TaskSendMethWrapper_new(task, arg);
+ if (cb == NULL) {
+ return -1;
+ }
+
+ handle = _PyObject_CallMethodId(
+ task->task_loop, &PyId_call_soon, "O", cb, NULL);
+ Py_DECREF(cb);
+ if (handle == NULL) {
+ return -1;
+ }
+
+ Py_DECREF(handle);
+ return 0;
+}
+
+static PyObject *
+task_set_error_soon(TaskObj *task, PyObject *et, const char *format, ...)
+{
+ PyObject* msg;
+
+ va_list vargs;
+#ifdef HAVE_STDARG_PROTOTYPES
+ va_start(vargs, format);
+#else
+ va_start(vargs);
+#endif
+ msg = PyUnicode_FromFormatV(format, vargs);
+ va_end(vargs);
+
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ PyObject *e = PyObject_CallFunctionObjArgs(et, msg, NULL);
+ Py_DECREF(msg);
+ if (e == NULL) {
+ return NULL;
+ }
+
+ if (task_call_step_soon(task, e) == -1) {
+ Py_DECREF(e);
+ return NULL;
+ }
+
+ Py_DECREF(e);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+task_step_impl(TaskObj *task, PyObject *exc)
+{
+ int res;
+ int clear_exc = 0;
+ PyObject *result = NULL;
+ PyObject *coro = task->task_coro;
+ PyObject *o;
+
+ if (task->task_state != STATE_PENDING) {
+ PyErr_Format(PyExc_AssertionError,
+ "_step(): already done: %R %R",
+ task,
+ exc ? exc : Py_None);
goto fail;
}
- Py_DECREF(module);
- module = PyImport_ImportModule("asyncio.futures");
- if (module == NULL) {
+ if (task->task_must_cancel) {
+ assert(exc != Py_None);
+
+ if (exc) {
+ /* Check if exc is a CancelledError */
+ res = PyObject_IsInstance(exc, asyncio_CancelledError);
+ if (res == -1) {
+ /* An error occurred, abort */
+ goto fail;
+ }
+ if (res == 0) {
+ /* exc is not CancelledError; reset it to NULL */
+ exc = NULL;
+ }
+ }
+
+ if (!exc) {
+ /* exc was not a CancelledError */
+ exc = PyObject_CallFunctionObjArgs(asyncio_CancelledError, NULL);
+ if (!exc) {
+ goto fail;
+ }
+ clear_exc = 1;
+ }
+
+ task->task_must_cancel = 0;
+ }
+
+ Py_CLEAR(task->task_fut_waiter);
+
+ if (exc == NULL) {
+ if (PyGen_CheckExact(coro) || PyCoro_CheckExact(coro)) {
+ result = _PyGen_Send((PyGenObject*)coro, Py_None);
+ }
+ else {
+ result = _PyObject_CallMethodIdObjArgs(
+ coro, &PyId_send, Py_None, NULL);
+ }
+ }
+ else {
+ result = _PyObject_CallMethodIdObjArgs(
+ coro, &PyId_throw, exc, NULL);
+ if (clear_exc) {
+ /* We created 'exc' during this call */
+ Py_CLEAR(exc);
+ }
+ }
+
+ if (result == NULL) {
+ PyObject *et, *ev, *tb;
+
+ if (_PyGen_FetchStopIterationValue(&o) == 0) {
+ /* The error is StopIteration and that means that
+ the underlying coroutine has resolved */
+ PyObject *res = future_set_result((FutureObj*)task, o);
+ Py_DECREF(o);
+ if (res == NULL) {
+ return NULL;
+ }
+ Py_DECREF(res);
+ Py_RETURN_NONE;
+ }
+
+ if (PyErr_ExceptionMatches(asyncio_CancelledError)) {
+ /* CancelledError */
+ PyErr_Clear();
+ return future_cancel((FutureObj*)task);
+ }
+
+ /* Some other exception; pop it and call Task.set_exception() */
+ PyErr_Fetch(&et, &ev, &tb);
+ assert(et);
+ if (!ev || !PyObject_TypeCheck(ev, (PyTypeObject *) et)) {
+ PyErr_NormalizeException(&et, &ev, &tb);
+ }
+ o = future_set_exception((FutureObj*)task, ev);
+ if (!o) {
+ /* An exception in Task.set_exception() */
+ Py_XDECREF(et);
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+ goto fail;
+ }
+ assert(o == Py_None);
+ Py_CLEAR(o);
+
+ if (!PyErr_GivenExceptionMatches(et, PyExc_Exception)) {
+ /* We've got a BaseException; re-raise it */
+ PyErr_Restore(et, ev, tb);
+ goto fail;
+ }
+
+ Py_XDECREF(et);
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+
+ Py_RETURN_NONE;
+ }
+
+ if (result == (PyObject*)task) {
+ /* We have a task that wants to await on itself */
+ goto self_await;
+ }
+
+ /* Check if `result` is FutureObj or TaskObj (and not a subclass) */
+ if (Future_CheckExact(result) || Task_CheckExact(result)) {
+ PyObject *wrapper;
+ PyObject *res;
+ FutureObj *fut = (FutureObj*)result;
+
+ /* Check if `result` future is attached to a different loop */
+ if (fut->fut_loop != task->task_loop) {
+ goto different_loop;
+ }
+
+ if (fut->fut_blocking) {
+ fut->fut_blocking = 0;
+
+ /* result.add_done_callback(task._wakeup) */
+ wrapper = TaskWakeupMethWrapper_new(task);
+ if (wrapper == NULL) {
+ goto fail;
+ }
+ res = future_add_done_callback((FutureObj*)result, wrapper);
+ Py_DECREF(wrapper);
+ if (res == NULL) {
+ goto fail;
+ }
+ Py_DECREF(res);
+
+ /* task._fut_waiter = result */
+ task->task_fut_waiter = result; /* no incref is necessary */
+
+ if (task->task_must_cancel) {
+ PyObject *r;
+ r = future_cancel(fut);
+ if (r == NULL) {
+ return NULL;
+ }
+ if (r == Py_True) {
+ task->task_must_cancel = 0;
+ }
+ Py_DECREF(r);
+ }
+
+ Py_RETURN_NONE;
+ }
+ else {
+ goto yield_insteadof_yf;
+ }
+ }
+
+ /* Check if `result` is a Future-compatible object */
+ o = PyObject_GetAttrString(result, "_asyncio_future_blocking");
+ if (o == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ }
+ else {
+ goto fail;
+ }
+ }
+ else {
+ if (o == Py_None) {
+ Py_CLEAR(o);
+ }
+ else {
+ /* `result` is a Future-compatible object */
+ PyObject *wrapper;
+ PyObject *res;
+
+ int blocking = PyObject_IsTrue(o);
+ Py_CLEAR(o);
+ if (blocking < 0) {
+ goto fail;
+ }
+
+ /* Check if `result` future is attached to a different loop */
+ PyObject *oloop = PyObject_GetAttrString(result, "_loop");
+ if (oloop == NULL) {
+ goto fail;
+ }
+ if (oloop != task->task_loop) {
+ Py_DECREF(oloop);
+ goto different_loop;
+ }
+ else {
+ Py_DECREF(oloop);
+ }
+
+ if (blocking) {
+ /* result._asyncio_future_blocking = False */
+ if (PyObject_SetAttrString(
+ result, "_asyncio_future_blocking", Py_False) == -1) {
+ goto fail;
+ }
+
+ /* result.add_done_callback(task._wakeup) */
+ wrapper = TaskWakeupMethWrapper_new(task);
+ if (wrapper == NULL) {
+ goto fail;
+ }
+ res = _PyObject_CallMethodId(
+ result, &PyId_add_done_callback, "O", wrapper, NULL);
+ Py_DECREF(wrapper);
+ if (res == NULL) {
+ goto fail;
+ }
+ Py_DECREF(res);
+
+ /* task._fut_waiter = result */
+ task->task_fut_waiter = result; /* no incref is necessary */
+
+ if (task->task_must_cancel) {
+ PyObject *r;
+ int is_true;
+ r = _PyObject_CallMethodId(result, &PyId_cancel, NULL);
+ if (r == NULL) {
+ return NULL;
+ }
+ is_true = PyObject_IsTrue(r);
+ Py_DECREF(r);
+ if (is_true < 0) {
+ return NULL;
+ }
+ else if (is_true) {
+ task->task_must_cancel = 0;
+ }
+ }
+
+ Py_RETURN_NONE;
+ }
+ else {
+ goto yield_insteadof_yf;
+ }
+ }
+ }
+
+ /* Check if `result` is None */
+ if (result == Py_None) {
+ /* Bare yield relinquishes control for one event loop iteration. */
+ if (task_call_step_soon(task, NULL)) {
+ goto fail;
+ }
+ return result;
+ }
+
+ /* Check if `result` is a generator */
+ o = PyObject_CallFunctionObjArgs(inspect_isgenerator, result, NULL);
+ if (o == NULL) {
+ /* An exception in inspect.isgenerator */
goto fail;
}
- asyncio_repr_info_func = PyObject_GetAttrString(module,
- "_future_repr_info");
- if (asyncio_repr_info_func == NULL) {
+ res = PyObject_IsTrue(o);
+ Py_CLEAR(o);
+ if (res == -1) {
+ /* An exception while checking if 'val' is True */
goto fail;
}
+ if (res == 1) {
+ /* `result` is a generator */
+ PyObject *ret;
+ ret = task_set_error_soon(
+ task, PyExc_RuntimeError,
+ "yield was used instead of yield from for "
+ "generator in task %R with %S", task, result);
+ Py_DECREF(result);
+ return ret;
+ }
+
+ /* The `result` is none of the above */
+ Py_DECREF(result);
+ return task_set_error_soon(
+ task, PyExc_RuntimeError, "Task got bad yield: %R", result);
+
+self_await:
+ o = task_set_error_soon(
+ task, PyExc_RuntimeError,
+ "Task cannot await on itself: %R", task);
+ Py_DECREF(result);
+ return o;
+
+yield_insteadof_yf:
+ o = task_set_error_soon(
+ task, PyExc_RuntimeError,
+ "yield was used instead of yield from "
+ "in task %R with %R",
+ task, result);
+ Py_DECREF(result);
+ return o;
+
+different_loop:
+ o = task_set_error_soon(
+ task, PyExc_RuntimeError,
+ "Task %R got Future %R attached to a different loop",
+ task, result);
+ Py_DECREF(result);
+ return o;
- asyncio_InvalidStateError = PyObject_GetAttrString(module,
- "InvalidStateError");
- if (asyncio_InvalidStateError == NULL) {
- goto fail;
+fail:
+ Py_XDECREF(result);
+ return NULL;
+}
+
+static PyObject *
+task_step(TaskObj *task, PyObject *exc)
+{
+ PyObject *res;
+ PyObject *ot;
+
+ if (PyDict_SetItem((PyObject *)current_tasks,
+ task->task_loop, (PyObject*)task) == -1)
+ {
+ return NULL;
}
- asyncio_CancelledError = PyObject_GetAttrString(module, "CancelledError");
- if (asyncio_CancelledError == NULL) {
- goto fail;
+ res = task_step_impl(task, exc);
+
+ if (res == NULL) {
+ PyObject *et, *ev, *tb;
+ PyErr_Fetch(&et, &ev, &tb);
+ ot = _PyDict_Pop(current_tasks, task->task_loop, NULL);
+ if (ot == NULL) {
+ Py_XDECREF(et);
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+ return NULL;
+ }
+ Py_DECREF(ot);
+ PyErr_Restore(et, ev, tb);
+ return NULL;
+ }
+ else {
+ ot = _PyDict_Pop(current_tasks, task->task_loop, NULL);
+ if (ot == NULL) {
+ Py_DECREF(res);
+ return NULL;
+ }
+ else {
+ Py_DECREF(ot);
+ return res;
+ }
}
+}
- Py_DECREF(module);
- return 0;
+static PyObject *
+task_wakeup(TaskObj *task, PyObject *o)
+{
+ assert(o);
-fail:
+ if (Future_CheckExact(o) || Task_CheckExact(o)) {
+ PyObject *fut_result = NULL;
+ int res = future_get_result((FutureObj*)o, &fut_result);
+ PyObject *result;
+
+ switch(res) {
+ case -1:
+ assert(fut_result == NULL);
+ return NULL;
+ case 0:
+ Py_DECREF(fut_result);
+ return task_call_step(task, NULL);
+ default:
+ assert(res == 1);
+ result = task_call_step(task, fut_result);
+ Py_DECREF(fut_result);
+ return result;
+ }
+ }
+
+ PyObject *fut_result = PyObject_CallMethod(o, "result", NULL);
+ if (fut_result == NULL) {
+ PyObject *et, *ev, *tb;
+ PyObject *res;
+
+ PyErr_Fetch(&et, &ev, &tb);
+ if (!ev || !PyObject_TypeCheck(ev, (PyTypeObject *) et)) {
+ PyErr_NormalizeException(&et, &ev, &tb);
+ }
+
+ res = task_call_step(task, ev);
+
+ Py_XDECREF(et);
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+
+ return res;
+ }
+ else {
+ Py_DECREF(fut_result);
+ return task_call_step(task, NULL);
+ }
+}
+
+
+/*********************** Module **************************/
+
+
+static void
+module_free(void *m)
+{
+ Py_CLEAR(current_tasks);
+ Py_CLEAR(all_tasks);
Py_CLEAR(traceback_extract_stack);
Py_CLEAR(asyncio_get_event_loop);
- Py_CLEAR(asyncio_repr_info_func);
+ Py_CLEAR(asyncio_future_repr_info_func);
+ Py_CLEAR(asyncio_task_repr_info_func);
+ Py_CLEAR(asyncio_task_get_stack_func);
+ Py_CLEAR(asyncio_task_print_stack_func);
Py_CLEAR(asyncio_InvalidStateError);
Py_CLEAR(asyncio_CancelledError);
+ Py_CLEAR(inspect_isgenerator);
+}
+
+static int
+module_init(void)
+{
+ PyObject *module = NULL;
+ PyObject *cls;
+
+#define WITH_MOD(NAME) \
+ Py_CLEAR(module); \
+ module = PyImport_ImportModule(NAME); \
+ if (module == NULL) { \
+ return -1; \
+ }
+
+#define GET_MOD_ATTR(VAR, NAME) \
+ VAR = PyObject_GetAttrString(module, NAME); \
+ if (VAR == NULL) { \
+ goto fail; \
+ }
+
+ WITH_MOD("asyncio.events")
+ GET_MOD_ATTR(asyncio_get_event_loop, "get_event_loop")
+
+ WITH_MOD("asyncio.base_futures")
+ GET_MOD_ATTR(asyncio_future_repr_info_func, "_future_repr_info")
+ GET_MOD_ATTR(asyncio_InvalidStateError, "InvalidStateError")
+ GET_MOD_ATTR(asyncio_CancelledError, "CancelledError")
+
+ WITH_MOD("asyncio.base_tasks")
+ GET_MOD_ATTR(asyncio_task_repr_info_func, "_task_repr_info")
+ GET_MOD_ATTR(asyncio_task_get_stack_func, "_task_get_stack")
+ GET_MOD_ATTR(asyncio_task_print_stack_func, "_task_print_stack")
+
+ WITH_MOD("inspect")
+ GET_MOD_ATTR(inspect_isgenerator, "isgenerator")
+
+ WITH_MOD("traceback")
+ GET_MOD_ATTR(traceback_extract_stack, "extract_stack")
+
+ WITH_MOD("weakref")
+ GET_MOD_ATTR(cls, "WeakSet")
+ all_tasks = PyObject_CallObject(cls, NULL);
+ Py_CLEAR(cls);
+ if (all_tasks == NULL) {
+ goto fail;
+ }
+
+ current_tasks = (PyDictObject *)PyDict_New();
+ if (current_tasks == NULL) {
+ goto fail;
+ }
+
Py_CLEAR(module);
+ return 0;
+
+fail:
+ Py_CLEAR(module);
+ module_free(NULL);
return -1;
-}
+#undef WITH_MOD
+#undef GET_MOD_ATTR
+}
PyDoc_STRVAR(module_doc, "Accelerator module for asyncio");
@@ -1006,14 +2413,14 @@ static struct PyModuleDef _asynciomodule = {
NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
- NULL, /* m_free */
+ (freefunc)module_free /* m_free */
};
PyMODINIT_FUNC
PyInit__asyncio(void)
{
- if (init_module() < 0) {
+ if (module_init() < 0) {
return NULL;
}
if (PyType_Ready(&FutureType) < 0) {
@@ -1022,6 +2429,15 @@ PyInit__asyncio(void)
if (PyType_Ready(&FutureIterType) < 0) {
return NULL;
}
+ if (PyType_Ready(&TaskSendMethWrapper_Type) < 0) {
+ return NULL;
+ }
+ if(PyType_Ready(&TaskWakeupMethWrapper_Type) < 0) {
+ return NULL;
+ }
+ if (PyType_Ready(&TaskType) < 0) {
+ return NULL;
+ }
PyObject *m = PyModule_Create(&_asynciomodule);
if (m == NULL) {
@@ -1034,5 +2450,11 @@ PyInit__asyncio(void)
return NULL;
}
+ Py_INCREF(&TaskType);
+ if (PyModule_AddObject(m, "Task", (PyObject *)&TaskType) < 0) {
+ Py_DECREF(&TaskType);
+ return NULL;
+ }
+
return m;
}
diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h
new file mode 100644
index 0000000..052d252
--- /dev/null
+++ b/Modules/clinic/_asynciomodule.c.h
@@ -0,0 +1,520 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(_asyncio_Future___init____doc__,
+"Future(*, loop=None)\n"
+"--\n"
+"\n"
+"This class is *almost* compatible with concurrent.futures.Future.\n"
+"\n"
+" Differences:\n"
+"\n"
+" - result() and exception() do not take a timeout argument and\n"
+" raise an exception when the future isn\'t done yet.\n"
+"\n"
+" - Callbacks registered with add_done_callback() are always called\n"
+" via the event loop\'s call_soon_threadsafe().\n"
+"\n"
+" - This class is not compatible with the wait() and as_completed()\n"
+" methods in the concurrent.futures package.");
+
+static int
+_asyncio_Future___init___impl(FutureObj *self, PyObject *loop);
+
+static int
+_asyncio_Future___init__(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ int return_value = -1;
+ static const char * const _keywords[] = {"loop", NULL};
+ static _PyArg_Parser _parser = {"|$O:Future", _keywords, 0};
+ PyObject *loop = NULL;
+
+ if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
+ &loop)) {
+ goto exit;
+ }
+ return_value = _asyncio_Future___init___impl((FutureObj *)self, loop);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_asyncio_Future_result__doc__,
+"result($self, /)\n"
+"--\n"
+"\n"
+"Return the result this future represents.\n"
+"\n"
+"If the future has been cancelled, raises CancelledError. If the\n"
+"future\'s result isn\'t yet available, raises InvalidStateError. If\n"
+"the future is done and has an exception set, this exception is raised.");
+
+#define _ASYNCIO_FUTURE_RESULT_METHODDEF \
+ {"result", (PyCFunction)_asyncio_Future_result, METH_NOARGS, _asyncio_Future_result__doc__},
+
+static PyObject *
+_asyncio_Future_result_impl(FutureObj *self);
+
+static PyObject *
+_asyncio_Future_result(FutureObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Future_result_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Future_exception__doc__,
+"exception($self, /)\n"
+"--\n"
+"\n"
+"Return the exception that was set on this future.\n"
+"\n"
+"The exception (or None if no exception was set) is returned only if\n"
+"the future is done. If the future has been cancelled, raises\n"
+"CancelledError. If the future isn\'t done yet, raises\n"
+"InvalidStateError.");
+
+#define _ASYNCIO_FUTURE_EXCEPTION_METHODDEF \
+ {"exception", (PyCFunction)_asyncio_Future_exception, METH_NOARGS, _asyncio_Future_exception__doc__},
+
+static PyObject *
+_asyncio_Future_exception_impl(FutureObj *self);
+
+static PyObject *
+_asyncio_Future_exception(FutureObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Future_exception_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Future_set_result__doc__,
+"set_result($self, res, /)\n"
+"--\n"
+"\n"
+"Mark the future done and set its result.\n"
+"\n"
+"If the future is already done when this method is called, raises\n"
+"InvalidStateError.");
+
+#define _ASYNCIO_FUTURE_SET_RESULT_METHODDEF \
+ {"set_result", (PyCFunction)_asyncio_Future_set_result, METH_O, _asyncio_Future_set_result__doc__},
+
+PyDoc_STRVAR(_asyncio_Future_set_exception__doc__,
+"set_exception($self, exception, /)\n"
+"--\n"
+"\n"
+"Mark the future done and set an exception.\n"
+"\n"
+"If the future is already done when this method is called, raises\n"
+"InvalidStateError.");
+
+#define _ASYNCIO_FUTURE_SET_EXCEPTION_METHODDEF \
+ {"set_exception", (PyCFunction)_asyncio_Future_set_exception, METH_O, _asyncio_Future_set_exception__doc__},
+
+PyDoc_STRVAR(_asyncio_Future_add_done_callback__doc__,
+"add_done_callback($self, fn, /)\n"
+"--\n"
+"\n"
+"Add a callback to be run when the future becomes done.\n"
+"\n"
+"The callback is called with a single argument - the future object. If\n"
+"the future is already done when this is called, the callback is\n"
+"scheduled with call_soon.");
+
+#define _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF \
+ {"add_done_callback", (PyCFunction)_asyncio_Future_add_done_callback, METH_O, _asyncio_Future_add_done_callback__doc__},
+
+PyDoc_STRVAR(_asyncio_Future_remove_done_callback__doc__,
+"remove_done_callback($self, fn, /)\n"
+"--\n"
+"\n"
+"Remove all instances of a callback from the \"call when done\" list.\n"
+"\n"
+"Returns the number of callbacks removed.");
+
+#define _ASYNCIO_FUTURE_REMOVE_DONE_CALLBACK_METHODDEF \
+ {"remove_done_callback", (PyCFunction)_asyncio_Future_remove_done_callback, METH_O, _asyncio_Future_remove_done_callback__doc__},
+
+PyDoc_STRVAR(_asyncio_Future_cancel__doc__,
+"cancel($self, /)\n"
+"--\n"
+"\n"
+"Cancel the future and schedule callbacks.\n"
+"\n"
+"If the future is already done or cancelled, return False. Otherwise,\n"
+"change the future\'s state to cancelled, schedule the callbacks and\n"
+"return True.");
+
+#define _ASYNCIO_FUTURE_CANCEL_METHODDEF \
+ {"cancel", (PyCFunction)_asyncio_Future_cancel, METH_NOARGS, _asyncio_Future_cancel__doc__},
+
+static PyObject *
+_asyncio_Future_cancel_impl(FutureObj *self);
+
+static PyObject *
+_asyncio_Future_cancel(FutureObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Future_cancel_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Future_cancelled__doc__,
+"cancelled($self, /)\n"
+"--\n"
+"\n"
+"Return True if the future was cancelled.");
+
+#define _ASYNCIO_FUTURE_CANCELLED_METHODDEF \
+ {"cancelled", (PyCFunction)_asyncio_Future_cancelled, METH_NOARGS, _asyncio_Future_cancelled__doc__},
+
+static PyObject *
+_asyncio_Future_cancelled_impl(FutureObj *self);
+
+static PyObject *
+_asyncio_Future_cancelled(FutureObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Future_cancelled_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Future_done__doc__,
+"done($self, /)\n"
+"--\n"
+"\n"
+"Return True if the future is done.\n"
+"\n"
+"Done means either that a result / exception are available, or that the\n"
+"future was cancelled.");
+
+#define _ASYNCIO_FUTURE_DONE_METHODDEF \
+ {"done", (PyCFunction)_asyncio_Future_done, METH_NOARGS, _asyncio_Future_done__doc__},
+
+static PyObject *
+_asyncio_Future_done_impl(FutureObj *self);
+
+static PyObject *
+_asyncio_Future_done(FutureObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Future_done_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Future__repr_info__doc__,
+"_repr_info($self, /)\n"
+"--\n"
+"\n");
+
+#define _ASYNCIO_FUTURE__REPR_INFO_METHODDEF \
+ {"_repr_info", (PyCFunction)_asyncio_Future__repr_info, METH_NOARGS, _asyncio_Future__repr_info__doc__},
+
+static PyObject *
+_asyncio_Future__repr_info_impl(FutureObj *self);
+
+static PyObject *
+_asyncio_Future__repr_info(FutureObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Future__repr_info_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Future__schedule_callbacks__doc__,
+"_schedule_callbacks($self, /)\n"
+"--\n"
+"\n");
+
+#define _ASYNCIO_FUTURE__SCHEDULE_CALLBACKS_METHODDEF \
+ {"_schedule_callbacks", (PyCFunction)_asyncio_Future__schedule_callbacks, METH_NOARGS, _asyncio_Future__schedule_callbacks__doc__},
+
+static PyObject *
+_asyncio_Future__schedule_callbacks_impl(FutureObj *self);
+
+static PyObject *
+_asyncio_Future__schedule_callbacks(FutureObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Future__schedule_callbacks_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Task___init____doc__,
+"Task(coro, *, loop=None)\n"
+"--\n"
+"\n"
+"A coroutine wrapped in a Future.");
+
+static int
+_asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop);
+
+static int
+_asyncio_Task___init__(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ int return_value = -1;
+ static const char * const _keywords[] = {"coro", "loop", NULL};
+ static _PyArg_Parser _parser = {"O|$O:Task", _keywords, 0};
+ PyObject *coro;
+ PyObject *loop = NULL;
+
+ if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
+ &coro, &loop)) {
+ goto exit;
+ }
+ return_value = _asyncio_Task___init___impl((TaskObj *)self, coro, loop);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_asyncio_Task_current_task__doc__,
+"current_task($type, /, loop=None)\n"
+"--\n"
+"\n"
+"Return the currently running task in an event loop or None.\n"
+"\n"
+"By default the current task for the current event loop is returned.\n"
+"\n"
+"None is returned when called not in the context of a Task.");
+
+#define _ASYNCIO_TASK_CURRENT_TASK_METHODDEF \
+ {"current_task", (PyCFunction)_asyncio_Task_current_task, METH_FASTCALL|METH_CLASS, _asyncio_Task_current_task__doc__},
+
+static PyObject *
+_asyncio_Task_current_task_impl(PyTypeObject *type, PyObject *loop);
+
+static PyObject *
+_asyncio_Task_current_task(PyTypeObject *type, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"loop", NULL};
+ static _PyArg_Parser _parser = {"|O:current_task", _keywords, 0};
+ PyObject *loop = NULL;
+
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
+ &loop)) {
+ goto exit;
+ }
+ return_value = _asyncio_Task_current_task_impl(type, loop);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_asyncio_Task_all_tasks__doc__,
+"all_tasks($type, /, loop=None)\n"
+"--\n"
+"\n"
+"Return a set of all tasks for an event loop.\n"
+"\n"
+"By default all tasks for the current event loop are returned.");
+
+#define _ASYNCIO_TASK_ALL_TASKS_METHODDEF \
+ {"all_tasks", (PyCFunction)_asyncio_Task_all_tasks, METH_FASTCALL|METH_CLASS, _asyncio_Task_all_tasks__doc__},
+
+static PyObject *
+_asyncio_Task_all_tasks_impl(PyTypeObject *type, PyObject *loop);
+
+static PyObject *
+_asyncio_Task_all_tasks(PyTypeObject *type, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"loop", NULL};
+ static _PyArg_Parser _parser = {"|O:all_tasks", _keywords, 0};
+ PyObject *loop = NULL;
+
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
+ &loop)) {
+ goto exit;
+ }
+ return_value = _asyncio_Task_all_tasks_impl(type, loop);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_asyncio_Task__repr_info__doc__,
+"_repr_info($self, /)\n"
+"--\n"
+"\n");
+
+#define _ASYNCIO_TASK__REPR_INFO_METHODDEF \
+ {"_repr_info", (PyCFunction)_asyncio_Task__repr_info, METH_NOARGS, _asyncio_Task__repr_info__doc__},
+
+static PyObject *
+_asyncio_Task__repr_info_impl(TaskObj *self);
+
+static PyObject *
+_asyncio_Task__repr_info(TaskObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Task__repr_info_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Task_cancel__doc__,
+"cancel($self, /)\n"
+"--\n"
+"\n"
+"Request that this task cancel itself.\n"
+"\n"
+"This arranges for a CancelledError to be thrown into the\n"
+"wrapped coroutine on the next cycle through the event loop.\n"
+"The coroutine then has a chance to clean up or even deny\n"
+"the request using try/except/finally.\n"
+"\n"
+"Unlike Future.cancel, this does not guarantee that the\n"
+"task will be cancelled: the exception might be caught and\n"
+"acted upon, delaying cancellation of the task or preventing\n"
+"cancellation completely. The task may also return a value or\n"
+"raise a different exception.\n"
+"\n"
+"Immediately after this method is called, Task.cancelled() will\n"
+"not return True (unless the task was already cancelled). A\n"
+"task will be marked as cancelled when the wrapped coroutine\n"
+"terminates with a CancelledError exception (even if cancel()\n"
+"was not called).");
+
+#define _ASYNCIO_TASK_CANCEL_METHODDEF \
+ {"cancel", (PyCFunction)_asyncio_Task_cancel, METH_NOARGS, _asyncio_Task_cancel__doc__},
+
+static PyObject *
+_asyncio_Task_cancel_impl(TaskObj *self);
+
+static PyObject *
+_asyncio_Task_cancel(TaskObj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _asyncio_Task_cancel_impl(self);
+}
+
+PyDoc_STRVAR(_asyncio_Task_get_stack__doc__,
+"get_stack($self, /, *, limit=None)\n"
+"--\n"
+"\n"
+"Return the list of stack frames for this task\'s coroutine.\n"
+"\n"
+"If the coroutine is not done, this returns the stack where it is\n"
+"suspended. If the coroutine has completed successfully or was\n"
+"cancelled, this returns an empty list. If the coroutine was\n"
+"terminated by an exception, this returns the list of traceback\n"
+"frames.\n"
+"\n"
+"The frames are always ordered from oldest to newest.\n"
+"\n"
+"The optional limit gives the maximum number of frames to\n"
+"return; by default all available frames are returned. Its\n"
+"meaning differs depending on whether a stack or a traceback is\n"
+"returned: the newest frames of a stack are returned, but the\n"
+"oldest frames of a traceback are returned. (This matches the\n"
+"behavior of the traceback module.)\n"
+"\n"
+"For reasons beyond our control, only one stack frame is\n"
+"returned for a suspended coroutine.");
+
+#define _ASYNCIO_TASK_GET_STACK_METHODDEF \
+ {"get_stack", (PyCFunction)_asyncio_Task_get_stack, METH_FASTCALL, _asyncio_Task_get_stack__doc__},
+
+static PyObject *
+_asyncio_Task_get_stack_impl(TaskObj *self, PyObject *limit);
+
+static PyObject *
+_asyncio_Task_get_stack(TaskObj *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"limit", NULL};
+ static _PyArg_Parser _parser = {"|$O:get_stack", _keywords, 0};
+ PyObject *limit = Py_None;
+
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
+ &limit)) {
+ goto exit;
+ }
+ return_value = _asyncio_Task_get_stack_impl(self, limit);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_asyncio_Task_print_stack__doc__,
+"print_stack($self, /, *, limit=None, file=None)\n"
+"--\n"
+"\n"
+"Print the stack or traceback for this task\'s coroutine.\n"
+"\n"
+"This produces output similar to that of the traceback module,\n"
+"for the frames retrieved by get_stack(). The limit argument\n"
+"is passed to get_stack(). The file argument is an I/O stream\n"
+"to which the output is written; by default output is written\n"
+"to sys.stderr.");
+
+#define _ASYNCIO_TASK_PRINT_STACK_METHODDEF \
+ {"print_stack", (PyCFunction)_asyncio_Task_print_stack, METH_FASTCALL, _asyncio_Task_print_stack__doc__},
+
+static PyObject *
+_asyncio_Task_print_stack_impl(TaskObj *self, PyObject *limit,
+ PyObject *file);
+
+static PyObject *
+_asyncio_Task_print_stack(TaskObj *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"limit", "file", NULL};
+ static _PyArg_Parser _parser = {"|$OO:print_stack", _keywords, 0};
+ PyObject *limit = Py_None;
+ PyObject *file = Py_None;
+
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
+ &limit, &file)) {
+ goto exit;
+ }
+ return_value = _asyncio_Task_print_stack_impl(self, limit, file);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_asyncio_Task__step__doc__,
+"_step($self, /, exc=None)\n"
+"--\n"
+"\n");
+
+#define _ASYNCIO_TASK__STEP_METHODDEF \
+ {"_step", (PyCFunction)_asyncio_Task__step, METH_FASTCALL, _asyncio_Task__step__doc__},
+
+static PyObject *
+_asyncio_Task__step_impl(TaskObj *self, PyObject *exc);
+
+static PyObject *
+_asyncio_Task__step(TaskObj *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"exc", NULL};
+ static _PyArg_Parser _parser = {"|O:_step", _keywords, 0};
+ PyObject *exc = NULL;
+
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
+ &exc)) {
+ goto exit;
+ }
+ return_value = _asyncio_Task__step_impl(self, exc);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_asyncio_Task__wakeup__doc__,
+"_wakeup($self, /, fut)\n"
+"--\n"
+"\n");
+
+#define _ASYNCIO_TASK__WAKEUP_METHODDEF \
+ {"_wakeup", (PyCFunction)_asyncio_Task__wakeup, METH_FASTCALL, _asyncio_Task__wakeup__doc__},
+
+static PyObject *
+_asyncio_Task__wakeup_impl(TaskObj *self, PyObject *fut);
+
+static PyObject *
+_asyncio_Task__wakeup(TaskObj *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"fut", NULL};
+ static _PyArg_Parser _parser = {"O:_wakeup", _keywords, 0};
+ PyObject *fut;
+
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
+ &fut)) {
+ goto exit;
+ }
+ return_value = _asyncio_Task__wakeup_impl(self, fut);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=8f036321bb083066 input=a9049054013a1b77]*/