summaryrefslogtreecommitdiff
path: root/Cython/Runtime/refnanny.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'Cython/Runtime/refnanny.pyx')
-rw-r--r--Cython/Runtime/refnanny.pyx130
1 files changed, 64 insertions, 66 deletions
diff --git a/Cython/Runtime/refnanny.pyx b/Cython/Runtime/refnanny.pyx
index d4b873fe9..bc72f62c6 100644
--- a/Cython/Runtime/refnanny.pyx
+++ b/Cython/Runtime/refnanny.pyx
@@ -1,6 +1,6 @@
# cython: language_level=3, auto_pickle=False
-from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF
+from cpython.ref cimport PyObject, Py_INCREF, Py_CLEAR, Py_XDECREF, Py_XINCREF
from cpython.exc cimport PyErr_Fetch, PyErr_Restore
from cpython.pystate cimport PyThreadState_Get
@@ -10,6 +10,9 @@ loglevel = 0
reflog = []
cdef log(level, action, obj, lineno):
+ if reflog is None:
+ # can happen during finalisation
+ return
if loglevel >= level:
reflog.append((lineno, action, id(obj)))
@@ -29,7 +32,7 @@ cdef class Context(object):
self.refs = {} # id -> (count, [lineno])
self.errors = []
- cdef regref(self, obj, lineno, bint is_null):
+ cdef regref(self, obj, Py_ssize_t lineno, bint is_null):
log(LOG_ALL, u'regref', u"<NULL>" if is_null else obj, lineno)
if is_null:
self.errors.append(f"NULL argument on line {lineno}")
@@ -39,7 +42,7 @@ cdef class Context(object):
self.refs[id_] = (count + 1, linenumbers)
linenumbers.append(lineno)
- cdef bint delref(self, obj, lineno, bint is_null) except -1:
+ cdef bint delref(self, obj, Py_ssize_t lineno, bint is_null) except -1:
# returns whether it is ok to do the decref operation
log(LOG_ALL, u'delref', u"<NULL>" if is_null else obj, lineno)
if is_null:
@@ -50,12 +53,11 @@ cdef class Context(object):
if count == 0:
self.errors.append(f"Too many decrefs on line {lineno}, reference acquired on lines {linenumbers!r}")
return False
- elif count == 1:
+ if count == 1:
del self.refs[id_]
- return True
else:
self.refs[id_] = (count - 1, linenumbers)
- return True
+ return True
cdef end(self):
if self.refs:
@@ -63,122 +65,118 @@ cdef class Context(object):
for count, linenos in self.refs.itervalues():
msg += f"\n ({count}) acquired on lines: {u', '.join([f'{x}' for x in linenos])}"
self.errors.append(msg)
- if self.errors:
- return u"\n".join([u'REFNANNY: '+error for error in self.errors])
- else:
- return None
+ return u"\n".join([f'REFNANNY: {error}' for error in self.errors]) if self.errors else None
+
-cdef void report_unraisable(object e=None):
+cdef void report_unraisable(filename, Py_ssize_t lineno, object e=None):
try:
if e is None:
import sys
e = sys.exc_info()[1]
- print(f"refnanny raised an exception: {e}")
- except:
- pass # We absolutely cannot exit with an exception
+ print(f"refnanny raised an exception from {filename}:{lineno}: {e}")
+ finally:
+ return # We absolutely cannot exit with an exception
+
# All Python operations must happen after any existing
# exception has been fetched, in case we are called from
# exception-handling code.
-cdef PyObject* SetupContext(char* funcname, int lineno, char* filename) except NULL:
+cdef PyObject* SetupContext(char* funcname, Py_ssize_t lineno, char* filename) except NULL:
if Context is None:
# Context may be None during finalize phase.
# In that case, we don't want to be doing anything fancy
# like caching and resetting exceptions.
return NULL
cdef (PyObject*) type = NULL, value = NULL, tb = NULL, result = NULL
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
PyErr_Fetch(&type, &value, &tb)
try:
ctx = Context(funcname, lineno, filename)
Py_INCREF(ctx)
result = <PyObject*>ctx
except Exception, e:
- report_unraisable(e)
+ report_unraisable(filename, lineno, e)
PyErr_Restore(type, value, tb)
return result
-cdef void GOTREF(PyObject* ctx, PyObject* p_obj, int lineno):
+cdef void GOTREF(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno):
if ctx == NULL: return
cdef (PyObject*) type = NULL, value = NULL, tb = NULL
PyErr_Fetch(&type, &value, &tb)
try:
- try:
- if p_obj is NULL:
- (<Context>ctx).regref(None, lineno, True)
- else:
- (<Context>ctx).regref(<object>p_obj, lineno, False)
- except:
- report_unraisable()
+ (<Context>ctx).regref(
+ <object>p_obj if p_obj is not NULL else None,
+ lineno,
+ is_null=p_obj is NULL,
+ )
except:
- # __Pyx_GetException may itself raise errors
- pass
- PyErr_Restore(type, value, tb)
+ report_unraisable((<Context>ctx).filename, lineno=(<Context>ctx).start)
+ finally:
+ PyErr_Restore(type, value, tb)
+ return # swallow any exceptions
-cdef int GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, int lineno):
+cdef bint GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno):
if ctx == NULL: return 1
cdef (PyObject*) type = NULL, value = NULL, tb = NULL
cdef bint decref_ok = False
PyErr_Fetch(&type, &value, &tb)
try:
- try:
- if p_obj is NULL:
- decref_ok = (<Context>ctx).delref(None, lineno, True)
- else:
- decref_ok = (<Context>ctx).delref(<object>p_obj, lineno, False)
- except:
- report_unraisable()
+ decref_ok = (<Context>ctx).delref(
+ <object>p_obj if p_obj is not NULL else None,
+ lineno,
+ is_null=p_obj is NULL,
+ )
except:
- # __Pyx_GetException may itself raise errors
- pass
- PyErr_Restore(type, value, tb)
- return decref_ok
+ report_unraisable((<Context>ctx).filename, lineno=(<Context>ctx).start)
+ finally:
+ PyErr_Restore(type, value, tb)
+ return decref_ok # swallow any exceptions
-cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, int lineno):
+cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno):
GIVEREF_and_report(ctx, p_obj, lineno)
-cdef void INCREF(PyObject* ctx, PyObject* obj, int lineno):
+cdef void INCREF(PyObject* ctx, PyObject* obj, Py_ssize_t lineno):
Py_XINCREF(obj)
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
GOTREF(ctx, obj, lineno)
-cdef void DECREF(PyObject* ctx, PyObject* obj, int lineno):
+cdef void DECREF(PyObject* ctx, PyObject* obj, Py_ssize_t lineno):
if GIVEREF_and_report(ctx, obj, lineno):
Py_XDECREF(obj)
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
cdef void FinishContext(PyObject** ctx):
if ctx == NULL or ctx[0] == NULL: return
cdef (PyObject*) type = NULL, value = NULL, tb = NULL
cdef object errors = None
cdef Context context
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
PyErr_Fetch(&type, &value, &tb)
try:
- try:
- context = <Context>ctx[0]
- errors = context.end()
- if errors:
- print(f"{context.filename.decode('latin1')}: {context.name.decode('latin1')}()")
- print(errors)
- context = None
- except:
- report_unraisable()
+ context = <Context>ctx[0]
+ errors = context.end()
+ if errors:
+ print(f"{context.filename.decode('latin1')}: {context.name.decode('latin1')}()")
+ print(errors)
+ context = None
except:
- # __Pyx_GetException may itself raise errors
- pass
- Py_XDECREF(ctx[0])
- ctx[0] = NULL
- PyErr_Restore(type, value, tb)
+ report_unraisable(
+ context.filename if context is not None else None,
+ lineno=context.start if context is not None else 0,
+ )
+ finally:
+ Py_CLEAR(ctx[0])
+ PyErr_Restore(type, value, tb)
+ return # swallow any exceptions
ctypedef struct RefNannyAPIStruct:
- void (*INCREF)(PyObject*, PyObject*, int)
- void (*DECREF)(PyObject*, PyObject*, int)
- void (*GOTREF)(PyObject*, PyObject*, int)
- void (*GIVEREF)(PyObject*, PyObject*, int)
- PyObject* (*SetupContext)(char*, int, char*) except NULL
- void (*FinishContext)(PyObject**)
+ void (*INCREF)(PyObject*, PyObject*, Py_ssize_t)
+ void (*DECREF)(PyObject*, PyObject*, Py_ssize_t)
+ void (*GOTREF)(PyObject*, PyObject*, Py_ssize_t)
+ void (*GIVEREF)(PyObject*, PyObject*, Py_ssize_t)
+ PyObject* (*SetupContext)(char*, Py_ssize_t, char*) except NULL
+ void (*FinishContext)(PyObject**)
cdef RefNannyAPIStruct api
api.INCREF = INCREF