diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2013-12-19 21:42:40 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2013-12-19 21:42:40 (GMT) |
commit | 4c3c699e623ac8d942a4ede93b13d8dc4146e0ec (patch) | |
tree | 83c2a34c6e9041756e5803544a915f6a1a6274c1 | |
parent | 994bf4332f03e2f7a7c642337853404f1f192071 (diff) | |
download | cpython-4c3c699e623ac8d942a4ede93b13d8dc4146e0ec.zip cpython-4c3c699e623ac8d942a4ede93b13d8dc4146e0ec.tar.gz cpython-4c3c699e623ac8d942a4ede93b13d8dc4146e0ec.tar.bz2 |
Close #19967: Thanks to the PEP 442, asyncio.Future can use a destructor in
Python 3.4 to log "uncatched" exceptions, instead of the dedicated
_TracebackLogger class.
-rw-r--r-- | Lib/asyncio/futures.py | 32 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_tasks.py | 2 |
2 files changed, 27 insertions, 7 deletions
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index dd3e718..1be3117 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -7,6 +7,7 @@ __all__ = ['CancelledError', 'TimeoutError', import concurrent.futures._base import logging +import sys import traceback from . import events @@ -17,6 +18,8 @@ _PENDING = 'PENDING' _CANCELLED = 'CANCELLED' _FINISHED = 'FINISHED' +_PY34 = sys.version_info >= (3, 4) + # TODO: Do we really want to depend on concurrent.futures internals? Error = concurrent.futures._base.Error CancelledError = concurrent.futures.CancelledError @@ -128,7 +131,8 @@ class Future: _blocking = False # proper use of future (yield vs yield from) - _tb_logger = None + _traceback = None # Used for Python 3.4 and later + _tb_logger = None # Used for Python 3.3 only def __init__(self, *, loop=None): """Initialize the future. @@ -162,6 +166,12 @@ class Future: res += '<{}>'.format(self._state) return res + if _PY34: + def __del__(self): + if self._traceback is not None: + logger.error('Future/Task exception was never retrieved:\n%s', + ''.join(self._traceback)) + def cancel(self): """Cancel the future and schedule callbacks. @@ -214,9 +224,10 @@ class Future: raise CancelledError if self._state != _FINISHED: raise InvalidStateError('Result is not ready.') + self._traceback = None if self._tb_logger is not None: self._tb_logger.clear() - self._tb_logger = None + self._tb_logger = None if self._exception is not None: raise self._exception return self._result @@ -233,9 +244,10 @@ class Future: raise CancelledError if self._state != _FINISHED: raise InvalidStateError('Exception is not set.') + self._traceback = None if self._tb_logger is not None: self._tb_logger.clear() - self._tb_logger = None + self._tb_logger = None return self._exception def add_done_callback(self, fn): @@ -286,12 +298,18 @@ class Future: if self._state != _PENDING: raise InvalidStateError('{}: {!r}'.format(self._state, self)) self._exception = exception - self._tb_logger = _TracebackLogger(exception) self._state = _FINISHED self._schedule_callbacks() - # Arrange for the logger to be activated after all callbacks - # have had a chance to call result() or exception(). - self._loop.call_soon(self._tb_logger.activate) + if _PY34: + self._traceback = traceback.format_exception( + exception.__class__, + exception, + exception.__traceback__) + else: + self._tb_logger = _TracebackLogger(exception) + # Arrange for the logger to be activated after all callbacks + # have had a chance to call result() or exception(). + self._loop.call_soon(self._tb_logger.activate) # Truly internal methods. diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 5470da1..e303f50 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -1352,6 +1352,7 @@ class GatherTestsBase: c.set_result(3) d.cancel() e.set_exception(RuntimeError()) + e.exception() def test_return_exceptions(self): a, b, c, d = [futures.Future(loop=self.one_loop) for i in range(4)] @@ -1431,6 +1432,7 @@ class FutureGatherTests(GatherTestsBase, unittest.TestCase): c.set_result(3) d.cancel() e.set_exception(RuntimeError()) + e.exception() def test_result_exception_one_cancellation(self): a, b, c, d, e, f = [futures.Future(loop=self.one_loop) |