Commit c39ba7d6 authored by Victor Stinner's avatar Victor Stinner

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().
parent f68bd88a
...@@ -155,7 +155,10 @@ class BaseEventLoop(events.AbstractEventLoop): ...@@ -155,7 +155,10 @@ class BaseEventLoop(events.AbstractEventLoop):
"""Schedule a coroutine object. """Schedule a coroutine object.
Return a task object.""" Return a task object."""
return tasks.Task(coro, loop=self) task = tasks.Task(coro, loop=self)
if task._source_traceback:
del task._source_traceback[-1]
return task
def _make_socket_transport(self, sock, protocol, waiter=None, *, def _make_socket_transport(self, sock, protocol, waiter=None, *,
extra=None, server=None): extra=None, server=None):
......
...@@ -57,7 +57,7 @@ del has_yield_from_bug ...@@ -57,7 +57,7 @@ del has_yield_from_bug
class CoroWrapper: class CoroWrapper:
# Wrapper for coroutine in _DEBUG mode. # Wrapper for coroutine object in _DEBUG mode.
def __init__(self, gen, func): def __init__(self, gen, func):
assert inspect.isgenerator(gen), gen assert inspect.isgenerator(gen), gen
...@@ -68,8 +68,11 @@ class CoroWrapper: ...@@ -68,8 +68,11 @@ class CoroWrapper:
# decorator # decorator
def __repr__(self): def __repr__(self):
return ('<%s %s>' coro_repr = _format_coroutine(self)
% (self.__class__.__name__, _format_coroutine(self))) if self._source_traceback:
frame = self._source_traceback[-1]
coro_repr += ', created at %s:%s' % (frame[0], frame[1])
return '<%s %s>' % (self.__class__.__name__, coro_repr)
def __iter__(self): def __iter__(self):
return self return self
...@@ -181,9 +184,18 @@ def _format_coroutine(coro): ...@@ -181,9 +184,18 @@ def _format_coroutine(coro):
coro_name = coro.__name__ coro_name = coro.__name__
filename = coro.gi_code.co_filename filename = coro.gi_code.co_filename
if coro.gi_frame is not None: if (isinstance(coro, CoroWrapper)
and not inspect.isgeneratorfunction(coro.func)):
filename, lineno = events._get_function_source(coro.func)
if coro.gi_frame is None:
coro_repr = '%s() done, defined at %s:%s' % (coro_name, filename, lineno)
else:
coro_repr = '%s() running, defined at %s:%s' % (coro_name, filename, lineno)
elif coro.gi_frame is not None:
lineno = coro.gi_frame.f_lineno lineno = coro.gi_frame.f_lineno
return '%s() at %s:%s' % (coro_name, filename, lineno) coro_repr = '%s() running at %s:%s' % (coro_name, filename, lineno)
else: else:
lineno = coro.gi_code.co_firstlineno lineno = coro.gi_code.co_firstlineno
return '%s() done at %s:%s' % (coro_name, filename, lineno) coro_repr = '%s() done, defined at %s:%s' % (coro_name, filename, lineno)
return coro_repr
...@@ -101,7 +101,12 @@ class Task(futures.Future): ...@@ -101,7 +101,12 @@ class Task(futures.Future):
else: else:
info.append(self._state.lower()) info.append(self._state.lower())
info.append(coroutines._format_coroutine(self._coro)) coro = coroutines._format_coroutine(self._coro)
info.append('coro=<%s>' % coro)
if self._source_traceback:
frame = self._source_traceback[-1]
info.append('created at %s:%s' % (frame[0], frame[1]))
if self._state == futures._FINISHED: if self._state == futures._FINISHED:
info.append(self._format_result()) info.append(self._format_result())
......
...@@ -24,6 +24,19 @@ def coroutine_function(): ...@@ -24,6 +24,19 @@ def coroutine_function():
pass pass
def format_coroutine(qualname, state, src, source_traceback, generator=False):
if generator:
state = '%s' % state
else:
state = '%s, defined' % state
if source_traceback is not None:
frame = source_traceback[-1]
return ('coro=<%s() %s at %s> created at %s:%s'
% (qualname, state, src, frame[0], frame[1]))
else:
return 'coro=<%s() %s at %s>' % (qualname, state, src)
class Dummy: class Dummy:
def __repr__(self): def __repr__(self):
...@@ -149,7 +162,9 @@ class TaskTests(test_utils.TestCase): ...@@ -149,7 +162,9 @@ class TaskTests(test_utils.TestCase):
# test pending Task # test pending Task
t = asyncio.Task(gen, loop=self.loop) t = asyncio.Task(gen, loop=self.loop)
t.add_done_callback(Dummy()) t.add_done_callback(Dummy())
coro = '%s() at %s' % (coro_qualname, src)
coro = format_coroutine(coro_qualname, 'running', src,
t._source_traceback, generator=True)
self.assertEqual(repr(t), self.assertEqual(repr(t),
'<Task pending %s cb=[<Dummy>()]>' % coro) '<Task pending %s cb=[<Dummy>()]>' % coro)
...@@ -161,13 +176,16 @@ class TaskTests(test_utils.TestCase): ...@@ -161,13 +176,16 @@ class TaskTests(test_utils.TestCase):
# test cancelled Task # test cancelled Task
self.assertRaises(asyncio.CancelledError, self.assertRaises(asyncio.CancelledError,
self.loop.run_until_complete, t) self.loop.run_until_complete, t)
coro = '%s() done at %s' % (coro_qualname, src) coro = format_coroutine(coro_qualname, 'done', src,
t._source_traceback)
self.assertEqual(repr(t), self.assertEqual(repr(t),
'<Task cancelled %s>' % coro) '<Task cancelled %s>' % coro)
# test finished Task # test finished Task
t = asyncio.Task(notmuch(), loop=self.loop) t = asyncio.Task(notmuch(), loop=self.loop)
self.loop.run_until_complete(t) self.loop.run_until_complete(t)
coro = format_coroutine(coro_qualname, 'done', src,
t._source_traceback)
self.assertEqual(repr(t), self.assertEqual(repr(t),
"<Task finished %s result='abc'>" % coro) "<Task finished %s result='abc'>" % coro)
...@@ -206,18 +224,35 @@ class TaskTests(test_utils.TestCase): ...@@ -206,18 +224,35 @@ class TaskTests(test_utils.TestCase):
if PY35: if PY35:
self.assertEqual(gen.__qualname__, coro_qualname) self.assertEqual(gen.__qualname__, coro_qualname)
# format the coroutine object
code = gen.gi_code
coro = ('%s() at %s:%s'
% (coro_qualname, code.co_filename, code.co_firstlineno))
# test repr(CoroWrapper) # test repr(CoroWrapper)
if coroutines._DEBUG: if coroutines._DEBUG:
# format the coroutine object
if coroutines._DEBUG:
filename, lineno = test_utils.get_function_source(notmuch)
frame = gen._source_traceback[-1]
coro = ('%s() running, defined at %s:%s, created at %s:%s'
% (coro_qualname, filename, lineno,
frame[0], frame[1]))
else:
code = gen.gi_code
coro = ('%s() running at %s:%s'
% (coro_qualname, code.co_filename, code.co_firstlineno))
self.assertEqual(repr(gen), '<CoroWrapper %s>' % coro) self.assertEqual(repr(gen), '<CoroWrapper %s>' % coro)
# test pending Task # test pending Task
t = asyncio.Task(gen, loop=self.loop) t = asyncio.Task(gen, loop=self.loop)
t.add_done_callback(Dummy()) t.add_done_callback(Dummy())
# format the coroutine object
if coroutines._DEBUG:
src = '%s:%s' % test_utils.get_function_source(notmuch)
else:
code = gen.gi_code
src = '%s:%s' % (code.co_filename, code.co_firstlineno)
coro = format_coroutine(coro_qualname, 'running', src,
t._source_traceback,
generator=not coroutines._DEBUG)
self.assertEqual(repr(t), self.assertEqual(repr(t),
'<Task pending %s cb=[<Dummy>()]>' % coro) '<Task pending %s cb=[<Dummy>()]>' % coro)
self.loop.run_until_complete(t) self.loop.run_until_complete(t)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment