Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a0c1ba6

Browse files
committed
Issue #28544: Implement asyncio.Task in C.
This implementation provides additional 10-20% speed boost for asyncio programs. The patch also fixes _asynciomodule.c to use Arguments Clinic, and makes '_schedule_callbacks' an overridable method (as it was in 3.5).
1 parent bbcb799 commit a0c1ba6

10 files changed

Lines changed: 2746 additions & 586 deletions

File tree

Lib/asyncio/base_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757

5858
def _format_handle(handle):
5959
cb = handle._callback
60-
if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task):
60+
if isinstance(getattr(cb, '__self__', None), tasks.Task):
6161
# format the task
6262
return repr(cb.__self__)
6363
else:

Lib/asyncio/base_futures.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
__all__ = []
2+
3+
import concurrent.futures._base
4+
import reprlib
5+
6+
from . import events
7+
8+
Error = concurrent.futures._base.Error
9+
CancelledError = concurrent.futures.CancelledError
10+
TimeoutError = concurrent.futures.TimeoutError
11+
12+
13+
class InvalidStateError(Error):
14+
"""The operation is not allowed in this state."""
15+
16+
17+
# States for Future.
18+
_PENDING = 'PENDING'
19+
_CANCELLED = 'CANCELLED'
20+
_FINISHED = 'FINISHED'
21+
22+
23+
def isfuture(obj):
24+
"""Check for a Future.
25+
26+
This returns True when obj is a Future instance or is advertising
27+
itself as duck-type compatible by setting _asyncio_future_blocking.
28+
See comment in Future for more details.
29+
"""
30+
return getattr(obj, '_asyncio_future_blocking', None) is not None
31+
32+
33+
def _format_callbacks(cb):
34+
"""helper function for Future.__repr__"""
35+
size = len(cb)
36+
if not size:
37+
cb = ''
38+
39+
def format_cb(callback):
40+
return events._format_callback_source(callback, ())
41+
42+
if size == 1:
43+
cb = format_cb(cb[0])
44+
elif size == 2:
45+
cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
46+
elif size > 2:
47+
cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
48+
size - 2,
49+
format_cb(cb[-1]))
50+
return 'cb=[%s]' % cb
51+
52+
53+
def _future_repr_info(future):
54+
# (Future) -> str
55+
"""helper function for Future.__repr__"""
56+
info = [future._state.lower()]
57+
if future._state == _FINISHED:
58+
if future._exception is not None:
59+
info.append('exception={!r}'.format(future._exception))
60+
else:
61+
# use reprlib to limit the length of the output, especially
62+
# for very long strings
63+
result = reprlib.repr(future._result)
64+
info.append('result={}'.format(result))
65+
if future._callbacks:
66+
info.append(_format_callbacks(future._callbacks))
67+
if future._source_traceback:
68+
frame = future._source_traceback[-1]
69+
info.append('created at %s:%s' % (frame[0], frame[1]))
70+
return info

Lib/asyncio/base_tasks.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import linecache
2+
import traceback
3+
4+
from . import base_futures
5+
from . import coroutines
6+
7+
8+
def _task_repr_info(task):
9+
info = base_futures._future_repr_info(task)
10+
11+
if task._must_cancel:
12+
# replace status
13+
info[0] = 'cancelling'
14+
15+
coro = coroutines._format_coroutine(task._coro)
16+
info.insert(1, 'coro=<%s>' % coro)
17+
18+
if task._fut_waiter is not None:
19+
info.insert(2, 'wait_for=%r' % task._fut_waiter)
20+
return info
21+
22+
23+
def _task_get_stack(task, limit):
24+
frames = []
25+
try:
26+
# 'async def' coroutines
27+
f = task._coro.cr_frame
28+
except AttributeError:
29+
f = task._coro.gi_frame
30+
if f is not None:
31+
while f is not None:
32+
if limit is not None:
33+
if limit <= 0:
34+
break
35+
limit -= 1
36+
frames.append(f)
37+
f = f.f_back
38+
frames.reverse()
39+
elif task._exception is not None:
40+
tb = task._exception.__traceback__
41+
while tb is not None:
42+
if limit is not None:
43+
if limit <= 0:
44+
break
45+
limit -= 1
46+
frames.append(tb.tb_frame)
47+
tb = tb.tb_next
48+
return frames
49+
50+
51+
def _task_print_stack(task, limit, file):
52+
extracted_list = []
53+
checked = set()
54+
for f in task.get_stack(limit=limit):
55+
lineno = f.f_lineno
56+
co = f.f_code
57+
filename = co.co_filename
58+
name = co.co_name
59+
if filename not in checked:
60+
checked.add(filename)
61+
linecache.checkcache(filename)
62+
line = linecache.getline(filename, lineno, f.f_globals)
63+
extracted_list.append((filename, lineno, name, line))
64+
exc = task._exception
65+
if not extracted_list:
66+
print('No stack for %r' % task, file=file)
67+
elif exc is not None:
68+
print('Traceback for %r (most recent call last):' % task,
69+
file=file)
70+
else:
71+
print('Stack for %r (most recent call last):' % task,
72+
file=file)
73+
traceback.print_list(extracted_list, file=file)
74+
if exc is not None:
75+
for line in traceback.format_exception_only(exc.__class__, exc):
76+
print(line, file=file, end='')

Lib/asyncio/coroutines.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from . import compat
1313
from . import events
14-
from . import futures
14+
from . import base_futures
1515
from .log import logger
1616

1717

@@ -204,7 +204,7 @@ def coroutine(func):
204204
@functools.wraps(func)
205205
def coro(*args, **kw):
206206
res = func(*args, **kw)
207-
if (futures.isfuture(res) or inspect.isgenerator(res) or
207+
if (base_futures.isfuture(res) or inspect.isgenerator(res) or
208208
isinstance(res, CoroWrapper)):
209209
res = yield from res
210210
elif _AwaitableABC is not None:

Lib/asyncio/futures.py

Lines changed: 18 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
11
"""A Future class similar to the one in PEP 3148."""
22

3-
__all__ = ['CancelledError', 'TimeoutError',
4-
'InvalidStateError',
5-
'Future', 'wrap_future', 'isfuture'
6-
]
3+
__all__ = ['CancelledError', 'TimeoutError', 'InvalidStateError',
4+
'Future', 'wrap_future', 'isfuture']
75

8-
import concurrent.futures._base
6+
import concurrent.futures
97
import logging
10-
import reprlib
118
import sys
129
import traceback
1310

11+
from . import base_futures
1412
from . import compat
1513
from . import events
1614

17-
# States for Future.
18-
_PENDING = 'PENDING'
19-
_CANCELLED = 'CANCELLED'
20-
_FINISHED = 'FINISHED'
2115

22-
Error = concurrent.futures._base.Error
23-
CancelledError = concurrent.futures.CancelledError
24-
TimeoutError = concurrent.futures.TimeoutError
16+
CancelledError = base_futures.CancelledError
17+
InvalidStateError = base_futures.InvalidStateError
18+
TimeoutError = base_futures.TimeoutError
19+
isfuture = base_futures.isfuture
2520

26-
STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
21+
22+
_PENDING = base_futures._PENDING
23+
_CANCELLED = base_futures._CANCELLED
24+
_FINISHED = base_futures._FINISHED
2725

2826

29-
class InvalidStateError(Error):
30-
"""The operation is not allowed in this state."""
27+
STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
3128

3229

3330
class _TracebackLogger:
@@ -110,56 +107,6 @@ def __del__(self):
110107
self.loop.call_exception_handler({'message': msg})
111108

112109

113-
def isfuture(obj):
114-
"""Check for a Future.
115-
116-
This returns True when obj is a Future instance or is advertising
117-
itself as duck-type compatible by setting _asyncio_future_blocking.
118-
See comment in Future for more details.
119-
"""
120-
return getattr(obj, '_asyncio_future_blocking', None) is not None
121-
122-
123-
def _format_callbacks(cb):
124-
"""helper function for Future.__repr__"""
125-
size = len(cb)
126-
if not size:
127-
cb = ''
128-
129-
def format_cb(callback):
130-
return events._format_callback_source(callback, ())
131-
132-
if size == 1:
133-
cb = format_cb(cb[0])
134-
elif size == 2:
135-
cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
136-
elif size > 2:
137-
cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
138-
size-2,
139-
format_cb(cb[-1]))
140-
return 'cb=[%s]' % cb
141-
142-
143-
def _future_repr_info(future):
144-
# (Future) -> str
145-
"""helper function for Future.__repr__"""
146-
info = [future._state.lower()]
147-
if future._state == _FINISHED:
148-
if future._exception is not None:
149-
info.append('exception={!r}'.format(future._exception))
150-
else:
151-
# use reprlib to limit the length of the output, especially
152-
# for very long strings
153-
result = reprlib.repr(future._result)
154-
info.append('result={}'.format(result))
155-
if future._callbacks:
156-
info.append(_format_callbacks(future._callbacks))
157-
if future._source_traceback:
158-
frame = future._source_traceback[-1]
159-
info.append('created at %s:%s' % (frame[0], frame[1]))
160-
return info
161-
162-
163110
class Future:
164111
"""This class is *almost* compatible with concurrent.futures.Future.
165112
@@ -212,7 +159,7 @@ def __init__(self, *, loop=None):
212159
if self._loop.get_debug():
213160
self._source_traceback = traceback.extract_stack(sys._getframe(1))
214161

215-
_repr_info = _future_repr_info
162+
_repr_info = base_futures._future_repr_info
216163

217164
def __repr__(self):
218165
return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
@@ -247,10 +194,10 @@ def cancel(self):
247194
if self._state != _PENDING:
248195
return False
249196
self._state = _CANCELLED
250-
self.__schedule_callbacks()
197+
self._schedule_callbacks()
251198
return True
252199

253-
def __schedule_callbacks(self):
200+
def _schedule_callbacks(self):
254201
"""Internal: Ask the event loop to call all callbacks.
255202
256203
The callbacks are scheduled to be called as soon as possible. Also
@@ -352,7 +299,7 @@ def set_result(self, result):
352299
raise InvalidStateError('{}: {!r}'.format(self._state, self))
353300
self._result = result
354301
self._state = _FINISHED
355-
self.__schedule_callbacks()
302+
self._schedule_callbacks()
356303

357304
def set_exception(self, exception):
358305
"""Mark the future done and set an exception.
@@ -369,7 +316,7 @@ def set_exception(self, exception):
369316
"and cannot be raised into a Future")
370317
self._exception = exception
371318
self._state = _FINISHED
372-
self.__schedule_callbacks()
319+
self._schedule_callbacks()
373320
if compat.PY34:
374321
self._log_traceback = True
375322
else:

0 commit comments

Comments
 (0)