Change tp_clear generation to clear to NULL.
authorTorsten Landschoff <torsten.landschoff@dynamore.de>
Thu, 1 Aug 2013 21:37:30 +0000 (23:37 +0200)
committerTorsten Landschoff <torsten.landschoff@dynamore.de>
Thu, 1 Aug 2013 21:37:30 +0000 (23:37 +0200)
Better to crash early than to never know that the reference changed to
None. See discussion here:

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

Cython/Compiler/ModuleNode.py
tests/run/clear_to_null.pyx [new file with mode: 0644]

index 6f2a114..8ba8678 100644 (file)
@@ -1367,7 +1367,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
 
         if py_attrs or py_buffers:
             self.generate_self_cast(scope, code)
-            code.putln("PyObject* tmp;")
 
         if base_type:
             # want to call it explicitly if possible so inlining can be performed
@@ -1390,13 +1389,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                     UtilityCode.load_cached("CallNextTpClear", "ExtensionTypes.c"))
 
         for entry in py_attrs:
-            name = "p->%s" % entry.cname
-            code.putln("tmp = ((PyObject*)%s);" % name)
-            if entry.is_declared_generic:
-                code.put_init_to_py_none(name, py_object_type, nanny=False)
-            else:
-                code.put_init_to_py_none(name, entry.type, nanny=False)
-            code.putln("Py_XDECREF(tmp);")
+            code.putln("Py_CLEAR(p->%s);" % entry.cname)
 
         for entry in py_buffers:
             # Note: shouldn't this call __Pyx_ReleaseBuffer ??
diff --git a/tests/run/clear_to_null.pyx b/tests/run/clear_to_null.pyx
new file mode 100644 (file)
index 0000000..6e49e03
--- /dev/null
@@ -0,0 +1,68 @@
+"""
+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 public object any_object
+    cdef public ExtensionType extension_type
+
+    def __cinit__(self):
+        self.any_object = "Hello World"
+        self.extension_type = ExtensionType()
+
+    cpdef public call_tp_clear(self):
+        cdef PyTypeObject *pto = Py_TYPE(self)
+        pto.tp_clear(self)
+
+    cpdef public str 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'
+
+    cpdef public str 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'