Skip to content

Commit 8246724

Browse files
committed
Log tracebakcs for handles in debug mode
1 parent 10cefd7 commit 8246724

File tree

4 files changed

+76
-10
lines changed

4 files changed

+76
-10
lines changed

tests/test_base.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,44 @@ def cb():
6464

6565
self.assertFalse(self.loop.is_closed())
6666

67+
def test_calls_debug_reporting(self):
68+
def run_test(debug, meth, stack_adj):
69+
context = None
70+
71+
def handler(loop, ctx):
72+
nonlocal context
73+
context = ctx
74+
75+
self.loop.set_debug(debug)
76+
self.loop.set_exception_handler(handler)
77+
78+
def cb():
79+
1 / 0
80+
81+
meth(cb)
82+
self.assertIsNone(context)
83+
self.loop.run_until_complete(asyncio.sleep(0.05, loop=self.loop))
84+
85+
self.assertIs(type(context['exception']), ZeroDivisionError)
86+
self.assertTrue(context['message'].startswith(
87+
'Exception in callback'))
88+
89+
if debug:
90+
tb = context['source_traceback']
91+
self.assertEqual(tb[-1 + stack_adj].name, 'run_test')
92+
else:
93+
self.assertFalse('source_traceback' in context)
94+
95+
for flag in (True, False):
96+
for meth_name, meth, stack_adj in (
97+
('call_soon',
98+
self.loop.call_soon, 0),
99+
('call_later', # `-1` accounts for lambda
100+
lambda *args: self.loop.call_later(0.01, *args), -1)
101+
):
102+
with self.subTest(debug=flag, meth_name=meth_name):
103+
run_test(flag, meth, stack_adj)
104+
67105
def test_now_update(self):
68106
async def run():
69107
st = self.loop.time()

uvloop/cbhandles.pxd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ cdef class Handle:
1010

1111
object __weakref__
1212

13+
readonly _source_traceback
14+
1315
cdef inline _set_loop(self, Loop loop)
1416
cdef inline _run(self)
1517
cdef _cancel(self)
@@ -23,5 +25,7 @@ cdef class TimerHandle:
2325
Loop loop
2426
object __weakref__
2527

28+
readonly _source_traceback
29+
2630
cdef _run(self)
2731
cdef _cancel(self)

uvloop/cbhandles.pyx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ cdef class Handle:
44
def __cinit__(self):
55
self.cancelled = 0
66
self.cb_type = 0
7+
self._source_traceback = None
78

89
cdef inline _set_loop(self, Loop loop):
910
self.loop = loop
1011
IF DEBUG:
1112
loop._debug_cb_handles_total += 1
1213
loop._debug_cb_handles_count += 1
14+
if loop._debug:
15+
self._source_traceback = tb_extract_stack(sys_getframe(0))
1316

1417
IF DEBUG:
1518
def __dealloc__(self):
@@ -78,10 +81,16 @@ cdef class Handle:
7881
else:
7982
msg = 'Exception in callback {}'.format(self.meth_name)
8083

81-
self.loop.call_exception_handler({
84+
context = {
8285
'message': msg,
83-
'exception': ex
84-
})
86+
'exception': ex,
87+
'handle': self,
88+
}
89+
90+
if self._source_traceback is not None:
91+
context['source_traceback'] = self._source_traceback
92+
93+
self.loop.call_exception_handler(context)
8594

8695
finally:
8796
self.loop._executing_py_code = old_exec_py_code
@@ -117,6 +126,9 @@ cdef class TimerHandle:
117126
self.args = args
118127
self.closed = 0
119128

129+
if loop._debug:
130+
self._source_traceback = tb_extract_stack(sys_getframe(0))
131+
120132
self.timer = UVTimer.new(
121133
loop, <method_t*>&self._run, self, delay)
122134

@@ -173,10 +185,16 @@ cdef class TimerHandle:
173185
else:
174186
callback()
175187
except Exception as ex:
176-
self.loop.call_exception_handler({
188+
context = {
177189
'message': 'Exception in callback {}'.format(callback),
178-
'exception': ex
179-
})
190+
'exception': ex,
191+
'handle': self,
192+
}
193+
194+
if self._source_traceback is not None:
195+
context['source_traceback'] = self._source_traceback
196+
197+
self.loop.call_exception_handler(context)
180198
finally:
181199
self.loop._executing_py_code = old_exec_py_code
182200
Py_DECREF(self)

uvloop/loop.pyx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,10 +1587,16 @@ cdef class Loop:
15871587
if key in {'message', 'exception'}:
15881588
continue
15891589
value = context[key]
1590-
try:
1591-
value = repr(value)
1592-
except Exception as ex:
1593-
value = 'Exception in __repr__ {!r}'.format(ex)
1590+
if key == 'source_traceback':
1591+
tb = ''.join(tb_format_list(value))
1592+
value = 'Object created at (most recent call last):\n'
1593+
value += tb.rstrip()
1594+
else:
1595+
try:
1596+
value = repr(value)
1597+
except Exception as ex:
1598+
value = ('Exception in __repr__ {!r}; '
1599+
'value type: {!r}'.format(ex, type(value)))
15941600
log_lines.append('{}: {}'.format(key, value))
15951601

15961602
aio_logger.error('\n'.join(log_lines), exc_info=exc_info)

0 commit comments

Comments
 (0)