2
Check that the @cython.no_gc_clear decorator disables generation of the
3
tp_clear slot so that __dealloc__ will still see the original reference
6
Discussed here: https://article.gmane.org/gmane.comp.python.cython.devel/14986
10
from cpython.ref cimport PyObject, Py_TYPE
12
# Pull tp_clear for PyTypeObject as I did not find another way to access it
16
ctypedef struct PyTypeObject:
17
void (*tp_clear)(object)
19
ctypedef struct __pyx_CyFunctionObject:
20
PyObject* func_closure
23
def is_tp_clear_null(obj):
24
return (<PyTypeObject*>Py_TYPE(obj)).tp_clear is NULL
27
def is_closure_tp_clear_null(func):
28
return is_tp_clear_null(
29
<object>(<__pyx_CyFunctionObject*>func).func_closure)
33
cdef class DisableTpClear:
35
An extension type that has a tp_clear method generated to test that it
36
actually clears the references to NULL.
38
>>> uut = DisableTpClear()
39
>>> is_tp_clear_null(uut)
41
>>> uut.call_tp_clear()
42
>>> type(uut.requires_cleanup) == list
47
cdef public object requires_cleanup
50
self.requires_cleanup = [
51
"Some object that needs cleaning in __dealloc__"]
53
def call_tp_clear(self):
54
cdef PyTypeObject *pto = Py_TYPE(self)
55
if pto.tp_clear != NULL:
59
cdef class ReallowTpClear(DisableTpClear):
62
>>> obj = ReallowTpClear()
63
>>> is_tp_clear_null(obj)
66
>>> obj.attr = obj # create hard reference cycle
67
>>> del obj; _ignore = gc.collect()
69
# Problem: cannot really validate that the cycle was cleaned up without using weakrefs etc...
71
cdef public object attr
74
def test_closure_without_clear(str x):
76
>>> c = test_closure_without_clear('abc')
77
>>> is_tp_clear_null(c)
79
>>> is_closure_tp_clear_null(c)
89
def test_closure_with_clear(list x):
91
>>> c = test_closure_with_clear(list('abc'))
92
>>> is_tp_clear_null(c)
94
>>> is_closure_tp_clear_null(c)
100
return ''.join(x) + 'xyz' + s