diff --git a/Cython/Runtime/refnanny.pyx b/Cython/Runtime/refnanny.pyx index a80ec988052..8a76cb9ad10 100644 --- a/Cython/Runtime/refnanny.pyx +++ b/Cython/Runtime/refnanny.pyx @@ -50,14 +50,20 @@ cdef extern from *: loglevel = 0 reflog = [] -cdef log(level, action, obj, lineno): - if reflog is None: - # can happen during finalisation - return - if loglevel >= level: +cdef int log(int level, action, obj, lineno) except -1: + if ( loglevel) >= level: + if reflog is None: + # can happen during finalisation + return 0 reflog.append((lineno, action, id(obj))) + return 0 LOG_NONE, LOG_ALL = range(2) +cdef int _LOG_NONE = LOG_NONE +cdef int _LOG_ALL = LOG_ALL + +cdef object NO_REFS = (0, None) + @cython.final cdef class Context(object): @@ -78,30 +84,35 @@ cdef class Context(object): def __dealloc__(self): __Pyx_refnanny_free_lock(self.lock) - cdef acquire_lock(self): + cdef void acquire_lock(self) noexcept: __Pyx_refnanny_lock_acquire(self.lock) - cdef release_lock(self): + cdef void release_lock(self) noexcept: __Pyx_refnanny_lock_release(self.lock) - cdef regref(self, obj, Py_ssize_t lineno, bint is_null): - log(LOG_ALL, u'regref', u"" if is_null else obj, lineno) + cdef int regref(self, obj, Py_ssize_t lineno, bint is_null) except -1: + log(_LOG_ALL, u'regref', u"" if is_null else obj, lineno) if is_null: self.errors.append(f"NULL argument on line {lineno}") - return + return 0 id_ = id(obj) - count, linenumbers = self.refs.get(id_, (0, [])) + count, linenumbers = self.refs.get(id_, NO_REFS) + if linenumbers is None: + linenumbers = [] self.refs[id_] = (count + 1, linenumbers) linenumbers.append(lineno) + return 0 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"" if is_null else obj, lineno) + log(_LOG_ALL, u'delref', u"" if is_null else obj, lineno) if is_null: self.errors.append(f"NULL argument on line {lineno}") return False id_ = id(obj) - count, linenumbers = self.refs.get(id_, (0, [])) + count, linenumbers = self.refs.get(id_, NO_REFS) + if linenumbers is None: + linenumbers = [] if count == 0: self.errors.append(f"Too many decrefs on line {lineno}, reference acquired on lines {linenumbers!r}") return False @@ -114,13 +125,13 @@ cdef class Context(object): cdef end(self): if self.refs: msg = u"References leaked:" - for count, linenos in self.refs.itervalues(): + for count, linenos in self.refs.values(): msg += f"\n ({count}) acquired on lines: {u', '.join([f'{x}' for x in linenos])}" self.errors.append(msg) return u"\n".join([f'REFNANNY: {error}' for error in self.errors]) if self.errors else None -cdef void report_unraisable(filename, Py_ssize_t lineno, object e=None): +cdef void report_unraisable(filename, Py_ssize_t lineno, object e=None) noexcept: try: if e is None: import sys @@ -144,7 +155,7 @@ cdef PyObject* SetupContext(char* funcname, Py_ssize_t lineno, char* filename) e PyThreadState_Get() # Check that we hold the GIL PyErr_Fetch(&type, &value, &tb) try: - ctx = Context(funcname, lineno, filename) + ctx = Context.__new__(Context, funcname, lineno, filename) Py_INCREF(ctx) result = ctx except Exception, e: @@ -152,41 +163,43 @@ cdef PyObject* SetupContext(char* funcname, Py_ssize_t lineno, char* filename) e PyErr_Restore(type, value, tb) return result -cdef void GOTREF(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno): - if ctx == NULL: return +cdef void GOTREF(PyObject* _ctx, PyObject* p_obj, Py_ssize_t lineno): + if _ctx == NULL: return cdef (PyObject*) type = NULL, value = NULL, tb = NULL - (ctx).acquire_lock() + cdef Context ctx = _ctx + ctx.acquire_lock() PyErr_Fetch(&type, &value, &tb) try: - (ctx).regref( + ctx.regref( p_obj if p_obj is not NULL else None, lineno, is_null=p_obj is NULL, ) except: - report_unraisable((ctx).filename, lineno=(ctx).start) + report_unraisable(ctx.filename, lineno=ctx.start) finally: PyErr_Restore(type, value, tb) - (ctx).release_lock() + ctx.release_lock() return # swallow any exceptions -cdef bint GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno): - if ctx == NULL: return 1 +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 - (ctx).acquire_lock() + cdef Context ctx = _ctx + ctx.acquire_lock() PyErr_Fetch(&type, &value, &tb) try: - decref_ok = (ctx).delref( + decref_ok = ctx.delref( p_obj if p_obj is not NULL else None, lineno, is_null=p_obj is NULL, ) except: - report_unraisable((ctx).filename, lineno=(ctx).start) + report_unraisable(ctx.filename, lineno=ctx.start) finally: PyErr_Restore(type, value, tb) - (ctx).release_lock() + ctx.release_lock() return decref_ok # swallow any exceptions cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno):