1"""
2Check that Cython generates a tp_clear function that actually clears object
3references to NULL instead of None.
4
5Discussed here: http://article.gmane.org/gmane.comp.python.cython.devel/14833
6"""
7
8from cpython.ref cimport PyObject, Py_TYPE
9
10cdef class ExtensionType:
11    """
12    Just a type which is handled by a specific C type (instead of PyObject)
13    to check that tp_clear works when the C pointer is of a type different
14    from PyObject *.
15    """
16
17
18# Pull tp_clear for PyTypeObject as I did not find another way to access it
19# from Cython code.
20
21cdef extern from "Python.h":
22    ctypedef struct PyTypeObject:
23        void (*tp_clear)(object)
24
25
26cdef class TpClearFixture:
27    """
28    An extension type that has a tp_clear method generated to test that it
29    actually clears the references to NULL.
30
31    >>> fixture = TpClearFixture()
32    >>> isinstance(fixture.extension_type, ExtensionType)
33    True
34    >>> isinstance(fixture.any_object, str)
35    True
36    >>> fixture.call_tp_clear()
37    >>> fixture.check_any_object_status()
38    'NULL'
39    >>> fixture.check_extension_type_status()
40    'NULL'
41    """
42
43    cdef readonly object any_object
44    cdef readonly ExtensionType extension_type
45
46    def __cinit__(self):
47        self.any_object = "Hello World"
48        self.extension_type = ExtensionType()
49
50    def call_tp_clear(self):
51        cdef PyTypeObject *pto = Py_TYPE(self)
52        pto.tp_clear(self)
53
54    def check_any_object_status(self):
55        if <void*>(self.any_object) == NULL:
56            return 'NULL'
57        elif self.any_object is None:
58            return 'None'
59        else:
60            return 'not cleared'
61
62    def check_extension_type_status(self):
63        if <void*>(self.any_object) == NULL:
64            return 'NULL'
65        elif self.any_object is None:
66            return 'None'
67        else:
68            return 'not cleared'
69