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
10 cdef log
(level
, action
, obj
, lineno
):
12 reflog
.append
((lineno
, action
, id(obj
)))
14 LOG_NONE
, LOG_ALL
= range(2)
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
23 def __cinit__
(self, name
, line
=0, filename
=None
):
26 self.filename
= filename
27 self.refs
= {} # id -> (count, [lineno])
30 cdef regref
(self, obj
, lineno
, bint is_null
):
31 log
(LOG_ALL
, u
'regref', u
"<NULL>" if is_null
else obj
, lineno
)
33 self.errors
.append
(u
"NULL argument on line %d" % lineno
)
36 count
, linenumbers
= self.refs
.get
(id_
, (0, []))
37 self.refs
[id_
] = (count
+ 1, linenumbers
)
38 linenumbers
.append
(lineno
)
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
)
44 self.errors
.append
(u
"NULL argument on line %d" % lineno
)
47 count
, linenumbers
= self.refs
.get
(id_
, (0, []))
49 self.errors
.append
(u
"Too many decrefs on line %d, reference acquired on lines %r" %
50 (lineno
, linenumbers
))
56 self.refs
[id_
] = (count
- 1, linenumbers
)
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
)
66 return u
"\n".join
([u
'REFNANNY: '+error
for error
in self.errors
])
70 cdef void report_unraisable
(object e
=None
):
75 print u
"refnanny raised an exception: %s" % e
77 pass # We absolutely cannot exit with an exception
79 # All Python operations must happen after any existing
80 # exception has been fetched, in case we are called from
81 # exception-handling code.
83 cdef PyObject
* SetupContext
(char* funcname
, int lineno
, char* filename
) except NULL
:
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.
89 cdef (PyObject
*) type = NULL
, value
= NULL
, tb
= NULL
, result
= NULL
91 PyErr_Fetch
(&type, &value
, &tb
)
93 ctx
= Context
(funcname
, lineno
, filename
)
95 result
= <PyObject
*>ctx
98 PyErr_Restore
(type, value
, tb
)
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
)
108 (<Context
>ctx
).regref
(None
, lineno
, True
)
110 (<Context
>ctx
).regref
(<object>p_obj
, lineno
, False
)
114 # __Pyx_GetException may itself raise errors
116 PyErr_Restore
(type, value
, tb
)
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
)
126 decref_ok
= (<Context
>ctx
).delref
(None
, lineno
, True
)
128 decref_ok
= (<Context
>ctx
).delref
(<object>p_obj
, lineno
, False
)
132 # __Pyx_GetException may itself raise errors
134 PyErr_Restore
(type, value
, tb
)
137 cdef void GIVEREF
(PyObject
* ctx
, PyObject
* p_obj
, int lineno
):
138 GIVEREF_and_report
(ctx
, p_obj
, lineno
)
140 cdef void INCREF
(PyObject
* ctx
, PyObject
* obj
, int lineno
):
143 GOTREF
(ctx
, obj
, lineno
)
145 cdef void DECREF
(PyObject
* ctx
, PyObject
* obj
, int lineno
):
146 if GIVEREF_and_report
(ctx
, obj
, lineno
):
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
156 PyErr_Fetch
(&type, &value
, &tb
)
159 context
= <Context
>ctx
[0]
160 errors
= context
.end
()
162 print u
"%s: %s()" % (context
.filename
.decode
('latin1'),
163 context
.name
.decode
('latin1'))
169 # __Pyx_GetException may itself raise errors
173 PyErr_Restore
(type, value
, tb
)
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
**)
183 cdef RefNannyAPIStruct api
187 api
.GIVEREF
= GIVEREF
188 api
.SetupContext
= SetupContext
189 api
.FinishContext
= FinishContext
191 cdef extern from "Python.h":
192 object PyLong_FromVoidPtr
(void*)
194 RefNannyAPI
= PyLong_FromVoidPtr
(<void*>&api
)