| Index: third_party/cython/src/Cython/Runtime/refnanny.pyx
|
| diff --git a/third_party/cython/src/Cython/Runtime/refnanny.pyx b/third_party/cython/src/Cython/Runtime/refnanny.pyx
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..80ec3f4bafca5db7d97a838fb9d215e220c3a22a
|
| --- /dev/null
|
| +++ b/third_party/cython/src/Cython/Runtime/refnanny.pyx
|
| @@ -0,0 +1,194 @@
|
| +from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF
|
| +from cpython.exc cimport PyErr_Fetch, PyErr_Restore
|
| +from cpython.pystate cimport PyThreadState_Get
|
| +
|
| +cimport cython
|
| +
|
| +loglevel = 0
|
| +reflog = []
|
| +
|
| +cdef log(level, action, obj, lineno):
|
| + if loglevel >= level:
|
| + reflog.append((lineno, action, id(obj)))
|
| +
|
| +LOG_NONE, LOG_ALL = range(2)
|
| +
|
| +@cython.final
|
| +cdef class Context(object):
|
| + cdef readonly object name, filename
|
| + cdef readonly dict refs
|
| + cdef readonly list errors
|
| + cdef readonly Py_ssize_t start
|
| +
|
| + def __cinit__(self, name, line=0, filename=None):
|
| + self.name = name
|
| + self.start = line
|
| + self.filename = filename
|
| + self.refs = {} # id -> (count, [lineno])
|
| + self.errors = []
|
| +
|
| + cdef regref(self, obj, lineno, bint is_null):
|
| + log(LOG_ALL, u'regref', u"<NULL>" if is_null else obj, lineno)
|
| + if is_null:
|
| + self.errors.append(u"NULL argument on line %d" % lineno)
|
| + return
|
| + id_ = id(obj)
|
| + count, linenumbers = self.refs.get(id_, (0, []))
|
| + self.refs[id_] = (count + 1, linenumbers)
|
| + linenumbers.append(lineno)
|
| +
|
| + cdef bint delref(self, obj, 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:
|
| + self.errors.append(u"NULL argument on line %d" % lineno)
|
| + return False
|
| + id_ = id(obj)
|
| + count, linenumbers = self.refs.get(id_, (0, []))
|
| + if count == 0:
|
| + self.errors.append(u"Too many decrefs on line %d, reference acquired on lines %r" %
|
| + (lineno, linenumbers))
|
| + return False
|
| + elif count == 1:
|
| + del self.refs[id_]
|
| + return True
|
| + else:
|
| + self.refs[id_] = (count - 1, linenumbers)
|
| + return True
|
| +
|
| + cdef end(self):
|
| + if self.refs:
|
| + msg = u"References leaked:"
|
| + for count, linenos in self.refs.itervalues():
|
| + msg += u"\n (%d) acquired on lines: %s" % (count, u", ".join([u"%d" % 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
|
| +
|
| +cdef void report_unraisable(object e=None):
|
| + try:
|
| + if e is None:
|
| + import sys
|
| + e = sys.exc_info()[1]
|
| + print u"refnanny raised an exception: %s" % e
|
| + except:
|
| + pass # 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:
|
| + 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()
|
| + PyErr_Fetch(&type, &value, &tb)
|
| + try:
|
| + ctx = Context(funcname, lineno, filename)
|
| + Py_INCREF(ctx)
|
| + result = <PyObject*>ctx
|
| + except Exception, e:
|
| + report_unraisable(e)
|
| + PyErr_Restore(type, value, tb)
|
| + return result
|
| +
|
| +cdef void GOTREF(PyObject* ctx, PyObject* p_obj, int 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()
|
| + except:
|
| + # __Pyx_GetException may itself raise errors
|
| + pass
|
| + PyErr_Restore(type, value, tb)
|
| +
|
| +cdef int GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, int 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()
|
| + except:
|
| + # __Pyx_GetException may itself raise errors
|
| + pass
|
| + PyErr_Restore(type, value, tb)
|
| + return decref_ok
|
| +
|
| +cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, int lineno):
|
| + GIVEREF_and_report(ctx, p_obj, lineno)
|
| +
|
| +cdef void INCREF(PyObject* ctx, PyObject* obj, int lineno):
|
| + Py_XINCREF(obj)
|
| + PyThreadState_Get()
|
| + GOTREF(ctx, obj, lineno)
|
| +
|
| +cdef void DECREF(PyObject* ctx, PyObject* obj, int lineno):
|
| + if GIVEREF_and_report(ctx, obj, lineno):
|
| + Py_XDECREF(obj)
|
| + PyThreadState_Get()
|
| +
|
| +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()
|
| + PyErr_Fetch(&type, &value, &tb)
|
| + try:
|
| + try:
|
| + context = <Context>ctx[0]
|
| + errors = context.end()
|
| + if errors:
|
| + print u"%s: %s()" % (context.filename.decode('latin1'),
|
| + context.name.decode('latin1'))
|
| + print errors
|
| + context = None
|
| + except:
|
| + report_unraisable()
|
| + except:
|
| + # __Pyx_GetException may itself raise errors
|
| + pass
|
| + Py_XDECREF(ctx[0])
|
| + ctx[0] = NULL
|
| + PyErr_Restore(type, value, tb)
|
| +
|
| +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**)
|
| +
|
| +cdef RefNannyAPIStruct api
|
| +api.INCREF = INCREF
|
| +api.DECREF = DECREF
|
| +api.GOTREF = GOTREF
|
| +api.GIVEREF = GIVEREF
|
| +api.SetupContext = SetupContext
|
| +api.FinishContext = FinishContext
|
| +
|
| +cdef extern from "Python.h":
|
| + object PyLong_FromVoidPtr(void*)
|
| +
|
| +RefNannyAPI = PyLong_FromVoidPtr(<void*>&api)
|
|
|