diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-06-27 13:35:15 +0200 |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-06-27 13:35:15 +0200 |
commit | 7852d4cd33ee6a97b1ad469c5f5421c8973bdac2 (patch) | |
tree | 92867746e8eea1f3753e57102bdc6a592ce8fa65 /asyncio/events.py | |
parent | c0e13fee423ed9943466d6e738b27606147c1032 (diff) | |
download | trollius-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.py | 18 |
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): |