Commit 3a8b41ae authored by scoder's avatar scoder Committed by GitHub

Merge pull request #1774 from traff/master

Fix "NoneType object is not callable" exception when using a tracing …
parents 8ecf2e96 bcb676e0
...@@ -198,7 +198,8 @@ ...@@ -198,7 +198,8 @@
PyThreadState *tstate; \ PyThreadState *tstate; \
PyGILState_STATE state = PyGILState_Ensure(); \ PyGILState_STATE state = PyGILState_Ensure(); \
tstate = __Pyx_PyThreadState_Current; \ tstate = __Pyx_PyThreadState_Current; \
if (unlikely(tstate->use_tracing && tstate->c_tracefunc)) { \ if (unlikely(tstate->use_tracing && tstate->c_tracefunc \
&& __pyx_frame->f_trace != Py_None)) { \
ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \ ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \
} \ } \
PyGILState_Release(state); \ PyGILState_Release(state); \
...@@ -206,7 +207,8 @@ ...@@ -206,7 +207,8 @@
} \ } \
} else { \ } else { \
PyThreadState* tstate = __Pyx_PyThreadState_Current; \ PyThreadState* tstate = __Pyx_PyThreadState_Current; \
if (unlikely(tstate->use_tracing && tstate->c_tracefunc)) { \ if (unlikely(tstate->use_tracing && tstate->c_tracefunc \
&& __pyx_frame->f_trace != Py_None)) { \
int ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \ int ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \
if (unlikely(ret)) goto_error; \ if (unlikely(ret)) goto_error; \
} \ } \
...@@ -216,7 +218,8 @@ ...@@ -216,7 +218,8 @@
#define __Pyx_TraceLine(lineno, nogil, goto_error) \ #define __Pyx_TraceLine(lineno, nogil, goto_error) \
if (likely(!__Pyx_use_tracing)); else { \ if (likely(!__Pyx_use_tracing)); else { \
PyThreadState* tstate = __Pyx_PyThreadState_Current; \ PyThreadState* tstate = __Pyx_PyThreadState_Current; \
if (unlikely(tstate->use_tracing && tstate->c_tracefunc)) { \ if (unlikely(tstate->use_tracing && tstate->c_tracefunc \
&& __pyx_frame->f_trace != Py_None)) { \
int ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \ int ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \
if (unlikely(ret)) goto_error; \ if (unlikely(ret)) goto_error; \
} \ } \
......
...@@ -30,26 +30,73 @@ map_trace_types = { ...@@ -30,26 +30,73 @@ map_trace_types = {
}.get }.get
cdef int _trace_func(PyObject* _traceobj, PyFrameObject* _frame, int what, PyObject* arg) except -1: cdef int trace_trampoline(PyObject* _traceobj, PyFrameObject* _frame, int what, PyObject* _arg) except -1:
frame, traceobj = <object>_frame, <object>_traceobj frame = <object>_frame
traceobj.append((map_trace_types(what), frame.f_lineno - frame.f_code.co_firstlineno)) traceobj = <object>_traceobj if _traceobj else None
arg = <object>_arg if _arg else None
if what == PyTrace_CALL:
callback = traceobj
else:
callback = frame.f_trace
if callback is None:
return 0 return 0
result = callback(frame, what, arg)
cdef int _failing_call_trace_func(PyObject* _traceobj, PyFrameObject* _frame, int what, PyObject* arg) except -1: frame.f_trace = result
if what == PyTrace_CALL:
if result is None:
PyEval_SetTrace(NULL, None)
return 0
else:
return 0
def _create_trace_func(trace):
def _trace_func(frame, event, arg):
trace.append((map_trace_types(event), frame.f_lineno -
frame.f_code.co_firstlineno))
return _trace_func
return _trace_func
def _create_failing_call_trace_func(trace):
func = _create_trace_func(trace)
def _trace_func(frame, event, arg):
if event == PyTrace_CALL:
raise ValueError("failing call trace!") raise ValueError("failing call trace!")
return _trace_func(_traceobj, _frame, what, arg)
func(frame, event, arg)
return _trace_func
return _trace_func
cdef int _failing_line_trace_func(PyObject* _traceobj, PyFrameObject* _frame, int what, PyObject* arg) except -1: def _create__failing_line_trace_func(trace):
if what == PyTrace_LINE and _traceobj: func = _create_trace_func(trace)
frame, traceobj = <object>_frame, <object>_traceobj def _trace_func(frame, event, arg):
if traceobj and traceobj[0] == frame.f_code.co_name: if event == PyTrace_LINE and trace:
if trace and trace[0] == frame.f_code.co_name:
# first line in the right function => fail! # first line in the right function => fail!
raise ValueError("failing line trace!") raise ValueError("failing line trace!")
return _trace_func(_traceobj, _frame, what, arg)
func(frame, event, arg)
return _trace_func
return _trace_func
def _create_disable_tracing(trace):
func = _create_trace_func(trace)
def _trace_func(frame, event, arg):
if frame.f_lineno - frame.f_code.co_firstlineno == 2:
return None
func(frame, event, arg)
return _trace_func
return _trace_func
def cy_add(a,b): def cy_add(a,b):
x = a + b # 1 x = a + b # 1
...@@ -87,7 +134,7 @@ def run_trace(func, *args): ...@@ -87,7 +134,7 @@ def run_trace(func, *args):
[('line', 2), ('line', 5), ('return', 5)] [('line', 2), ('line', 5), ('return', 5)]
""" """
trace = [] trace = []
PyEval_SetTrace(<Py_tracefunc>_trace_func, trace) PyEval_SetTrace(<Py_tracefunc>trace_trampoline, _create_trace_func(trace))
try: try:
func(*args) func(*args)
finally: finally:
...@@ -105,7 +152,7 @@ def fail_on_call_trace(func, *args): ...@@ -105,7 +152,7 @@ def fail_on_call_trace(func, *args):
ValueError: failing call trace! ValueError: failing call trace!
""" """
trace = [] trace = []
PyEval_SetTrace(<Py_tracefunc>_failing_call_trace_func, trace) PyEval_SetTrace(<Py_tracefunc>trace_trampoline, _create_failing_call_trace_func(trace))
try: try:
func(*args) func(*args)
finally: finally:
...@@ -142,7 +189,7 @@ def fail_on_line_trace(fail_func): ...@@ -142,7 +189,7 @@ def fail_on_line_trace(fail_func):
cdef int x = 1 cdef int x = 1
trace = ['NO ERROR'] trace = ['NO ERROR']
exception = None exception = None
PyEval_SetTrace(<Py_tracefunc>_failing_line_trace_func, trace) PyEval_SetTrace(<Py_tracefunc>trace_trampoline, _create__failing_line_trace_func(trace))
try: try:
x += 1 x += 1
cy_add(1, 2) cy_add(1, 2)
...@@ -161,3 +208,24 @@ def fail_on_line_trace(fail_func): ...@@ -161,3 +208,24 @@ def fail_on_line_trace(fail_func):
else: else:
assert x == 5 assert x == 5
return trace return trace
def disable_trace(func, *args):
"""
>>> def py_add(a,b):
... x = a+b
... return x
>>> disable_trace(py_add, 1, 2)
[('call', 0), ('line', 1)]
>>> disable_trace(cy_add, 1, 2)
[('call', 0), ('line', 1)]
>>> disable_trace(cy_add_with_nogil, 1, 2)
[('call', 0), ('line', 1)]
"""
trace = []
PyEval_SetTrace(<Py_tracefunc>trace_trampoline, _create_disable_tracing(trace))
try:
func(*args)
finally:
PyEval_SetTrace(NULL, None)
return trace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment