1""" 2Check that the @cython.no_gc_clear decorator disables generation of the 3tp_clear slot so that __dealloc__ will still see the original reference 4contents. 5 6Discussed here: http://article.gmane.org/gmane.comp.python.cython.devel/14986 7""" 8 9cimport cython 10from cpython.ref cimport PyObject, Py_TYPE 11 12# Pull tp_clear for PyTypeObject as I did not find another way to access it 13# from Cython code. 14 15cdef extern from *: 16 ctypedef struct PyTypeObject: 17 void (*tp_clear)(object) 18 19 ctypedef struct __pyx_CyFunctionObject: 20 PyObject* func_closure 21 22 23def is_tp_clear_null(obj): 24 return (<PyTypeObject*>Py_TYPE(obj)).tp_clear is NULL 25 26 27def is_closure_tp_clear_null(func): 28 return is_tp_clear_null( 29 <object>(<__pyx_CyFunctionObject*>func).func_closure) 30 31 32@cython.no_gc_clear 33cdef class DisableTpClear: 34 """ 35 An extension type that has a tp_clear method generated to test that it 36 actually clears the references to NULL. 37 38 >>> uut = DisableTpClear() 39 >>> is_tp_clear_null(uut) 40 True 41 >>> uut.call_tp_clear() 42 >>> type(uut.requires_cleanup) == list 43 True 44 >>> del uut 45 """ 46 47 cdef public object requires_cleanup 48 49 def __cinit__(self): 50 self.requires_cleanup = [ 51 "Some object that needs cleaning in __dealloc__"] 52 53 def call_tp_clear(self): 54 cdef PyTypeObject *pto = Py_TYPE(self) 55 if pto.tp_clear != NULL: 56 pto.tp_clear(self) 57 58 59cdef class ReallowTpClear(DisableTpClear): 60 """ 61 >>> import gc 62 >>> obj = ReallowTpClear() 63 >>> is_tp_clear_null(obj) 64 False 65 66 >>> obj.attr = obj # create hard reference cycle 67 >>> del obj; _ignore = gc.collect() 68 69 # Problem: cannot really validate that the cycle was cleaned up without using weakrefs etc... 70 """ 71 cdef public object attr 72 73 74def test_closure_without_clear(str x): 75 """ 76 >>> c = test_closure_without_clear('abc') 77 >>> is_tp_clear_null(c) 78 False 79 >>> is_closure_tp_clear_null(c) 80 True 81 >>> c('cba') 82 'abcxyzcba' 83 """ 84 def c(str s): 85 return x + 'xyz' + s 86 return c 87 88 89def test_closure_with_clear(list x): 90 """ 91 >>> c = test_closure_with_clear(list('abc')) 92 >>> is_tp_clear_null(c) 93 False 94 >>> is_closure_tp_clear_null(c) 95 False 96 >>> c('cba') 97 'abcxyzcba' 98 """ 99 def c(str s): 100 return ''.join(x) + 'xyz' + s 101 return c 102