]> granicus.if.org Git - python/commitdiff
asyncio: Set __qualname__ attribute of CoroWrapper in @coroutine decorator on
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 17 Jun 2014 23:14:59 +0000 (01:14 +0200)
committerVictor Stinner <victor.stinner@gmail.com>
Tue, 17 Jun 2014 23:14:59 +0000 (01:14 +0200)
Python 3.5

- Drop __slots__ optimization of CoroWrapper to be able to set the __qualname__
  attribute.
- Add tests on __name__, __qualname__ and __module__ of a coroutine function
  and coroutine object.
- Fix test_tasks when run in debug mode (PYTHONASYNCIODEBUG env var set) on
  Python 3.3 or 3.4

Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_tasks.py

index 281bf608e8770e5883f8b926a4b0b44a3b24c659..eaf93f887323e265051eb2355ee1169863ca0dfa 100644 (file)
@@ -32,12 +32,12 @@ from .log import logger
 _DEBUG = (not sys.flags.ignore_environment
           and bool(os.environ.get('PYTHONASYNCIODEBUG')))
 
+_PY35 = (sys.version_info >= (3, 5))
+
 
 class CoroWrapper:
     # Wrapper for coroutine in _DEBUG mode.
 
-    __slots__ = ['gen', 'func', '__name__', '__doc__', '__weakref__']
-
     def __init__(self, gen, func):
         assert inspect.isgenerator(gen), gen
         self.gen = gen
@@ -111,8 +111,10 @@ def coroutine(func):
         @functools.wraps(func)
         def wrapper(*args, **kwds):
             w = CoroWrapper(coro(*args, **kwds), func)
-            w.__name__ = coro.__name__
-            w.__doc__ = coro.__doc__
+            w.__name__ = func.__name__
+            if _PY35:
+                w.__qualname__ = func.__qualname__
+            w.__doc__ = func.__doc__
             return w
 
     wrapper._is_coroutine = True  # For iscoroutinefunction().
index 4e239ecbcbd892cfa85671e6077882788ac5fbac..dcc81234d67d3b92d8437c3d362425aa297aadfb 100644 (file)
@@ -9,9 +9,13 @@ import weakref
 from test.script_helper import assert_python_ok
 
 import asyncio
+from asyncio import tasks
 from asyncio import test_utils
 
 
+PY35 = (sys.version_info >= (3, 5))
+
+
 @asyncio.coroutine
 def coroutine_function():
     pass
@@ -117,10 +121,22 @@ class TaskTests(unittest.TestCase):
             yield from []
             return 'abc'
 
+        self.assertEqual(notmuch.__name__, 'notmuch')
+        if PY35:
+            self.assertEqual(notmuch.__qualname__,
+                             'TaskTests.test_task_repr.<locals>.notmuch')
+        self.assertEqual(notmuch.__module__, __name__)
+
         filename, lineno = test_utils.get_function_source(notmuch)
         src = "%s:%s" % (filename, lineno)
 
-        t = asyncio.Task(notmuch(), loop=self.loop)
+        gen = notmuch()
+        self.assertEqual(gen.__name__, 'notmuch')
+        if PY35:
+            self.assertEqual(gen.__qualname__,
+                             'TaskTests.test_task_repr.<locals>.notmuch')
+
+        t = asyncio.Task(gen, loop=self.loop)
         t.add_done_callback(Dummy())
         self.assertEqual(repr(t),
                          'Task(<notmuch at %s>)<PENDING, [Dummy()]>' % src)
@@ -143,6 +159,12 @@ class TaskTests(unittest.TestCase):
         def notmuch():
             pass
 
+        self.assertEqual(notmuch.__name__, 'notmuch')
+        self.assertEqual(notmuch.__module__, __name__)
+        if PY35:
+            self.assertEqual(notmuch.__qualname__,
+                             'TaskTests.test_task_repr_custom.<locals>.notmuch')
+
         class T(asyncio.Future):
             def __repr__(self):
                 return 'T[]'
@@ -152,16 +174,26 @@ class TaskTests(unittest.TestCase):
                 return super().__repr__()
 
         gen = notmuch()
-        t = MyTask(gen, loop=self.loop)
-        filename = gen.gi_code.co_filename
-        lineno = gen.gi_frame.f_lineno
-        if sys.version_info >= (3, 5):
-            name = 'notmuch'
+        if PY35 or tasks._DEBUG:
+            # On Python >= 3.5, generators now inherit the name of the
+            # function, as expected, and have a qualified name (__qualname__
+            # attribute). In debug mode, @coroutine decorator uses CoroWrapper
+            # which gets its name (__name__ attribute) from the wrapped
+            # coroutine function.
+            coro_name = 'notmuch'
         else:
             # On Python < 3.5, generators inherit the name of the code, not of
             # the function. See: http://bugs.python.org/issue21205
-            name = 'coro'
-        self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (name, filename, lineno))
+            coro_name = 'coro'
+        self.assertEqual(gen.__name__, coro_name)
+        if PY35:
+            self.assertEqual(gen.__qualname__,
+                             'TaskTests.test_task_repr_custom.<locals>.notmuch')
+
+        t = MyTask(gen, loop=self.loop)
+        filename = gen.gi_code.co_filename
+        lineno = gen.gi_frame.f_lineno
+        self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (coro_name, filename, lineno))
 
     def test_task_basics(self):
         @asyncio.coroutine