]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Close #19967: Thanks to the PEP 442, asyncio.Future can use a destructor in
authorVictor Stinner <victor.stinner@gmail.com>
Thu, 19 Dec 2013 21:42:40 +0000 (22:42 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Thu, 19 Dec 2013 21:42:40 +0000 (22:42 +0100)
Python 3.4 to log "uncatched" exceptions, instead of the dedicated
_TracebackLogger class.

Lib/asyncio/futures.py
Lib/test/test_asyncio/test_tasks.py

index dd3e718d9a501ebb23156451173a80f442335d7d..1be3117ebbe62270daeaeb4010e7718eefa35fc8 100644 (file)
@@ -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.
 
index 5470da15430613c07107e1147dd8f051b591b4e8..e303f505df2ccd49ad5a70fb2f09016aae1e868f 100644 (file)
@@ -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)