summaryrefslogtreecommitdiff
path: root/asyncio/events.py
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-06-27 13:35:15 +0200
committerVictor Stinner <victor.stinner@gmail.com>2014-06-27 13:35:15 +0200
commit7852d4cd33ee6a97b1ad469c5f5421c8973bdac2 (patch)
tree92867746e8eea1f3753e57102bdc6a592ce8fa65 /asyncio/events.py
parentc0e13fee423ed9943466d6e738b27606147c1032 (diff)
downloadtrollius-7852d4cd33ee6a97b1ad469c5f5421c8973bdac2.tar.gz
Tulip issue #137: In debug mode, save traceback where Future, Task and Handle
objects are created. Pass the traceback to call_exception_handler() in the 'source_traceback' key. The traceback is truncated to hide internal calls in asyncio, show only the traceback from user code. Add tests for the new source_traceback, and a test for the 'Future/Task exception was never retrieved' log.
Diffstat (limited to 'asyncio/events.py')
-rw-r--r--asyncio/events.py18
1 files changed, 14 insertions, 4 deletions
diff --git a/asyncio/events.py b/asyncio/events.py
index 58c6bd5..b389cfb 100644
--- a/asyncio/events.py
+++ b/asyncio/events.py
@@ -11,6 +11,7 @@ __all__ = ['AbstractEventLoopPolicy',
import functools
import inspect
import subprocess
+import traceback
import threading
import socket
import sys
@@ -66,7 +67,8 @@ def _format_callback(func, args, suffix=''):
class Handle:
"""Object returned by callback registration methods."""
- __slots__ = ['_callback', '_args', '_cancelled', '_loop', '__weakref__']
+ __slots__ = ('_callback', '_args', '_cancelled', '_loop',
+ '_source_traceback', '__weakref__')
def __init__(self, callback, args, loop):
assert not isinstance(callback, Handle), 'A Handle is not a callback'
@@ -74,6 +76,10 @@ class Handle:
self._callback = callback
self._args = args
self._cancelled = False
+ if self._loop.get_debug():
+ self._source_traceback = traceback.extract_stack(sys._getframe(1))
+ else:
+ self._source_traceback = None
def __repr__(self):
info = []
@@ -91,11 +97,14 @@ class Handle:
except Exception as exc:
cb = _format_callback(self._callback, self._args)
msg = 'Exception in callback {}'.format(cb)
- self._loop.call_exception_handler({
+ context = {
'message': msg,
'exception': exc,
'handle': self,
- })
+ }
+ if self._source_traceback:
+ context['source_traceback'] = self._source_traceback
+ self._loop.call_exception_handler(context)
self = None # Needed to break cycles when an exception occurs.
@@ -107,7 +116,8 @@ class TimerHandle(Handle):
def __init__(self, when, callback, args, loop):
assert when is not None
super().__init__(callback, args, loop)
-
+ if self._source_traceback:
+ del self._source_traceback[-1]
self._when = when
def __repr__(self):