(Merge 3.4) asyncio, Tulip issue 137: In debug mode, add the traceback where
the coroutine object was created to the "coroutine ... was never yield from" log
This commit is contained in:
parent
19b011109d
commit
fe4a979099
|
@ -43,6 +43,7 @@ class CoroWrapper:
|
||||||
assert inspect.isgenerator(gen), gen
|
assert inspect.isgenerator(gen), gen
|
||||||
self.gen = gen
|
self.gen = gen
|
||||||
self.func = func
|
self.func = func
|
||||||
|
self._source_traceback = traceback.extract_stack(sys._getframe(1))
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
@ -81,13 +82,13 @@ class CoroWrapper:
|
||||||
gen = getattr(self, 'gen', None)
|
gen = getattr(self, 'gen', None)
|
||||||
frame = getattr(gen, 'gi_frame', None)
|
frame = getattr(gen, 'gi_frame', None)
|
||||||
if frame is not None and frame.f_lasti == -1:
|
if frame is not None and frame.f_lasti == -1:
|
||||||
func = self.func
|
func = events._format_callback(self.func, ())
|
||||||
code = func.__code__
|
tb = ''.join(traceback.format_list(self._source_traceback))
|
||||||
filename = code.co_filename
|
message = ('Coroutine %s was never yielded from\n'
|
||||||
lineno = code.co_firstlineno
|
'Coroutine object created at (most recent call last):\n'
|
||||||
logger.error(
|
'%s'
|
||||||
'Coroutine %r defined at %s:%s was never yielded from',
|
% (func, tb.rstrip()))
|
||||||
func.__name__, filename, lineno)
|
logger.error(message)
|
||||||
|
|
||||||
|
|
||||||
def coroutine(func):
|
def coroutine(func):
|
||||||
|
@ -112,6 +113,8 @@ def coroutine(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(*args, **kwds):
|
def wrapper(*args, **kwds):
|
||||||
w = CoroWrapper(coro(*args, **kwds), func)
|
w = CoroWrapper(coro(*args, **kwds), func)
|
||||||
|
if w._source_traceback:
|
||||||
|
del w._source_traceback[-1]
|
||||||
w.__name__ = func.__name__
|
w.__name__ = func.__name__
|
||||||
if _PY35:
|
if _PY35:
|
||||||
w.__qualname__ = func.__qualname__
|
w.__qualname__ = func.__qualname__
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Tests for tasks.py."""
|
"""Tests for tasks.py."""
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -1572,6 +1573,37 @@ class TaskTests(test_utils.TestCase):
|
||||||
})
|
})
|
||||||
mock_handler.reset_mock()
|
mock_handler.reset_mock()
|
||||||
|
|
||||||
|
@mock.patch('asyncio.tasks.logger')
|
||||||
|
def test_coroutine_never_yielded(self, m_log):
|
||||||
|
debug = asyncio.tasks._DEBUG
|
||||||
|
try:
|
||||||
|
asyncio.tasks._DEBUG = True
|
||||||
|
@asyncio.coroutine
|
||||||
|
def coro_noop():
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
asyncio.tasks._DEBUG = debug
|
||||||
|
|
||||||
|
tb_filename = __file__
|
||||||
|
tb_lineno = sys._getframe().f_lineno + 1
|
||||||
|
coro = coro_noop()
|
||||||
|
coro = None
|
||||||
|
support.gc_collect()
|
||||||
|
|
||||||
|
self.assertTrue(m_log.error.called)
|
||||||
|
message = m_log.error.call_args[0][0]
|
||||||
|
func_filename, func_lineno = test_utils.get_function_source(coro_noop)
|
||||||
|
regex = (r'^Coroutine %s\(\) at %s:%s was never yielded from\n'
|
||||||
|
r'Coroutine object created at \(most recent call last\):\n'
|
||||||
|
r'.*\n'
|
||||||
|
r' File "%s", line %s, in test_coroutine_never_yielded\n'
|
||||||
|
r' coro = coro_noop\(\)$'
|
||||||
|
% (re.escape(coro_noop.__qualname__),
|
||||||
|
func_filename, func_lineno,
|
||||||
|
tb_filename, tb_lineno))
|
||||||
|
|
||||||
|
self.assertRegex(message, re.compile(regex, re.DOTALL))
|
||||||
|
|
||||||
|
|
||||||
class GatherTestsBase:
|
class GatherTestsBase:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue