mirror of https://github.com/python/cpython
(Merge 3.4) asyncio, Tulip issue 177: Rewite repr() of Future, Task, Handle and
TimerHandle - Uniformize repr() output to format "<Class ...>" - On Python 3.5+, repr(Task) uses the qualified name instead of the short name of the coroutine
This commit is contained in:
commit
893df48682
|
@ -18,6 +18,7 @@ import sys
|
||||||
|
|
||||||
_PY34 = sys.version_info >= (3, 4)
|
_PY34 = sys.version_info >= (3, 4)
|
||||||
|
|
||||||
|
|
||||||
def _get_function_source(func):
|
def _get_function_source(func):
|
||||||
if _PY34:
|
if _PY34:
|
||||||
func = inspect.unwrap(func)
|
func = inspect.unwrap(func)
|
||||||
|
@ -33,6 +34,35 @@ def _get_function_source(func):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_args(args):
|
||||||
|
# function formatting ('hello',) as ('hello')
|
||||||
|
args_repr = repr(args)
|
||||||
|
if len(args) == 1 and args_repr.endswith(',)'):
|
||||||
|
args_repr = args_repr[:-2] + ')'
|
||||||
|
return args_repr
|
||||||
|
|
||||||
|
|
||||||
|
def _format_callback(func, args, suffix=''):
|
||||||
|
if isinstance(func, functools.partial):
|
||||||
|
if args is not None:
|
||||||
|
suffix = _format_args(args) + suffix
|
||||||
|
return _format_callback(func.func, func.args, suffix)
|
||||||
|
|
||||||
|
func_repr = getattr(func, '__qualname__', None)
|
||||||
|
if not func_repr:
|
||||||
|
func_repr = repr(func)
|
||||||
|
|
||||||
|
if args is not None:
|
||||||
|
func_repr += _format_args(args)
|
||||||
|
if suffix:
|
||||||
|
func_repr += suffix
|
||||||
|
|
||||||
|
source = _get_function_source(func)
|
||||||
|
if source:
|
||||||
|
func_repr += ' at %s:%s' % source
|
||||||
|
return func_repr
|
||||||
|
|
||||||
|
|
||||||
class Handle:
|
class Handle:
|
||||||
"""Object returned by callback registration methods."""
|
"""Object returned by callback registration methods."""
|
||||||
|
|
||||||
|
@ -46,18 +76,11 @@ class Handle:
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
cb_repr = getattr(self._callback, '__qualname__', None)
|
info = []
|
||||||
if not cb_repr:
|
|
||||||
cb_repr = str(self._callback)
|
|
||||||
|
|
||||||
source = _get_function_source(self._callback)
|
|
||||||
if source:
|
|
||||||
cb_repr += ' at %s:%s' % source
|
|
||||||
|
|
||||||
res = 'Handle({}, {})'.format(cb_repr, self._args)
|
|
||||||
if self._cancelled:
|
if self._cancelled:
|
||||||
res += '<cancelled>'
|
info.append('cancelled')
|
||||||
return res
|
info.append(_format_callback(self._callback, self._args))
|
||||||
|
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self._cancelled = True
|
self._cancelled = True
|
||||||
|
@ -88,13 +111,12 @@ class TimerHandle(Handle):
|
||||||
self._when = when
|
self._when = when
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
res = 'TimerHandle({}, {}, {})'.format(self._when,
|
info = []
|
||||||
self._callback,
|
|
||||||
self._args)
|
|
||||||
if self._cancelled:
|
if self._cancelled:
|
||||||
res += '<cancelled>'
|
info.append('cancelled')
|
||||||
|
info.append('when=%s' % self._when)
|
||||||
return res
|
info.append(_format_callback(self._callback, self._args))
|
||||||
|
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self._when)
|
return hash(self._when)
|
||||||
|
|
|
@ -150,24 +150,40 @@ class Future:
|
||||||
self._loop = loop
|
self._loop = loop
|
||||||
self._callbacks = []
|
self._callbacks = []
|
||||||
|
|
||||||
def __repr__(self):
|
def _format_callbacks(self):
|
||||||
res = self.__class__.__name__
|
cb = self._callbacks
|
||||||
if self._state == _FINISHED:
|
size = len(cb)
|
||||||
if self._exception is not None:
|
if not size:
|
||||||
res += '<exception={!r}>'.format(self._exception)
|
cb = ''
|
||||||
else:
|
|
||||||
res += '<result={!r}>'.format(self._result)
|
def format_cb(callback):
|
||||||
elif self._callbacks:
|
return events._format_callback(callback, ())
|
||||||
size = len(self._callbacks)
|
|
||||||
if size > 2:
|
if size == 1:
|
||||||
res += '<{}, [{}, <{} more>, {}]>'.format(
|
cb = format_cb(cb[0])
|
||||||
self._state, self._callbacks[0],
|
elif size == 2:
|
||||||
size-2, self._callbacks[-1])
|
cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
|
||||||
else:
|
elif size > 2:
|
||||||
res += '<{}, {}>'.format(self._state, self._callbacks)
|
cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
|
||||||
|
size-2,
|
||||||
|
format_cb(cb[-1]))
|
||||||
|
return 'cb=[%s]' % cb
|
||||||
|
|
||||||
|
def _format_result(self):
|
||||||
|
if self._state != _FINISHED:
|
||||||
|
return None
|
||||||
|
elif self._exception is not None:
|
||||||
|
return 'exception={!r}'.format(self._exception)
|
||||||
else:
|
else:
|
||||||
res += '<{}>'.format(self._state)
|
return 'result={!r}'.format(self._result)
|
||||||
return res
|
|
||||||
|
def __repr__(self):
|
||||||
|
info = [self._state.lower()]
|
||||||
|
if self._state == _FINISHED:
|
||||||
|
info.append(self._format_result())
|
||||||
|
if self._callbacks:
|
||||||
|
info.append(self._format_callbacks())
|
||||||
|
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
||||||
|
|
||||||
# On Python 3.3 or older, objects with a destructor part of a reference
|
# On Python 3.3 or older, objects with a destructor part of a reference
|
||||||
# cycle are never destroyed. It's not more the case on Python 3.4 thanks to
|
# cycle are never destroyed. It's not more the case on Python 3.4 thanks to
|
||||||
|
|
|
@ -132,6 +132,22 @@ def iscoroutine(obj):
|
||||||
return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj)
|
return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_coroutine(coro):
|
||||||
|
assert iscoroutine(coro)
|
||||||
|
if _PY35:
|
||||||
|
coro_name = coro.__qualname__
|
||||||
|
else:
|
||||||
|
coro_name = coro.__name__
|
||||||
|
|
||||||
|
filename = coro.gi_code.co_filename
|
||||||
|
if coro.gi_frame is not None:
|
||||||
|
lineno = coro.gi_frame.f_lineno
|
||||||
|
return '%s() at %s:%s' % (coro_name, filename, lineno)
|
||||||
|
else:
|
||||||
|
lineno = coro.gi_code.co_firstlineno
|
||||||
|
return '%s() done at %s:%s' % (coro_name, filename, lineno)
|
||||||
|
|
||||||
|
|
||||||
class Task(futures.Future):
|
class Task(futures.Future):
|
||||||
"""A coroutine wrapped in a Future."""
|
"""A coroutine wrapped in a Future."""
|
||||||
|
|
||||||
|
@ -195,26 +211,21 @@ class Task(futures.Future):
|
||||||
futures.Future.__del__(self)
|
futures.Future.__del__(self)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
res = super().__repr__()
|
info = []
|
||||||
if (self._must_cancel and
|
if self._must_cancel:
|
||||||
self._state == futures._PENDING and
|
info.append('cancelling')
|
||||||
'<PENDING' in res):
|
else:
|
||||||
res = res.replace('<PENDING', '<CANCELLING', 1)
|
info.append(self._state.lower())
|
||||||
i = res.find('<')
|
|
||||||
if i < 0:
|
info.append(_format_coroutine(self._coro))
|
||||||
i = len(res)
|
|
||||||
text = self._coro.__name__
|
if self._state == futures._FINISHED:
|
||||||
coro = self._coro
|
info.append(self._format_result())
|
||||||
if iscoroutine(coro):
|
|
||||||
filename = coro.gi_code.co_filename
|
if self._callbacks:
|
||||||
if coro.gi_frame is not None:
|
info.append(self._format_callbacks())
|
||||||
lineno = coro.gi_frame.f_lineno
|
|
||||||
text += ' at %s:%s' % (filename, lineno)
|
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
||||||
else:
|
|
||||||
lineno = coro.gi_code.co_firstlineno
|
|
||||||
text += ' done at %s:%s' % (filename, lineno)
|
|
||||||
res = res[:i] + '(<{}>)'.format(text) + res[i:]
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_stack(self, *, limit=None):
|
def get_stack(self, *, limit=None):
|
||||||
"""Return the list of stack frames for this task's coroutine.
|
"""Return the list of stack frames for this task's coroutine.
|
||||||
|
|
|
@ -1016,14 +1016,14 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
|
||||||
self.loop.run_forever()
|
self.loop.run_forever()
|
||||||
fmt, *args = m_logger.warning.call_args[0]
|
fmt, *args = m_logger.warning.call_args[0]
|
||||||
self.assertRegex(fmt % tuple(args),
|
self.assertRegex(fmt % tuple(args),
|
||||||
"^Executing Handle.*stop_loop_cb.* took .* seconds$")
|
"^Executing <Handle.*stop_loop_cb.*> took .* seconds$")
|
||||||
|
|
||||||
# slow task
|
# slow task
|
||||||
asyncio.async(stop_loop_coro(self.loop), loop=self.loop)
|
asyncio.async(stop_loop_coro(self.loop), loop=self.loop)
|
||||||
self.loop.run_forever()
|
self.loop.run_forever()
|
||||||
fmt, *args = m_logger.warning.call_args[0]
|
fmt, *args = m_logger.warning.call_args[0]
|
||||||
self.assertRegex(fmt % tuple(args),
|
self.assertRegex(fmt % tuple(args),
|
||||||
"^Executing Task.*stop_loop_coro.* took .* seconds$")
|
"^Executing <Task.*stop_loop_coro.*> took .* seconds$")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -1747,7 +1747,7 @@ else:
|
||||||
return asyncio.SelectorEventLoop(selectors.SelectSelector())
|
return asyncio.SelectorEventLoop(selectors.SelectSelector())
|
||||||
|
|
||||||
|
|
||||||
def noop():
|
def noop(*args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -1797,50 +1797,52 @@ class HandleTests(unittest.TestCase):
|
||||||
h = asyncio.Handle(lambda: None, (), self.loop)
|
h = asyncio.Handle(lambda: None, (), self.loop)
|
||||||
wd['h'] = h # Would fail without __weakref__ slot.
|
wd['h'] = h # Would fail without __weakref__ slot.
|
||||||
|
|
||||||
def test_repr(self):
|
def test_handle_repr(self):
|
||||||
# simple function
|
# simple function
|
||||||
h = asyncio.Handle(noop, (), self.loop)
|
h = asyncio.Handle(noop, (), self.loop)
|
||||||
src = test_utils.get_function_source(noop)
|
src = test_utils.get_function_source(noop)
|
||||||
self.assertEqual(repr(h),
|
self.assertEqual(repr(h),
|
||||||
'Handle(noop at %s:%s, ())' % src)
|
'<Handle noop() at %s:%s>' % src)
|
||||||
|
|
||||||
# cancelled handle
|
# cancelled handle
|
||||||
h.cancel()
|
h.cancel()
|
||||||
self.assertEqual(repr(h),
|
self.assertEqual(repr(h),
|
||||||
'Handle(noop at %s:%s, ())<cancelled>' % src)
|
'<Handle cancelled noop() at %s:%s>' % src)
|
||||||
|
|
||||||
# decorated function
|
# decorated function
|
||||||
cb = asyncio.coroutine(noop)
|
cb = asyncio.coroutine(noop)
|
||||||
h = asyncio.Handle(cb, (), self.loop)
|
h = asyncio.Handle(cb, (), self.loop)
|
||||||
self.assertEqual(repr(h),
|
self.assertEqual(repr(h),
|
||||||
'Handle(noop at %s:%s, ())' % src)
|
'<Handle noop() at %s:%s>' % src)
|
||||||
|
|
||||||
# partial function
|
# partial function
|
||||||
cb = functools.partial(noop)
|
cb = functools.partial(noop, 1, 2)
|
||||||
h = asyncio.Handle(cb, (), self.loop)
|
h = asyncio.Handle(cb, (3,), self.loop)
|
||||||
filename, lineno = src
|
filename, lineno = src
|
||||||
regex = (r'^Handle\(functools.partial\('
|
regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s>$'
|
||||||
r'<function noop .*>\) at %s:%s, '
|
% (re.escape(filename), lineno))
|
||||||
r'\(\)\)$' % (re.escape(filename), lineno))
|
|
||||||
self.assertRegex(repr(h), regex)
|
self.assertRegex(repr(h), regex)
|
||||||
|
|
||||||
# partial method
|
# partial method
|
||||||
if sys.version_info >= (3, 4):
|
if sys.version_info >= (3, 4):
|
||||||
method = HandleTests.test_repr
|
method = HandleTests.test_handle_repr
|
||||||
cb = functools.partialmethod(method)
|
cb = functools.partialmethod(method)
|
||||||
src = test_utils.get_function_source(method)
|
src = test_utils.get_function_source(method)
|
||||||
h = asyncio.Handle(cb, (), self.loop)
|
h = asyncio.Handle(cb, (), self.loop)
|
||||||
|
|
||||||
filename, lineno = src
|
filename, lineno = src
|
||||||
regex = (r'^Handle\(functools.partialmethod\('
|
cb_regex = r'<function HandleTests.test_handle_repr .*>'
|
||||||
r'<function HandleTests.test_repr .*>, , \) at %s:%s, '
|
cb_regex = (r'functools.partialmethod\(%s, , \)\(\)' % cb_regex)
|
||||||
r'\(\)\)$' % (re.escape(filename), lineno))
|
regex = (r'^<Handle %s at %s:%s>$'
|
||||||
|
% (cb_regex, re.escape(filename), lineno))
|
||||||
self.assertRegex(repr(h), regex)
|
self.assertRegex(repr(h), regex)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TimerTests(unittest.TestCase):
|
class TimerTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.loop = mock.Mock()
|
||||||
|
|
||||||
def test_hash(self):
|
def test_hash(self):
|
||||||
when = time.monotonic()
|
when = time.monotonic()
|
||||||
h = asyncio.TimerHandle(when, lambda: False, (),
|
h = asyncio.TimerHandle(when, lambda: False, (),
|
||||||
|
@ -1858,29 +1860,37 @@ class TimerTests(unittest.TestCase):
|
||||||
self.assertIs(h._args, args)
|
self.assertIs(h._args, args)
|
||||||
self.assertFalse(h._cancelled)
|
self.assertFalse(h._cancelled)
|
||||||
|
|
||||||
r = repr(h)
|
# cancel
|
||||||
self.assertTrue(r.endswith('())'))
|
|
||||||
|
|
||||||
h.cancel()
|
h.cancel()
|
||||||
self.assertTrue(h._cancelled)
|
self.assertTrue(h._cancelled)
|
||||||
|
|
||||||
r = repr(h)
|
|
||||||
self.assertTrue(r.endswith('())<cancelled>'), r)
|
|
||||||
|
|
||||||
|
# when cannot be None
|
||||||
self.assertRaises(AssertionError,
|
self.assertRaises(AssertionError,
|
||||||
asyncio.TimerHandle, None, callback, args,
|
asyncio.TimerHandle, None, callback, args,
|
||||||
mock.Mock())
|
self.loop)
|
||||||
|
|
||||||
|
def test_timer_repr(self):
|
||||||
|
# simple function
|
||||||
|
h = asyncio.TimerHandle(123, noop, (), self.loop)
|
||||||
|
src = test_utils.get_function_source(noop)
|
||||||
|
self.assertEqual(repr(h),
|
||||||
|
'<TimerHandle when=123 noop() at %s:%s>' % src)
|
||||||
|
|
||||||
|
# cancelled handle
|
||||||
|
h.cancel()
|
||||||
|
self.assertEqual(repr(h),
|
||||||
|
'<TimerHandle cancelled when=123 noop() at %s:%s>'
|
||||||
|
% src)
|
||||||
|
|
||||||
def test_timer_comparison(self):
|
def test_timer_comparison(self):
|
||||||
loop = mock.Mock()
|
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
return args
|
return args
|
||||||
|
|
||||||
when = time.monotonic()
|
when = time.monotonic()
|
||||||
|
|
||||||
h1 = asyncio.TimerHandle(when, callback, (), loop)
|
h1 = asyncio.TimerHandle(when, callback, (), self.loop)
|
||||||
h2 = asyncio.TimerHandle(when, callback, (), loop)
|
h2 = asyncio.TimerHandle(when, callback, (), self.loop)
|
||||||
# TODO: Use assertLess etc.
|
# TODO: Use assertLess etc.
|
||||||
self.assertFalse(h1 < h2)
|
self.assertFalse(h1 < h2)
|
||||||
self.assertFalse(h2 < h1)
|
self.assertFalse(h2 < h1)
|
||||||
|
@ -1896,8 +1906,8 @@ class TimerTests(unittest.TestCase):
|
||||||
h2.cancel()
|
h2.cancel()
|
||||||
self.assertFalse(h1 == h2)
|
self.assertFalse(h1 == h2)
|
||||||
|
|
||||||
h1 = asyncio.TimerHandle(when, callback, (), loop)
|
h1 = asyncio.TimerHandle(when, callback, (), self.loop)
|
||||||
h2 = asyncio.TimerHandle(when + 10.0, callback, (), loop)
|
h2 = asyncio.TimerHandle(when + 10.0, callback, (), self.loop)
|
||||||
self.assertTrue(h1 < h2)
|
self.assertTrue(h1 < h2)
|
||||||
self.assertFalse(h2 < h1)
|
self.assertFalse(h2 < h1)
|
||||||
self.assertTrue(h1 <= h2)
|
self.assertTrue(h1 <= h2)
|
||||||
|
@ -1909,7 +1919,7 @@ class TimerTests(unittest.TestCase):
|
||||||
self.assertFalse(h1 == h2)
|
self.assertFalse(h1 == h2)
|
||||||
self.assertTrue(h1 != h2)
|
self.assertTrue(h1 != h2)
|
||||||
|
|
||||||
h3 = asyncio.Handle(callback, (), loop)
|
h3 = asyncio.Handle(callback, (), self.loop)
|
||||||
self.assertIs(NotImplemented, h1.__eq__(h3))
|
self.assertIs(NotImplemented, h1.__eq__(h3))
|
||||||
self.assertIs(NotImplemented, h1.__ne__(h3))
|
self.assertIs(NotImplemented, h1.__ne__(h3))
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Tests for futures.py."""
|
"""Tests for futures.py."""
|
||||||
|
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
|
import re
|
||||||
import threading
|
import threading
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
@ -12,6 +13,12 @@ from asyncio import test_utils
|
||||||
def _fakefunc(f):
|
def _fakefunc(f):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
def first_cb():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def last_cb():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FutureTests(test_utils.TestCase):
|
class FutureTests(test_utils.TestCase):
|
||||||
|
|
||||||
|
@ -95,39 +102,60 @@ class FutureTests(test_utils.TestCase):
|
||||||
# The second "yield from f" does not yield f.
|
# The second "yield from f" does not yield f.
|
||||||
self.assertEqual(next(g), ('C', 42)) # yield 'C', y.
|
self.assertEqual(next(g), ('C', 42)) # yield 'C', y.
|
||||||
|
|
||||||
def test_repr(self):
|
def test_future_repr(self):
|
||||||
f_pending = asyncio.Future(loop=self.loop)
|
f_pending = asyncio.Future(loop=self.loop)
|
||||||
self.assertEqual(repr(f_pending), 'Future<PENDING>')
|
self.assertEqual(repr(f_pending), '<Future pending>')
|
||||||
f_pending.cancel()
|
f_pending.cancel()
|
||||||
|
|
||||||
f_cancelled = asyncio.Future(loop=self.loop)
|
f_cancelled = asyncio.Future(loop=self.loop)
|
||||||
f_cancelled.cancel()
|
f_cancelled.cancel()
|
||||||
self.assertEqual(repr(f_cancelled), 'Future<CANCELLED>')
|
self.assertEqual(repr(f_cancelled), '<Future cancelled>')
|
||||||
|
|
||||||
f_result = asyncio.Future(loop=self.loop)
|
f_result = asyncio.Future(loop=self.loop)
|
||||||
f_result.set_result(4)
|
f_result.set_result(4)
|
||||||
self.assertEqual(repr(f_result), 'Future<result=4>')
|
self.assertEqual(repr(f_result), '<Future finished result=4>')
|
||||||
self.assertEqual(f_result.result(), 4)
|
self.assertEqual(f_result.result(), 4)
|
||||||
|
|
||||||
exc = RuntimeError()
|
exc = RuntimeError()
|
||||||
f_exception = asyncio.Future(loop=self.loop)
|
f_exception = asyncio.Future(loop=self.loop)
|
||||||
f_exception.set_exception(exc)
|
f_exception.set_exception(exc)
|
||||||
self.assertEqual(repr(f_exception), 'Future<exception=RuntimeError()>')
|
self.assertEqual(repr(f_exception), '<Future finished exception=RuntimeError()>')
|
||||||
self.assertIs(f_exception.exception(), exc)
|
self.assertIs(f_exception.exception(), exc)
|
||||||
|
|
||||||
f_few_callbacks = asyncio.Future(loop=self.loop)
|
def func_repr(func):
|
||||||
f_few_callbacks.add_done_callback(_fakefunc)
|
filename, lineno = test_utils.get_function_source(func)
|
||||||
self.assertIn('Future<PENDING, [<function _fakefunc',
|
text = '%s() at %s:%s' % (func.__qualname__, filename, lineno)
|
||||||
repr(f_few_callbacks))
|
return re.escape(text)
|
||||||
f_few_callbacks.cancel()
|
|
||||||
|
f_one_callbacks = asyncio.Future(loop=self.loop)
|
||||||
|
f_one_callbacks.add_done_callback(_fakefunc)
|
||||||
|
fake_repr = func_repr(_fakefunc)
|
||||||
|
self.assertRegex(repr(f_one_callbacks),
|
||||||
|
r'<Future pending cb=\[%s\]>' % fake_repr)
|
||||||
|
f_one_callbacks.cancel()
|
||||||
|
self.assertEqual(repr(f_one_callbacks),
|
||||||
|
'<Future cancelled>')
|
||||||
|
|
||||||
|
f_two_callbacks = asyncio.Future(loop=self.loop)
|
||||||
|
f_two_callbacks.add_done_callback(first_cb)
|
||||||
|
f_two_callbacks.add_done_callback(last_cb)
|
||||||
|
first_repr = func_repr(first_cb)
|
||||||
|
last_repr = func_repr(last_cb)
|
||||||
|
self.assertRegex(repr(f_two_callbacks),
|
||||||
|
r'<Future pending cb=\[%s, %s\]>'
|
||||||
|
% (first_repr, last_repr))
|
||||||
|
|
||||||
f_many_callbacks = asyncio.Future(loop=self.loop)
|
f_many_callbacks = asyncio.Future(loop=self.loop)
|
||||||
for i in range(20):
|
f_many_callbacks.add_done_callback(first_cb)
|
||||||
|
for i in range(8):
|
||||||
f_many_callbacks.add_done_callback(_fakefunc)
|
f_many_callbacks.add_done_callback(_fakefunc)
|
||||||
r = repr(f_many_callbacks)
|
f_many_callbacks.add_done_callback(last_cb)
|
||||||
self.assertIn('Future<PENDING, [<function _fakefunc', r)
|
cb_regex = r'%s, <8 more>, %s' % (first_repr, last_repr)
|
||||||
self.assertIn('<18 more>', r)
|
self.assertRegex(repr(f_many_callbacks),
|
||||||
|
r'<Future pending cb=\[%s\]>' % cb_regex)
|
||||||
f_many_callbacks.cancel()
|
f_many_callbacks.cancel()
|
||||||
|
self.assertEqual(repr(f_many_callbacks),
|
||||||
|
'<Future cancelled>')
|
||||||
|
|
||||||
def test_copy_state(self):
|
def test_copy_state(self):
|
||||||
# Test the internal _copy_state method since it's being directly
|
# Test the internal _copy_state method since it's being directly
|
||||||
|
|
|
@ -26,7 +26,7 @@ def coroutine_function():
|
||||||
class Dummy:
|
class Dummy:
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Dummy()'
|
return '<Dummy>'
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
pass
|
pass
|
||||||
|
@ -122,6 +122,7 @@ class TaskTests(test_utils.TestCase):
|
||||||
yield from []
|
yield from []
|
||||||
return 'abc'
|
return 'abc'
|
||||||
|
|
||||||
|
# test coroutine function
|
||||||
self.assertEqual(notmuch.__name__, 'notmuch')
|
self.assertEqual(notmuch.__name__, 'notmuch')
|
||||||
if PY35:
|
if PY35:
|
||||||
self.assertEqual(notmuch.__qualname__,
|
self.assertEqual(notmuch.__qualname__,
|
||||||
|
@ -131,72 +132,87 @@ class TaskTests(test_utils.TestCase):
|
||||||
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)
|
||||||
|
|
||||||
|
# test coroutine object
|
||||||
gen = notmuch()
|
gen = notmuch()
|
||||||
|
if PY35:
|
||||||
|
coro_qualname = 'TaskTests.test_task_repr.<locals>.notmuch'
|
||||||
|
else:
|
||||||
|
coro_qualname = 'notmuch'
|
||||||
self.assertEqual(gen.__name__, 'notmuch')
|
self.assertEqual(gen.__name__, 'notmuch')
|
||||||
if PY35:
|
if PY35:
|
||||||
self.assertEqual(gen.__qualname__,
|
self.assertEqual(gen.__qualname__,
|
||||||
'TaskTests.test_task_repr.<locals>.notmuch')
|
coro_qualname)
|
||||||
|
|
||||||
|
# test pending Task
|
||||||
t = asyncio.Task(gen, loop=self.loop)
|
t = asyncio.Task(gen, loop=self.loop)
|
||||||
t.add_done_callback(Dummy())
|
t.add_done_callback(Dummy())
|
||||||
|
coro = '%s() at %s' % (coro_qualname, src)
|
||||||
self.assertEqual(repr(t),
|
self.assertEqual(repr(t),
|
||||||
'Task(<notmuch at %s>)<PENDING, [Dummy()]>' % src)
|
'<Task pending %s cb=[<Dummy>()]>' % coro)
|
||||||
|
|
||||||
|
# test cancelling Task
|
||||||
t.cancel() # Does not take immediate effect!
|
t.cancel() # Does not take immediate effect!
|
||||||
self.assertEqual(repr(t),
|
self.assertEqual(repr(t),
|
||||||
'Task(<notmuch at %s>)<CANCELLING, [Dummy()]>' % src)
|
'<Task cancelling %s cb=[<Dummy>()]>' % coro)
|
||||||
|
|
||||||
|
# test cancelled Task
|
||||||
self.assertRaises(asyncio.CancelledError,
|
self.assertRaises(asyncio.CancelledError,
|
||||||
self.loop.run_until_complete, t)
|
self.loop.run_until_complete, t)
|
||||||
|
coro = '%s() done at %s' % (coro_qualname, src)
|
||||||
self.assertEqual(repr(t),
|
self.assertEqual(repr(t),
|
||||||
'Task(<notmuch done at %s:%s>)<CANCELLED>'
|
'<Task cancelled %s>' % coro)
|
||||||
% (filename, lineno))
|
|
||||||
|
|
||||||
|
# test finished Task
|
||||||
t = asyncio.Task(notmuch(), loop=self.loop)
|
t = asyncio.Task(notmuch(), loop=self.loop)
|
||||||
self.loop.run_until_complete(t)
|
self.loop.run_until_complete(t)
|
||||||
self.assertEqual(repr(t),
|
self.assertEqual(repr(t),
|
||||||
"Task(<notmuch done at %s:%s>)<result='abc'>"
|
"<Task finished %s result='abc'>" % coro)
|
||||||
% (filename, lineno))
|
|
||||||
|
|
||||||
def test_task_repr_custom(self):
|
def test_task_repr_coro_decorator(self):
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def notmuch():
|
def notmuch():
|
||||||
pass
|
# notmuch() function doesn't use yield from: it will be wrapped by
|
||||||
|
# @coroutine decorator
|
||||||
|
return 123
|
||||||
|
|
||||||
|
# test coroutine function
|
||||||
self.assertEqual(notmuch.__name__, 'notmuch')
|
self.assertEqual(notmuch.__name__, 'notmuch')
|
||||||
self.assertEqual(notmuch.__module__, __name__)
|
|
||||||
if PY35:
|
if PY35:
|
||||||
self.assertEqual(notmuch.__qualname__,
|
self.assertEqual(notmuch.__qualname__,
|
||||||
'TaskTests.test_task_repr_custom.<locals>.notmuch')
|
'TaskTests.test_task_repr_coro_decorator.<locals>.notmuch')
|
||||||
|
self.assertEqual(notmuch.__module__, __name__)
|
||||||
class T(asyncio.Future):
|
|
||||||
def __repr__(self):
|
|
||||||
return 'T[]'
|
|
||||||
|
|
||||||
class MyTask(asyncio.Task, T):
|
|
||||||
def __repr__(self):
|
|
||||||
return super().__repr__()
|
|
||||||
|
|
||||||
|
# test coroutine object
|
||||||
gen = notmuch()
|
gen = notmuch()
|
||||||
if PY35 or tasks._DEBUG:
|
if PY35:
|
||||||
# On Python >= 3.5, generators now inherit the name of the
|
# On Python >= 3.5, generators now inherit the name of the
|
||||||
# function, as expected, and have a qualified name (__qualname__
|
# function, as expected, and have a qualified name (__qualname__
|
||||||
# attribute). In debug mode, @coroutine decorator uses CoroWrapper
|
# attribute).
|
||||||
# which gets its name (__name__ attribute) from the wrapped
|
|
||||||
# coroutine function.
|
|
||||||
coro_name = 'notmuch'
|
coro_name = 'notmuch'
|
||||||
|
coro_qualname = 'TaskTests.test_task_repr_coro_decorator.<locals>.notmuch'
|
||||||
|
elif tasks._DEBUG:
|
||||||
|
# In debug mode, @coroutine decorator uses CoroWrapper which gets
|
||||||
|
# its name (__name__ attribute) from the wrapped coroutine
|
||||||
|
# function.
|
||||||
|
coro_name = coro_qualname = '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
|
||||||
coro_name = 'coro'
|
coro_name = coro_qualname = 'coro'
|
||||||
self.assertEqual(gen.__name__, coro_name)
|
self.assertEqual(gen.__name__, coro_name)
|
||||||
if PY35:
|
if PY35:
|
||||||
self.assertEqual(gen.__qualname__,
|
self.assertEqual(gen.__qualname__, coro_qualname)
|
||||||
'TaskTests.test_task_repr_custom.<locals>.notmuch')
|
|
||||||
|
|
||||||
t = MyTask(gen, loop=self.loop)
|
# format the coroutine object
|
||||||
filename = gen.gi_code.co_filename
|
code = gen.gi_code
|
||||||
lineno = gen.gi_frame.f_lineno
|
coro = ('%s() at %s:%s'
|
||||||
self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (coro_name, filename, lineno))
|
% (coro_qualname, code.co_filename, code.co_firstlineno))
|
||||||
|
|
||||||
|
# test pending Task
|
||||||
|
t = asyncio.Task(gen, loop=self.loop)
|
||||||
|
t.add_done_callback(Dummy())
|
||||||
|
self.assertEqual(repr(t),
|
||||||
|
'<Task pending %s cb=[<Dummy>()]>' % coro)
|
||||||
|
|
||||||
def test_task_basics(self):
|
def test_task_basics(self):
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
Loading…
Reference in New Issue