diff --git a/kivy/_event.pxd b/kivy/_event.pxd index 355a5438c..8fd1913ef 100644 --- a/kivy/_event.pxd +++ b/kivy/_event.pxd @@ -43,7 +43,10 @@ cdef class EventObservers: # when binding while dispatching, don't dispatch from new_callback and down # because those callbacks didn't exist when we started dispatching cdef BoundCallabck *new_callback + # this needs to be here to enable cython cyclic gc + cdef object dummy_obj + cdef inline void release_callbacks(self) except * cdef inline void bind(self, object observer) except * cdef inline void fast_bind(self, object observer, tuple largs, dict kwargs, int is_ref) except * cdef inline void unbind(self, object observer, int is_ref, int stop_on_first) except * diff --git a/kivy/_event.pyx b/kivy/_event.pyx index 0c8269293..833514f89 100644 --- a/kivy/_event.pyx +++ b/kivy/_event.pyx @@ -15,6 +15,12 @@ __all__ = ('EventDispatcher', 'ObjectWithUid', 'Observable') cdef extern from "Python.h": + ctypedef int (*visitproc)(PyObject *, void *) + ctypedef int (*inquiry)(PyObject *) + ctypedef int (*traverseproc)(PyObject *, visitproc, void *) + ctypedef struct PyTypeObject: + traverseproc tp_traverse + inquiry tp_clear void Py_INCREF(PyObject *) void Py_DECREF(PyObject *) @@ -786,9 +792,13 @@ cdef class EventObservers: self.unbound_dispatched_callback = 0 def __dealloc__(self): - cdef BoundCallabck *callback + self.release_callbacks() + + cdef inline void release_callbacks(self): + cdef BoundCallabck *callback = self.first_callback cdef BoundCallabck *last_c - callback = self.first_callback + self.first_callback = NULL + while callback != NULL: last_c = callback callback = callback.next @@ -824,7 +834,6 @@ cdef class EventObservers: if self.current_dispatch != NULL and self.new_callback == NULL: self.new_callback = callback - cdef inline void fast_bind(self, object observer, tuple largs, dict kwargs, int is_ref) except *: '''Similar to bind, except it accepts largs, kwargs that is forwards. @@ -1122,3 +1131,39 @@ cdef class EventObservers: else: yield (callback.func), (), {}, callback.is_ref callback = callback.next + + +cdef traverseproc tp_traverse_old = (EventObservers).tp_traverse +cdef inquiry tp_clear_old = (EventObservers).tp_clear + +cdef int observers_traverse(PyObject *obj, visitproc visit, void *arg): + cdef BoundCallabck *callback = (obj).first_callback + cdef int vret + if tp_traverse_old != NULL: + vret = tp_traverse_old(obj, visit, arg) + if vret: + return vret + + while callback != NULL: + vret = visit(callback.func, arg) + if vret: + return vret + if callback.largs != NULL: + vret = visit(callback.largs, arg) + if vret: + return vret + if callback.kwargs != NULL: + vret = visit(callback.kwargs, arg) + if vret: + return vret + callback = callback.next + return 0 + +cdef int observers_clear(PyObject *obj): + if tp_clear_old != NULL: + tp_clear_old(obj) + (obj).release_callbacks() + return 0 + +(EventObservers).tp_traverse = observers_traverse +(EventObservers).tp_clear = observers_clear