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 @@
PyThreadState *tstate; \
PyGILState_STATE state = PyGILState_Ensure(); \
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); \
} \
PyGILState_Release(state); \
......@@ -206,7 +207,8 @@
} \
} else { \
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); \
if (unlikely(ret)) goto_error; \
} \
......@@ -216,7 +218,8 @@
#define __Pyx_TraceLine(lineno, nogil, goto_error) \
if (likely(!__Pyx_use_tracing)); else { \
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); \
if (unlikely(ret)) goto_error; \
} \
......
......@@ -30,26 +30,73 @@ map_trace_types = {
}.get
cdef int _trace_func(PyObject* _traceobj, PyFrameObject* _frame, int what, PyObject* arg) except -1:
frame, traceobj = <object>_frame, <object>_traceobj
traceobj.append((map_trace_types(what), frame.f_lineno - frame.f_code.co_firstlineno))
cdef int trace_trampoline(PyObject* _traceobj, PyFrameObject* _frame, int what, PyObject* _arg) except -1:
frame = <object>_frame
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
result = callback(frame, what, arg)
cdef int _failing_call_trace_func(PyObject* _traceobj, PyFrameObject* _frame, int what, PyObject* arg) except -1:
if what == PyTrace_CALL:
frame.f_trace = result
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!")
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:
if what == PyTrace_LINE and _traceobj:
frame, traceobj = <object>_frame, <object>_traceobj
if traceobj and traceobj[0] == frame.f_code.co_name:
def _create__failing_line_trace_func(trace):
func = _create_trace_func(trace)
def _trace_func(frame, event, arg):
if event == PyTrace_LINE and trace:
if trace and trace[0] == frame.f_code.co_name:
# first line in the right function => fail!
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):
x = a + b # 1
......@@ -87,7 +134,7 @@ def run_trace(func, *args):
[('line', 2), ('line', 5), ('return', 5)]
"""
trace = []
PyEval_SetTrace(<Py_tracefunc>_trace_func, trace)
PyEval_SetTrace(<Py_tracefunc>trace_trampoline, _create_trace_func(trace))
try:
func(*args)
finally:
......@@ -105,7 +152,7 @@ def fail_on_call_trace(func, *args):
ValueError: failing call trace!
"""
trace = []
PyEval_SetTrace(<Py_tracefunc>_failing_call_trace_func, trace)
PyEval_SetTrace(<Py_tracefunc>trace_trampoline, _create_failing_call_trace_func(trace))
try:
func(*args)
finally:
......@@ -142,7 +189,7 @@ def fail_on_line_trace(fail_func):
cdef int x = 1
trace = ['NO ERROR']
exception = None
PyEval_SetTrace(<Py_tracefunc>_failing_line_trace_func, trace)
PyEval_SetTrace(<Py_tracefunc>trace_trampoline, _create__failing_line_trace_func(trace))
try:
x += 1
cy_add(1, 2)
......@@ -161,3 +208,24 @@ def fail_on_line_trace(fail_func):
else:
assert x == 5
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