From c39ba7d611fe556314acc8c11cd9f805512db663 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 11 Jul 2014 00:21:27 +0200 Subject: [PATCH] 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(). --- Lib/asyncio/base_events.py | 5 ++- Lib/asyncio/coroutines.py | 24 ++++++++++---- Lib/asyncio/tasks.py | 7 ++++- Lib/test/test_asyncio/test_tasks.py | 49 ++++++++++++++++++++++++----- 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 833f81d4a90..f6d7a58f5e0 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -155,7 +155,10 @@ class BaseEventLoop(events.AbstractEventLoop): """Schedule a coroutine 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, *, extra=None, server=None): diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index 48730c225ab..4cbfa854ef7 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -57,7 +57,7 @@ del has_yield_from_bug class CoroWrapper: - # Wrapper for coroutine in _DEBUG mode. + # Wrapper for coroutine object in _DEBUG mode. def __init__(self, gen, func): assert inspect.isgenerator(gen), gen @@ -68,8 +68,11 @@ class CoroWrapper: # decorator def __repr__(self): - return ('<%s %s>' - % (self.__class__.__name__, _format_coroutine(self))) + coro_repr = _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): return self @@ -181,9 +184,18 @@ def _format_coroutine(coro): coro_name = coro.__name__ 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 - return '%s() at %s:%s' % (coro_name, filename, lineno) + coro_repr = '%s() running at %s:%s' % (coro_name, filename, lineno) else: 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 diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index befc2967c71..61f48223ca3 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -101,7 +101,12 @@ class Task(futures.Future): else: 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: info.append(self._format_result()) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index afadc7c1ed5..b13818f7f32 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -24,6 +24,19 @@ def coroutine_function(): 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: def __repr__(self): @@ -149,7 +162,9 @@ class TaskTests(test_utils.TestCase): # test pending Task t = asyncio.Task(gen, loop=self.loop) 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), '()]>' % coro) @@ -161,13 +176,16 @@ class TaskTests(test_utils.TestCase): # test cancelled Task self.assertRaises(asyncio.CancelledError, 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), '' % coro) # test finished Task t = asyncio.Task(notmuch(), loop=self.loop) self.loop.run_until_complete(t) + coro = format_coroutine(coro_qualname, 'done', src, + t._source_traceback) self.assertEqual(repr(t), "" % coro) @@ -206,18 +224,35 @@ class TaskTests(test_utils.TestCase): if PY35: 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) 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), '' % coro) # test pending Task t = asyncio.Task(gen, loop=self.loop) 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), '()]>' % coro) self.loop.run_until_complete(t)