Blob Blame History Raw
"""
Check that Cython generates a tp_clear function that actually clears object
references to NULL instead of None.

Discussed here: http://article.gmane.org/gmane.comp.python.cython.devel/14833
"""

from cpython.ref cimport PyObject, Py_TYPE

cdef class ExtensionType:
    """
    Just a type which is handled by a specific C type (instead of PyObject)
    to check that tp_clear works when the C pointer is of a type different
    from PyObject *.
    """


# Pull tp_clear for PyTypeObject as I did not find another way to access it
# from Cython code.

cdef extern from "Python.h":
    ctypedef struct PyTypeObject:
        void (*tp_clear)(object)


cdef class TpClearFixture:
    """
    An extension type that has a tp_clear method generated to test that it
    actually clears the references to NULL.

    >>> fixture = TpClearFixture()
    >>> isinstance(fixture.extension_type, ExtensionType)
    True
    >>> isinstance(fixture.any_object, str)
    True
    >>> fixture.call_tp_clear()
    >>> fixture.check_any_object_status()
    'NULL'
    >>> fixture.check_extension_type_status()
    'NULL'
    """
    
    cdef readonly object any_object
    cdef readonly ExtensionType extension_type

    def __cinit__(self):
        self.any_object = "Hello World"
        self.extension_type = ExtensionType()

    def call_tp_clear(self):
        cdef PyTypeObject *pto = Py_TYPE(self)
        pto.tp_clear(self)

    def check_any_object_status(self):
        if <void*>(self.any_object) == NULL:
            return 'NULL'
        elif self.any_object is None:
            return 'None' 
        else:
            return 'not cleared'

    def check_extension_type_status(self):
        if <void*>(self.any_object) == NULL:
            return 'NULL'
        elif self.any_object is None:
            return 'None' 
        else:
            return 'not cleared'