(Merge 3.4) asyncio: sync with Tulip
- Issues #21936, #21163: Fix sporadic failures of test_future_exception_never_retrieved() - Handle.cancel() now clears references to callback and args - In debug mode, repr(Handle) now contains the location where the Handle was created.
This commit is contained in:
commit
61d368e9c9
|
@ -82,14 +82,20 @@ class Handle:
|
||||||
self._source_traceback = None
|
self._source_traceback = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
info = []
|
info = [self.__class__.__name__]
|
||||||
if self._cancelled:
|
if self._cancelled:
|
||||||
info.append('cancelled')
|
info.append('cancelled')
|
||||||
info.append(_format_callback(self._callback, self._args))
|
if self._callback is not None:
|
||||||
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
info.append(_format_callback(self._callback, self._args))
|
||||||
|
if self._source_traceback:
|
||||||
|
frame = self._source_traceback[-1]
|
||||||
|
info.append('created at %s:%s' % (frame[0], frame[1]))
|
||||||
|
return '<%s>' % ' '.join(info)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self._cancelled = True
|
self._cancelled = True
|
||||||
|
self._callback = None
|
||||||
|
self._args = None
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
try:
|
try:
|
||||||
|
@ -125,7 +131,11 @@ class TimerHandle(Handle):
|
||||||
if self._cancelled:
|
if self._cancelled:
|
||||||
info.append('cancelled')
|
info.append('cancelled')
|
||||||
info.append('when=%s' % self._when)
|
info.append('when=%s' % self._when)
|
||||||
info.append(_format_callback(self._callback, self._args))
|
if self._callback is not None:
|
||||||
|
info.append(_format_callback(self._callback, self._args))
|
||||||
|
if self._source_traceback:
|
||||||
|
frame = self._source_traceback[-1]
|
||||||
|
info.append('created at %s:%s' % (frame[0], frame[1]))
|
||||||
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
|
|
|
@ -1810,27 +1810,30 @@ class HandleTests(test_utils.TestCase):
|
||||||
wd['h'] = h # Would fail without __weakref__ slot.
|
wd['h'] = h # Would fail without __weakref__ slot.
|
||||||
|
|
||||||
def test_handle_repr(self):
|
def test_handle_repr(self):
|
||||||
|
self.loop.get_debug.return_value = False
|
||||||
|
|
||||||
# simple function
|
# simple function
|
||||||
h = asyncio.Handle(noop, (), self.loop)
|
h = asyncio.Handle(noop, (1, 2), self.loop)
|
||||||
src = test_utils.get_function_source(noop)
|
filename, lineno = test_utils.get_function_source(noop)
|
||||||
self.assertEqual(repr(h),
|
self.assertEqual(repr(h),
|
||||||
'<Handle noop() at %s:%s>' % src)
|
'<Handle noop(1, 2) at %s:%s>'
|
||||||
|
% (filename, lineno))
|
||||||
|
|
||||||
# cancelled handle
|
# cancelled handle
|
||||||
h.cancel()
|
h.cancel()
|
||||||
self.assertEqual(repr(h),
|
self.assertEqual(repr(h),
|
||||||
'<Handle cancelled noop() at %s:%s>' % src)
|
'<Handle cancelled>')
|
||||||
|
|
||||||
# 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>'
|
||||||
|
% (filename, lineno))
|
||||||
|
|
||||||
# partial function
|
# partial function
|
||||||
cb = functools.partial(noop, 1, 2)
|
cb = functools.partial(noop, 1, 2)
|
||||||
h = asyncio.Handle(cb, (3,), self.loop)
|
h = asyncio.Handle(cb, (3,), self.loop)
|
||||||
filename, lineno = src
|
|
||||||
regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s>$'
|
regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s>$'
|
||||||
% (re.escape(filename), lineno))
|
% (re.escape(filename), lineno))
|
||||||
self.assertRegex(repr(h), regex)
|
self.assertRegex(repr(h), regex)
|
||||||
|
@ -1839,16 +1842,33 @@ class HandleTests(test_utils.TestCase):
|
||||||
if sys.version_info >= (3, 4):
|
if sys.version_info >= (3, 4):
|
||||||
method = HandleTests.test_handle_repr
|
method = HandleTests.test_handle_repr
|
||||||
cb = functools.partialmethod(method)
|
cb = functools.partialmethod(method)
|
||||||
src = test_utils.get_function_source(method)
|
filename, lineno = test_utils.get_function_source(method)
|
||||||
h = asyncio.Handle(cb, (), self.loop)
|
h = asyncio.Handle(cb, (), self.loop)
|
||||||
|
|
||||||
filename, lineno = src
|
|
||||||
cb_regex = r'<function HandleTests.test_handle_repr .*>'
|
cb_regex = r'<function HandleTests.test_handle_repr .*>'
|
||||||
cb_regex = (r'functools.partialmethod\(%s, , \)\(\)' % cb_regex)
|
cb_regex = (r'functools.partialmethod\(%s, , \)\(\)' % cb_regex)
|
||||||
regex = (r'^<Handle %s at %s:%s>$'
|
regex = (r'^<Handle %s at %s:%s>$'
|
||||||
% (cb_regex, re.escape(filename), lineno))
|
% (cb_regex, re.escape(filename), lineno))
|
||||||
self.assertRegex(repr(h), regex)
|
self.assertRegex(repr(h), regex)
|
||||||
|
|
||||||
|
def test_handle_repr_debug(self):
|
||||||
|
self.loop.get_debug.return_value = True
|
||||||
|
|
||||||
|
# simple function
|
||||||
|
create_filename = __file__
|
||||||
|
create_lineno = sys._getframe().f_lineno + 1
|
||||||
|
h = asyncio.Handle(noop, (1, 2), self.loop)
|
||||||
|
filename, lineno = test_utils.get_function_source(noop)
|
||||||
|
self.assertEqual(repr(h),
|
||||||
|
'<Handle noop(1, 2) at %s:%s created at %s:%s>'
|
||||||
|
% (filename, lineno, create_filename, create_lineno))
|
||||||
|
|
||||||
|
# cancelled handle
|
||||||
|
h.cancel()
|
||||||
|
self.assertEqual(repr(h),
|
||||||
|
'<Handle cancelled created at %s:%s>'
|
||||||
|
% (create_filename, create_lineno))
|
||||||
|
|
||||||
def test_handle_source_traceback(self):
|
def test_handle_source_traceback(self):
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||||
loop.set_debug(True)
|
loop.set_debug(True)
|
||||||
|
@ -1894,7 +1914,7 @@ class TimerTests(unittest.TestCase):
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
return args
|
return args
|
||||||
|
|
||||||
args = ()
|
args = (1, 2, 3)
|
||||||
when = time.monotonic()
|
when = time.monotonic()
|
||||||
h = asyncio.TimerHandle(when, callback, args, mock.Mock())
|
h = asyncio.TimerHandle(when, callback, args, mock.Mock())
|
||||||
self.assertIs(h._callback, callback)
|
self.assertIs(h._callback, callback)
|
||||||
|
@ -1904,7 +1924,8 @@ class TimerTests(unittest.TestCase):
|
||||||
# cancel
|
# cancel
|
||||||
h.cancel()
|
h.cancel()
|
||||||
self.assertTrue(h._cancelled)
|
self.assertTrue(h._cancelled)
|
||||||
|
self.assertIsNone(h._callback)
|
||||||
|
self.assertIsNone(h._args)
|
||||||
|
|
||||||
# when cannot be None
|
# when cannot be None
|
||||||
self.assertRaises(AssertionError,
|
self.assertRaises(AssertionError,
|
||||||
|
@ -1912,6 +1933,8 @@ class TimerTests(unittest.TestCase):
|
||||||
self.loop)
|
self.loop)
|
||||||
|
|
||||||
def test_timer_repr(self):
|
def test_timer_repr(self):
|
||||||
|
self.loop.get_debug.return_value = False
|
||||||
|
|
||||||
# simple function
|
# simple function
|
||||||
h = asyncio.TimerHandle(123, noop, (), self.loop)
|
h = asyncio.TimerHandle(123, noop, (), self.loop)
|
||||||
src = test_utils.get_function_source(noop)
|
src = test_utils.get_function_source(noop)
|
||||||
|
@ -1921,8 +1944,27 @@ class TimerTests(unittest.TestCase):
|
||||||
# cancelled handle
|
# cancelled handle
|
||||||
h.cancel()
|
h.cancel()
|
||||||
self.assertEqual(repr(h),
|
self.assertEqual(repr(h),
|
||||||
'<TimerHandle cancelled when=123 noop() at %s:%s>'
|
'<TimerHandle cancelled when=123>')
|
||||||
% src)
|
|
||||||
|
def test_timer_repr_debug(self):
|
||||||
|
self.loop.get_debug.return_value = True
|
||||||
|
|
||||||
|
# simple function
|
||||||
|
create_filename = __file__
|
||||||
|
create_lineno = sys._getframe().f_lineno + 1
|
||||||
|
h = asyncio.TimerHandle(123, noop, (), self.loop)
|
||||||
|
filename, lineno = test_utils.get_function_source(noop)
|
||||||
|
self.assertEqual(repr(h),
|
||||||
|
'<TimerHandle when=123 noop() '
|
||||||
|
'at %s:%s created at %s:%s>'
|
||||||
|
% (filename, lineno, create_filename, create_lineno))
|
||||||
|
|
||||||
|
# cancelled handle
|
||||||
|
h.cancel()
|
||||||
|
self.assertEqual(repr(h),
|
||||||
|
'<TimerHandle cancelled when=123 created at %s:%s>'
|
||||||
|
% (create_filename, create_lineno))
|
||||||
|
|
||||||
|
|
||||||
def test_timer_comparison(self):
|
def test_timer_comparison(self):
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
|
|
|
@ -299,6 +299,12 @@ class FutureTests(test_utils.TestCase):
|
||||||
|
|
||||||
@mock.patch('asyncio.base_events.logger')
|
@mock.patch('asyncio.base_events.logger')
|
||||||
def test_future_exception_never_retrieved(self, m_log):
|
def test_future_exception_never_retrieved(self, m_log):
|
||||||
|
# FIXME: Python issue #21163, other tests may "leak" pending task which
|
||||||
|
# emit a warning when they are destroyed by the GC
|
||||||
|
support.gc_collect()
|
||||||
|
m_log.error.reset_mock()
|
||||||
|
# ---
|
||||||
|
|
||||||
self.loop.set_debug(True)
|
self.loop.set_debug(True)
|
||||||
|
|
||||||
def memory_error():
|
def memory_error():
|
||||||
|
|
Loading…
Reference in New Issue