Skip to content

Commit 1964a8a

Browse files
committed
asyncio: sync with Tulip
- repr(Task) and repr(CoroWrapper) now also includes where these objects were created. If the coroutine is not a generator (don't use "yield from"), use the location of the function, not the location of the coro() wrapper. - Fix create_task(): truncate the traceback to hide the call to create_task().
1 parent 41a93fa commit 1964a8a

File tree

4 files changed

+70
-15
lines changed

4 files changed

+70
-15
lines changed

Lib/asyncio/base_events.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ def create_task(self, coro):
155155
"""Schedule a coroutine object.
156156
157157
Return a task object."""
158-
return tasks.Task(coro, loop=self)
158+
task = tasks.Task(coro, loop=self)
159+
if task._source_traceback:
160+
del task._source_traceback[-1]
161+
return task
159162

160163
def _make_socket_transport(self, sock, protocol, waiter=None, *,
161164
extra=None, server=None):

Lib/asyncio/coroutines.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def yield_from_gen(gen):
5757

5858

5959
class CoroWrapper:
60-
# Wrapper for coroutine in _DEBUG mode.
60+
# Wrapper for coroutine object in _DEBUG mode.
6161

6262
def __init__(self, gen, func):
6363
assert inspect.isgenerator(gen), gen
@@ -68,8 +68,11 @@ def __init__(self, gen, func):
6868
# decorator
6969

7070
def __repr__(self):
71-
return ('<%s %s>'
72-
% (self.__class__.__name__, _format_coroutine(self)))
71+
coro_repr = _format_coroutine(self)
72+
if self._source_traceback:
73+
frame = self._source_traceback[-1]
74+
coro_repr += ', created at %s:%s' % (frame[0], frame[1])
75+
return '<%s %s>' % (self.__class__.__name__, coro_repr)
7376

7477
def __iter__(self):
7578
return self
@@ -181,9 +184,18 @@ def _format_coroutine(coro):
181184
coro_name = coro.__name__
182185

183186
filename = coro.gi_code.co_filename
184-
if coro.gi_frame is not None:
187+
if (isinstance(coro, CoroWrapper)
188+
and not inspect.isgeneratorfunction(coro.func)):
189+
filename, lineno = events._get_function_source(coro.func)
190+
if coro.gi_frame is None:
191+
coro_repr = '%s() done, defined at %s:%s' % (coro_name, filename, lineno)
192+
else:
193+
coro_repr = '%s() running, defined at %s:%s' % (coro_name, filename, lineno)
194+
elif coro.gi_frame is not None:
185195
lineno = coro.gi_frame.f_lineno
186-
return '%s() at %s:%s' % (coro_name, filename, lineno)
196+
coro_repr = '%s() running at %s:%s' % (coro_name, filename, lineno)
187197
else:
188198
lineno = coro.gi_code.co_firstlineno
189-
return '%s() done at %s:%s' % (coro_name, filename, lineno)
199+
coro_repr = '%s() done, defined at %s:%s' % (coro_name, filename, lineno)
200+
201+
return coro_repr

Lib/asyncio/tasks.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ def __repr__(self):
101101
else:
102102
info.append(self._state.lower())
103103

104-
info.append(coroutines._format_coroutine(self._coro))
104+
coro = coroutines._format_coroutine(self._coro)
105+
info.append('coro=<%s>' % coro)
106+
107+
if self._source_traceback:
108+
frame = self._source_traceback[-1]
109+
info.append('created at %s:%s' % (frame[0], frame[1]))
105110

106111
if self._state == futures._FINISHED:
107112
info.append(self._format_result())

Lib/test/test_asyncio/test_tasks.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ def coroutine_function():
2424
pass
2525

2626

27+
def format_coroutine(qualname, state, src, source_traceback, generator=False):
28+
if generator:
29+
state = '%s' % state
30+
else:
31+
state = '%s, defined' % state
32+
if source_traceback is not None:
33+
frame = source_traceback[-1]
34+
return ('coro=<%s() %s at %s> created at %s:%s'
35+
% (qualname, state, src, frame[0], frame[1]))
36+
else:
37+
return 'coro=<%s() %s at %s>' % (qualname, state, src)
38+
39+
2740
class Dummy:
2841

2942
def __repr__(self):
@@ -149,7 +162,9 @@ def notmuch():
149162
# test pending Task
150163
t = asyncio.Task(gen, loop=self.loop)
151164
t.add_done_callback(Dummy())
152-
coro = '%s() at %s' % (coro_qualname, src)
165+
166+
coro = format_coroutine(coro_qualname, 'running', src,
167+
t._source_traceback, generator=True)
153168
self.assertEqual(repr(t),
154169
'<Task pending %s cb=[<Dummy>()]>' % coro)
155170

@@ -161,13 +176,16 @@ def notmuch():
161176
# test cancelled Task
162177
self.assertRaises(asyncio.CancelledError,
163178
self.loop.run_until_complete, t)
164-
coro = '%s() done at %s' % (coro_qualname, src)
179+
coro = format_coroutine(coro_qualname, 'done', src,
180+
t._source_traceback)
165181
self.assertEqual(repr(t),
166182
'<Task cancelled %s>' % coro)
167183

168184
# test finished Task
169185
t = asyncio.Task(notmuch(), loop=self.loop)
170186
self.loop.run_until_complete(t)
187+
coro = format_coroutine(coro_qualname, 'done', src,
188+
t._source_traceback)
171189
self.assertEqual(repr(t),
172190
"<Task finished %s result='abc'>" % coro)
173191

@@ -206,18 +224,35 @@ def notmuch():
206224
if PY35:
207225
self.assertEqual(gen.__qualname__, coro_qualname)
208226

209-
# format the coroutine object
210-
code = gen.gi_code
211-
coro = ('%s() at %s:%s'
212-
% (coro_qualname, code.co_filename, code.co_firstlineno))
213-
214227
# test repr(CoroWrapper)
215228
if coroutines._DEBUG:
229+
# format the coroutine object
230+
if coroutines._DEBUG:
231+
filename, lineno = test_utils.get_function_source(notmuch)
232+
frame = gen._source_traceback[-1]
233+
coro = ('%s() running, defined at %s:%s, created at %s:%s'
234+
% (coro_qualname, filename, lineno,
235+
frame[0], frame[1]))
236+
else:
237+
code = gen.gi_code
238+
coro = ('%s() running at %s:%s'
239+
% (coro_qualname, code.co_filename, code.co_firstlineno))
240+
216241
self.assertEqual(repr(gen), '<CoroWrapper %s>' % coro)
217242

218243
# test pending Task
219244
t = asyncio.Task(gen, loop=self.loop)
220245
t.add_done_callback(Dummy())
246+
247+
# format the coroutine object
248+
if coroutines._DEBUG:
249+
src = '%s:%s' % test_utils.get_function_source(notmuch)
250+
else:
251+
code = gen.gi_code
252+
src = '%s:%s' % (code.co_filename, code.co_firstlineno)
253+
coro = format_coroutine(coro_qualname, 'running', src,
254+
t._source_traceback,
255+
generator=not coroutines._DEBUG)
221256
self.assertEqual(repr(t),
222257
'<Task pending %s cb=[<Dummy>()]>' % coro)
223258
self.loop.run_until_complete(t)

0 commit comments

Comments
 (0)