summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_capi.py
diff options
context:
space:
mode:
authorEddie Elizondo <eduardo.elizondorueda@gmail.com>2019-09-11 09:17:13 (GMT)
committerPetr Viktorin <encukou@gmail.com>2019-09-11 09:17:13 (GMT)
commitff023ed36ea260ab64be5895f1f1f087c798987a (patch)
treedd5e417057ca8cc75876ddda2b292bcce727e249 /Lib/test/test_capi.py
parent7264e92b718d307cc499b2f10eab7644b00f0499 (diff)
downloadcpython-ff023ed36ea260ab64be5895f1f1f087c798987a.zip
cpython-ff023ed36ea260ab64be5895f1f1f087c798987a.tar.gz
cpython-ff023ed36ea260ab64be5895f1f1f087c798987a.tar.bz2
bpo-37879: Suppress subtype_dealloc decref when base type is a C heap type (GH-15323)
The instance destructor for a type is responsible for preparing an instance for deallocation by decrementing the reference counts of its referents. If an instance belongs to a heap type, the type object of an instance has its reference count decremented while for static types, which are permanently allocated, the type object is unaffected by the instance destructor. Previously, the default instance destructor searched the class hierarchy for an inherited instance destructor and, if present, would invoke it. Then, if the instance type is a heap type, it would decrement the reference count of that heap type. However, this could result in the premature destruction of a type because the inherited instance destructor should have already decremented the reference count of the type object. This change avoids the premature destruction of the type object by suppressing the decrement of its reference count when an inherited, non-default instance destructor has been invoked. Finally, an assertion on the Py_SIZE of a type was deleted. Heap types have a non zero size, making this into an incorrect assertion. https://github.com/python/cpython/pull/15323
Diffstat (limited to 'Lib/test/test_capi.py')
-rw-r--r--Lib/test/test_capi.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 7b35ba6..4d6e2f2 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -383,6 +383,92 @@ class CAPITest(unittest.TestCase):
del L
self.assertEqual(PyList.num, 0)
+ def test_subclass_of_heap_gc_ctype_with_tpdealloc_decrefs_once(self):
+ class HeapGcCTypeSubclass(_testcapi.HeapGcCType):
+ def __init__(self):
+ self.value2 = 20
+ super().__init__()
+
+ subclass_instance = HeapGcCTypeSubclass()
+ type_refcnt = sys.getrefcount(HeapGcCTypeSubclass)
+
+ # Test that subclass instance was fully created
+ self.assertEqual(subclass_instance.value, 10)
+ self.assertEqual(subclass_instance.value2, 20)
+
+ # Test that the type reference count is only decremented once
+ del subclass_instance
+ self.assertEqual(type_refcnt - 1, sys.getrefcount(HeapGcCTypeSubclass))
+
+ def test_subclass_of_heap_gc_ctype_with_del_modifying_dunder_class_only_decrefs_once(self):
+ class A(_testcapi.HeapGcCType):
+ def __init__(self):
+ self.value2 = 20
+ super().__init__()
+
+ class B(A):
+ def __init__(self):
+ super().__init__()
+
+ def __del__(self):
+ self.__class__ = A
+ A.refcnt_in_del = sys.getrefcount(A)
+ B.refcnt_in_del = sys.getrefcount(B)
+
+ subclass_instance = B()
+ type_refcnt = sys.getrefcount(B)
+ new_type_refcnt = sys.getrefcount(A)
+
+ # Test that subclass instance was fully created
+ self.assertEqual(subclass_instance.value, 10)
+ self.assertEqual(subclass_instance.value2, 20)
+
+ del subclass_instance
+
+ # Test that setting __class__ modified the reference counts of the types
+ self.assertEqual(type_refcnt - 1, B.refcnt_in_del)
+ self.assertEqual(new_type_refcnt + 1, A.refcnt_in_del)
+
+ # Test that the original type already has decreased its refcnt
+ self.assertEqual(type_refcnt - 1, sys.getrefcount(B))
+
+ # Test that subtype_dealloc decref the newly assigned __class__ only once
+ self.assertEqual(new_type_refcnt, sys.getrefcount(A))
+
+ def test_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self):
+ subclass_instance = _testcapi.HeapCTypeSubclass()
+ type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass)
+
+ # Test that subclass instance was fully created
+ self.assertEqual(subclass_instance.value, 10)
+ self.assertEqual(subclass_instance.value2, 20)
+
+ # Test that the type reference count is only decremented once
+ del subclass_instance
+ self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclass))
+
+ def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_once(self):
+ subclass_instance = _testcapi.HeapCTypeSubclassWithFinalizer()
+ type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer)
+ new_type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass)
+
+ # Test that subclass instance was fully created
+ self.assertEqual(subclass_instance.value, 10)
+ self.assertEqual(subclass_instance.value2, 20)
+
+ # The tp_finalize slot will set __class__ to HeapCTypeSubclass
+ del subclass_instance
+
+ # Test that setting __class__ modified the reference counts of the types
+ self.assertEqual(type_refcnt - 1, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del)
+ self.assertEqual(new_type_refcnt + 1, _testcapi.HeapCTypeSubclass.refcnt_in_del)
+
+ # Test that the original type already has decreased its refcnt
+ self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer))
+
+ # Test that subtype_dealloc decref the newly assigned __class__ only once
+ self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass))
+
class TestPendingCalls(unittest.TestCase):