Skip to content

Commit 28c4d5e

Browse files
committed
Fixed line_profiler._line_profiler import
line_profiler._line_profiler.pyx::call_callback() - Fixed obscure bug occurring when the function tries to import the `line_profiler._line_profiler` module object when `coverage.py` tracing is on - More verbose error messages because C code doesn't show up in stack traces
1 parent eb318ca commit 28c4d5e

File tree

1 file changed

+40
-16
lines changed

1 file changed

+40
-16
lines changed

line_profiler/_line_profiler.pyx

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ cdef extern from "Python.h":
101101
cdef int PyFrame_GetLineNumber(PyFrameObject *frame)
102102

103103
cdef extern from *:
104-
"""
104+
r"""
105105
typedef struct TraceCallback {
106106
/* Notes:
107107
* - These fields are synonymous with the corresponding
@@ -171,6 +171,27 @@ cdef extern from *:
171171
|| callback->c_traceobj == NULL);
172172
}
173173
174+
#if PY_VERSION_HEX >= 0x030D00a1 // 3.13.0a0
175+
#define add_module_ref PyImport_AddModuleRef
176+
#else
177+
inline PyObject *add_module_ref(const char *name) {
178+
PyObject *mod = NULL, *name_str = NULL;
179+
name_str = PyUnicode_FromString(name);
180+
if (name_str == NULL) goto cleanup;
181+
mod = PyImport_AddModuleObject(name_str);
182+
Py_XINCREF(mod);
183+
cleanup:
184+
Py_XDECREF(name_str);
185+
return mod;
186+
}
187+
#endif
188+
#define RAISE_IN_CALL(xc, const_msg) \
189+
PyErr_SetString(xc,\
190+
"in `line_profiler._line_profiler.call_callback()`: " \
191+
const_msg)
192+
#define THIS_MODULE "line_profiler._line_profiler"
193+
#define DISABLE_CALLBACK "disable_line_events"
194+
174195
int call_callback(TraceCallback *callback, PyFrameObject *py_frame,
175196
int what, PyObject *arg) {
176197
/* Call the cached trace callback `callback` where appropriate,
@@ -222,33 +243,36 @@ cdef extern from *:
222243
nullify_callback(&after);
223244
restore_callback(&before);
224245
225-
// Check if a frame-local callback has disabled future line
226-
// events, and revert the change in such a case (while
227-
// withholding future line events from the callback)
246+
// Check if a callback has disabled future line events for the
247+
// frame, and if so, revert the change while withholding future
248+
// line events from the callback
228249
if (!(py_frame->f_trace_lines)
229250
&& f_trace_lines != py_frame->f_trace_lines) {
230251
py_frame->f_trace_lines = f_trace_lines;
231252
if (py_frame->f_trace != NULL && py_frame->f_trace != Py_None) {
232-
mod = PyImport_ImportModule("line_profiler._line_profiler");
253+
// FIXME: can we get more performance by stashing a
254+
// somewhat permanent reference to
255+
// `line_profiler._line_profiler.disable_line_events()`
256+
// somewhere?
257+
mod = add_module_ref(THIS_MODULE);
233258
if (mod == NULL) {
234-
PyErr_SetString(PyExc_ImportError,
235-
"cannot import "
236-
"`line_profiler._line_profiler`");
259+
RAISE_IN_CALL(PyExc_ImportError,
260+
"cannot import `" THIS_MODULE "`");
237261
result = -1;
238262
goto cleanup;
239263
}
240-
dle = PyObject_GetAttrString(mod, "disable_line_events");
264+
dle = PyObject_GetAttrString(mod, DISABLE_CALLBACK);
241265
if (dle == NULL) {
242-
PyErr_SetString(PyExc_AttributeError,
243-
"`line_profiler._line_profiler` has no "
244-
"attribute `disable_line_events`");
266+
RAISE_IN_CALL(PyExc_AttributeError,
267+
"`line_profiler._line_profiler` has no "
268+
"attribute `" DISABLE_CALLBACK "`");
245269
result = -1;
246270
goto cleanup;
247271
}
248-
// Note: don't DECREF the pointer! Nothing else is
249-
// holding a reference to it.
250-
f_trace = PyObject_CallFunctionObjArgs(dle, py_frame->f_trace,
251-
NULL);
272+
// Note: DON'T `Py_[X]DECREF()` the pointer! Nothing
273+
// else is holding a reference to it.
274+
f_trace = PyObject_CallFunctionObjArgs(
275+
dle, py_frame->f_trace, NULL);
252276
if (f_trace == NULL) {
253277
// No need to raise another exception, it's already
254278
// raised in the call

0 commit comments

Comments
 (0)