[Python-checkins] cpython: (Merge 3.4) asyncio, Tulip issue 137: In debug mode, add the traceback where

victor.stinner python-checkins at python.org
Fri Jun 27 12:30:48 CEST 2014


http://hg.python.org/cpython/rev/d8c24c25c42d
changeset:   91443:d8c24c25c42d
parent:      91441:4be3ee53c62c
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Jun 27 12:29:30 2014 +0200
summary:
  (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

files:
  Lib/asyncio/tasks.py                |  17 +++++---
  Lib/test/test_asyncio/test_tasks.py |  32 +++++++++++++++++
  2 files changed, 42 insertions(+), 7 deletions(-)


diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -43,6 +43,7 @@
         assert inspect.isgenerator(gen), gen
         self.gen = gen
         self.func = func
+        self._source_traceback = traceback.extract_stack(sys._getframe(1))
 
     def __iter__(self):
         return self
@@ -81,13 +82,13 @@
         gen = getattr(self, 'gen', None)
         frame = getattr(gen, 'gi_frame', None)
         if frame is not None and frame.f_lasti == -1:
-            func = self.func
-            code = func.__code__
-            filename = code.co_filename
-            lineno = code.co_firstlineno
-            logger.error(
-                'Coroutine %r defined at %s:%s was never yielded from',
-                func.__name__, filename, lineno)
+            func = events._format_callback(self.func, ())
+            tb = ''.join(traceback.format_list(self._source_traceback))
+            message = ('Coroutine %s was never yielded from\n'
+                       'Coroutine object created at (most recent call last):\n'
+                       '%s'
+                       % (func, tb.rstrip()))
+            logger.error(message)
 
 
 def coroutine(func):
@@ -112,6 +113,8 @@
         @functools.wraps(func)
         def wrapper(*args, **kwds):
             w = CoroWrapper(coro(*args, **kwds), func)
+            if w._source_traceback:
+                del w._source_traceback[-1]
             w.__name__ = func.__name__
             if _PY35:
                 w.__qualname__ = func.__qualname__
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -1,6 +1,7 @@
 """Tests for tasks.py."""
 
 import os.path
+import re
 import sys
 import types
 import unittest
@@ -1572,6 +1573,37 @@
         })
         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:
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list