diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-07-10 20:32:58 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-07-10 20:32:58 (GMT) |
commit | f68bd88aa61ae6214d3dc5552a7e3f9cf1401507 (patch) | |
tree | b976d878139b73d846ea1498aa867589a7f51b13 | |
parent | eb43214427b1ae6d7095bdd2333e9bc2220f9449 (diff) | |
download | cpython-f68bd88aa61ae6214d3dc5552a7e3f9cf1401507.zip cpython-f68bd88aa61ae6214d3dc5552a7e3f9cf1401507.tar.gz cpython-f68bd88aa61ae6214d3dc5552a7e3f9cf1401507.tar.bz2 |
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.
-rw-r--r-- | Lib/asyncio/events.py | 18 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_events.py | 66 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_futures.py | 6 |
3 files changed, 74 insertions, 16 deletions
diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 1f5e582..bddd7e3 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -82,14 +82,20 @@ class Handle: self._source_traceback = None def __repr__(self): - info = [] + info = [self.__class__.__name__] if self._cancelled: info.append('cancelled') - info.append(_format_callback(self._callback, self._args)) - return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) + 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>' % ' '.join(info) def cancel(self): self._cancelled = True + self._callback = None + self._args = None def _run(self): try: @@ -125,7 +131,11 @@ class TimerHandle(Handle): if self._cancelled: info.append('cancelled') 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)) def __hash__(self): diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index e5c5729..e04c287 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1810,27 +1810,30 @@ class HandleTests(test_utils.TestCase): wd['h'] = h # Would fail without __weakref__ slot. def test_handle_repr(self): + self.loop.get_debug.return_value = False + # simple function - h = asyncio.Handle(noop, (), self.loop) - src = test_utils.get_function_source(noop) + h = asyncio.Handle(noop, (1, 2), self.loop) + filename, lineno = test_utils.get_function_source(noop) self.assertEqual(repr(h), - '<Handle noop() at %s:%s>' % src) + '<Handle noop(1, 2) at %s:%s>' + % (filename, lineno)) # cancelled handle h.cancel() self.assertEqual(repr(h), - '<Handle cancelled noop() at %s:%s>' % src) + '<Handle cancelled>') # decorated function cb = asyncio.coroutine(noop) h = asyncio.Handle(cb, (), self.loop) self.assertEqual(repr(h), - '<Handle noop() at %s:%s>' % src) + '<Handle noop() at %s:%s>' + % (filename, lineno)) # partial function cb = functools.partial(noop, 1, 2) h = asyncio.Handle(cb, (3,), self.loop) - filename, lineno = src regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s>$' % (re.escape(filename), lineno)) self.assertRegex(repr(h), regex) @@ -1839,16 +1842,33 @@ class HandleTests(test_utils.TestCase): if sys.version_info >= (3, 4): method = HandleTests.test_handle_repr 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) - filename, lineno = src cb_regex = r'<function HandleTests.test_handle_repr .*>' cb_regex = (r'functools.partialmethod\(%s, , \)\(\)' % cb_regex) regex = (r'^<Handle %s at %s:%s>$' % (cb_regex, re.escape(filename), lineno)) 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): loop = asyncio.get_event_loop_policy().new_event_loop() loop.set_debug(True) @@ -1894,7 +1914,7 @@ class TimerTests(unittest.TestCase): def callback(*args): return args - args = () + args = (1, 2, 3) when = time.monotonic() h = asyncio.TimerHandle(when, callback, args, mock.Mock()) self.assertIs(h._callback, callback) @@ -1904,7 +1924,8 @@ class TimerTests(unittest.TestCase): # cancel h.cancel() self.assertTrue(h._cancelled) - + self.assertIsNone(h._callback) + self.assertIsNone(h._args) # when cannot be None self.assertRaises(AssertionError, @@ -1912,6 +1933,8 @@ class TimerTests(unittest.TestCase): self.loop) def test_timer_repr(self): + self.loop.get_debug.return_value = False + # simple function h = asyncio.TimerHandle(123, noop, (), self.loop) src = test_utils.get_function_source(noop) @@ -1921,8 +1944,27 @@ class TimerTests(unittest.TestCase): # cancelled handle h.cancel() self.assertEqual(repr(h), - '<TimerHandle cancelled when=123 noop() at %s:%s>' - % src) + '<TimerHandle cancelled when=123>') + + 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 callback(*args): diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 157adb7..50e9414 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -299,6 +299,12 @@ class FutureTests(test_utils.TestCase): @mock.patch('asyncio.base_events.logger') 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) def memory_error(): |