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

Skip to content

Commit bbd96c6

Browse files
committed
asyncio, Tulip issue 137: In debug mode, add the traceback where the coroutine
object was created to the "coroutine ... was never yield from" log
1 parent c4cca45 commit bbd96c6

2 files changed

Lines changed: 42 additions & 7 deletions

File tree

Lib/asyncio/tasks.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def __init__(self, gen, func):
4343
assert inspect.isgenerator(gen), gen
4444
self.gen = gen
4545
self.func = func
46+
self._source_traceback = traceback.extract_stack(sys._getframe(1))
4647

4748
def __iter__(self):
4849
return self
@@ -81,13 +82,13 @@ def __del__(self):
8182
gen = getattr(self, 'gen', None)
8283
frame = getattr(gen, 'gi_frame', None)
8384
if frame is not None and frame.f_lasti == -1:
84-
func = self.func
85-
code = func.__code__
86-
filename = code.co_filename
87-
lineno = code.co_firstlineno
88-
logger.error(
89-
'Coroutine %r defined at %s:%s was never yielded from',
90-
func.__name__, filename, lineno)
85+
func = events._format_callback(self.func, ())
86+
tb = ''.join(traceback.format_list(self._source_traceback))
87+
message = ('Coroutine %s was never yielded from\n'
88+
'Coroutine object created at (most recent call last):\n'
89+
'%s'
90+
% (func, tb.rstrip()))
91+
logger.error(message)
9192

9293

9394
def coroutine(func):
@@ -112,6 +113,8 @@ def coro(*args, **kw):
112113
@functools.wraps(func)
113114
def wrapper(*args, **kwds):
114115
w = CoroWrapper(coro(*args, **kwds), func)
116+
if w._source_traceback:
117+
del w._source_traceback[-1]
115118
w.__name__ = func.__name__
116119
if _PY35:
117120
w.__qualname__ = func.__qualname__

Lib/test/test_asyncio/test_tasks.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests for tasks.py."""
22

33
import os.path
4+
import re
45
import sys
56
import types
67
import unittest
@@ -1572,6 +1573,37 @@ def kill_me(loop):
15721573
})
15731574
mock_handler.reset_mock()
15741575

1576+
@mock.patch('asyncio.tasks.logger')
1577+
def test_coroutine_never_yielded(self, m_log):
1578+
debug = asyncio.tasks._DEBUG
1579+
try:
1580+
asyncio.tasks._DEBUG = True
1581+
@asyncio.coroutine
1582+
def coro_noop():
1583+
pass
1584+
finally:
1585+
asyncio.tasks._DEBUG = debug
1586+
1587+
tb_filename = __file__
1588+
tb_lineno = sys._getframe().f_lineno + 1
1589+
coro = coro_noop()
1590+
coro = None
1591+
support.gc_collect()
1592+
1593+
self.assertTrue(m_log.error.called)
1594+
message = m_log.error.call_args[0][0]
1595+
func_filename, func_lineno = test_utils.get_function_source(coro_noop)
1596+
regex = (r'^Coroutine %s\(\) at %s:%s was never yielded from\n'
1597+
r'Coroutine object created at \(most recent call last\):\n'
1598+
r'.*\n'
1599+
r' File "%s", line %s, in test_coroutine_never_yielded\n'
1600+
r' coro = coro_noop\(\)$'
1601+
% (re.escape(coro_noop.__qualname__),
1602+
func_filename, func_lineno,
1603+
tb_filename, tb_lineno))
1604+
1605+
self.assertRegex(message, re.compile(regex, re.DOTALL))
1606+
15751607

15761608
class GatherTestsBase:
15771609

0 commit comments

Comments
 (0)