mirror of https://github.com/python/cpython
asyncio: Set __qualname__ attribute of CoroWrapper in @coroutine decorator on
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
This commit is contained in:
parent
66dc6b0f53
commit
8d3e02ef5a
|
@ -32,12 +32,12 @@ from .log import logger
|
||||||
_DEBUG = (not sys.flags.ignore_environment
|
_DEBUG = (not sys.flags.ignore_environment
|
||||||
and bool(os.environ.get('PYTHONASYNCIODEBUG')))
|
and bool(os.environ.get('PYTHONASYNCIODEBUG')))
|
||||||
|
|
||||||
|
_PY35 = (sys.version_info >= (3, 5))
|
||||||
|
|
||||||
|
|
||||||
class CoroWrapper:
|
class CoroWrapper:
|
||||||
# Wrapper for coroutine in _DEBUG mode.
|
# Wrapper for coroutine in _DEBUG mode.
|
||||||
|
|
||||||
__slots__ = ['gen', 'func', '__name__', '__doc__', '__weakref__']
|
|
||||||
|
|
||||||
def __init__(self, gen, func):
|
def __init__(self, gen, func):
|
||||||
assert inspect.isgenerator(gen), gen
|
assert inspect.isgenerator(gen), gen
|
||||||
self.gen = gen
|
self.gen = gen
|
||||||
|
@ -111,8 +111,10 @@ 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)
|
||||||
w.__name__ = coro.__name__
|
w.__name__ = func.__name__
|
||||||
w.__doc__ = coro.__doc__
|
if _PY35:
|
||||||
|
w.__qualname__ = func.__qualname__
|
||||||
|
w.__doc__ = func.__doc__
|
||||||
return w
|
return w
|
||||||
|
|
||||||
wrapper._is_coroutine = True # For iscoroutinefunction().
|
wrapper._is_coroutine = True # For iscoroutinefunction().
|
||||||
|
|
|
@ -9,9 +9,13 @@ import weakref
|
||||||
from test.script_helper import assert_python_ok
|
from test.script_helper import assert_python_ok
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from asyncio import tasks
|
||||||
from asyncio import test_utils
|
from asyncio import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
PY35 = (sys.version_info >= (3, 5))
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def coroutine_function():
|
def coroutine_function():
|
||||||
pass
|
pass
|
||||||
|
@ -117,10 +121,22 @@ class TaskTests(unittest.TestCase):
|
||||||
yield from []
|
yield from []
|
||||||
return 'abc'
|
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)
|
filename, lineno = test_utils.get_function_source(notmuch)
|
||||||
src = "%s:%s" % (filename, lineno)
|
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())
|
t.add_done_callback(Dummy())
|
||||||
self.assertEqual(repr(t),
|
self.assertEqual(repr(t),
|
||||||
'Task(<notmuch at %s>)<PENDING, [Dummy()]>' % src)
|
'Task(<notmuch at %s>)<PENDING, [Dummy()]>' % src)
|
||||||
|
@ -143,6 +159,12 @@ class TaskTests(unittest.TestCase):
|
||||||
def notmuch():
|
def notmuch():
|
||||||
pass
|
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):
|
class T(asyncio.Future):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'T[]'
|
return 'T[]'
|
||||||
|
@ -152,16 +174,26 @@ class TaskTests(unittest.TestCase):
|
||||||
return super().__repr__()
|
return super().__repr__()
|
||||||
|
|
||||||
gen = notmuch()
|
gen = notmuch()
|
||||||
t = MyTask(gen, loop=self.loop)
|
if PY35 or tasks._DEBUG:
|
||||||
filename = gen.gi_code.co_filename
|
# On Python >= 3.5, generators now inherit the name of the
|
||||||
lineno = gen.gi_frame.f_lineno
|
# function, as expected, and have a qualified name (__qualname__
|
||||||
if sys.version_info >= (3, 5):
|
# attribute). In debug mode, @coroutine decorator uses CoroWrapper
|
||||||
name = 'notmuch'
|
# which gets its name (__name__ attribute) from the wrapped
|
||||||
|
# coroutine function.
|
||||||
|
coro_name = 'notmuch'
|
||||||
else:
|
else:
|
||||||
# On Python < 3.5, generators inherit the name of the code, not of
|
# On Python < 3.5, generators inherit the name of the code, not of
|
||||||
# the function. See: http://bugs.python.org/issue21205
|
# the function. See: http://bugs.python.org/issue21205
|
||||||
name = 'coro'
|
coro_name = 'coro'
|
||||||
self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (name, filename, lineno))
|
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):
|
def test_task_basics(self):
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
Loading…
Reference in New Issue