Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

195 lines (172 sloc) 6.26 kb
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
cdef PyObject* 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, context.name)
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)
Jump to Line
Something went wrong with that request. Please try again.