1 from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF 2 from cpython.exc cimport PyErr_Fetch, PyErr_Restore 3 from cpython.pystate cimport PyThreadState_Get 4 5 cimport cython 6 7 loglevel = 0 8 reflog = [] 9 10 cdef log(level, action, obj, lineno): 11 if loglevel >= level: 12 reflog.append((lineno, action, id(obj))) 13 14 LOG_NONE, LOG_ALL = range(2) 15 16 @cython.final 17 cdef class Context(object): 18 cdef readonly object name, filename 19 cdef readonly dict refs 20 cdef readonly list errors 21 cdef readonly Py_ssize_t start 22 23 def __cinit__(self, name, line=0, filename=None): 24 self.name = name 25 self.start = line 26 self.filename = filename 27 self.refs = {} # id -> (count, [lineno]) 28 self.errors = [] 29 30 cdef regref(self, obj, lineno, bint is_null): 31 log(LOG_ALL, u'regref', u"<NULL>" if is_null else obj, lineno) 32 if is_null: 33 self.errors.append(u"NULL argument on line %d" % lineno) 34 return 35 id_ = id(obj) 36 count, linenumbers = self.refs.get(id_, (0, [])) 37 self.refs[id_] = (count + 1, linenumbers) 38 linenumbers.append(lineno) 39 40 cdef bint delref(self, obj, lineno, bint is_null) except -1: 41 # returns whether it is ok to do the decref operation 42 log(LOG_ALL, u'delref', u"<NULL>" if is_null else obj, lineno) 43 if is_null: 44 self.errors.append(u"NULL argument on line %d" % lineno) 45 return False 46 id_ = id(obj) 47 count, linenumbers = self.refs.get(id_, (0, [])) 48 if count == 0: 49 self.errors.append(u"Too many decrefs on line %d, reference acquired on lines %r" % 50 (lineno, linenumbers)) 51 return False 52 elif count == 1: 53 del self.refs[id_] 54 return True 55 else: 56 self.refs[id_] = (count - 1, linenumbers) 57 return True 58 59 cdef end(self): 60 if self.refs: 61 msg = u"References leaked:" 62 for count, linenos in self.refs.itervalues(): 63 msg += u"\n (%d) acquired on lines: %s" % (count, u", ".join([u"%d" % x for x in linenos])) 64 self.errors.append(msg) 65 if self.errors: 66 return u"\n".join([u'REFNANNY: '+error for error in self.errors]) 67 else: 68 return None 69 70 cdef void report_unraisable(object e=None): 71 try: 72 if e is None: 73 import sys 74 e = sys.exc_info()[1] 75 print u"refnanny raised an exception: %s" % e 76 except: 77 pass # We absolutely cannot exit with an exception 78 79 # All Python operations must happen after any existing 80 # exception has been fetched, in case we are called from 81 # exception-handling code. 82 83 cdef PyObject* SetupContext(char* funcname, int lineno, char* filename) except NULL: 84 if Context is None: 85 # Context may be None during finalize phase. 86 # In that case, we don't want to be doing anything fancy 87 # like caching and resetting exceptions. 88 return NULL 89 cdef (PyObject*) type = NULL, value = NULL, tb = NULL, result = NULL 90 PyThreadState_Get() 91 PyErr_Fetch(&type, &value, &tb) 92 try: 93 ctx = Context(funcname, lineno, filename) 94 Py_INCREF(ctx) 95 result = <PyObject*>ctx 96 except Exception, e: 97 report_unraisable(e) 98 PyErr_Restore(type, value, tb) 99 return result 100 101 cdef void GOTREF(PyObject* ctx, PyObject* p_obj, int lineno): 102 if ctx == NULL: return 103 cdef (PyObject*) type = NULL, value = NULL, tb = NULL 104 PyErr_Fetch(&type, &value, &tb) 105 try: 106 try: 107 if p_obj is NULL: 108 (<Context>ctx).regref(None, lineno, True) 109 else: 110 (<Context>ctx).regref(<object>p_obj, lineno, False) 111 except: 112 report_unraisable() 113 except: 114 # __Pyx_GetException may itself raise errors 115 pass 116 PyErr_Restore(type, value, tb) 117 118 cdef int GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, int lineno): 119 if ctx == NULL: return 1 120 cdef (PyObject*) type = NULL, value = NULL, tb = NULL 121 cdef bint decref_ok = False 122 PyErr_Fetch(&type, &value, &tb) 123 try: 124 try: 125 if p_obj is NULL: 126 decref_ok = (<Context>ctx).delref(None, lineno, True) 127 else: 128 decref_ok = (<Context>ctx).delref(<object>p_obj, lineno, False) 129 except: 130 report_unraisable() 131 except: 132 # __Pyx_GetException may itself raise errors 133 pass 134 PyErr_Restore(type, value, tb) 135 return decref_ok 136 137 cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, int lineno): 138 GIVEREF_and_report(ctx, p_obj, lineno) 139 140 cdef void INCREF(PyObject* ctx, PyObject* obj, int lineno): 141 Py_XINCREF(obj) 142 PyThreadState_Get() 143 GOTREF(ctx, obj, lineno) 144 145 cdef void DECREF(PyObject* ctx, PyObject* obj, int lineno): 146 if GIVEREF_and_report(ctx, obj, lineno): 147 Py_XDECREF(obj) 148 PyThreadState_Get() 149 150 cdef void FinishContext(PyObject** ctx): 151 if ctx == NULL or ctx[0] == NULL: return 152 cdef (PyObject*) type = NULL, value = NULL, tb = NULL 153 cdef object errors = None 154 cdef Context context 155 PyThreadState_Get() 156 PyErr_Fetch(&type, &value, &tb) 157 try: 158 try: 159 context = <Context>ctx[0] 160 errors = context.end() 161 if errors: 162 print u"%s: %s()" % (context.filename.decode('latin1'), 163 context.name.decode('latin1')) 164 print errors 165 context = None 166 except: 167 report_unraisable() 168 except: 169 # __Pyx_GetException may itself raise errors 170 pass 171 Py_XDECREF(ctx[0]) 172 ctx[0] = NULL 173 PyErr_Restore(type, value, tb) 174 175 ctypedef struct RefNannyAPIStruct: 176 void (*INCREF)(PyObject*, PyObject*, int) 177 void (*DECREF)(PyObject*, PyObject*, int) 178 void (*GOTREF)(PyObject*, PyObject*, int) 179 void (*GIVEREF)(PyObject*, PyObject*, int) 180 PyObject* (*SetupContext)(char*, int, char*) except NULL 181 void (*FinishContext)(PyObject**) 182 183 cdef RefNannyAPIStruct api 184 api.INCREF = INCREF 185 api.DECREF = DECREF 186 api.GOTREF = GOTREF 187 api.GIVEREF = GIVEREF 188 api.SetupContext = SetupContext 189 api.FinishContext = FinishContext 190 191 cdef extern from "Python.h": 192 object PyLong_FromVoidPtr(void*) 193 194 RefNannyAPI = PyLong_FromVoidPtr(<void*>&api) 195