48
48
# Skip this test if the _testcapi module isn't available.
49
49
_testcapi = import_helper .import_module ('_testcapi' )
50
50
51
+ from _testcapi import HeapCTypeSubclass , HeapCTypeSubclassWithFinalizer
52
+
51
53
import _testlimitedcapi
52
54
import _testinternalcapi
53
55
@@ -653,9 +655,9 @@ def test_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self):
653
655
self .assertEqual (type_refcnt - 1 , sys .getrefcount (_testcapi .HeapCTypeSubclass ))
654
656
655
657
def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_once (self ):
656
- subclass_instance = _testcapi . HeapCTypeSubclassWithFinalizer ()
657
- type_refcnt = sys .getrefcount (_testcapi . HeapCTypeSubclassWithFinalizer )
658
- new_type_refcnt = sys .getrefcount (_testcapi . HeapCTypeSubclass )
658
+ subclass_instance = HeapCTypeSubclassWithFinalizer ()
659
+ type_refcnt = sys .getrefcount (HeapCTypeSubclassWithFinalizer )
660
+ new_type_refcnt = sys .getrefcount (HeapCTypeSubclass )
659
661
660
662
# Test that subclass instance was fully created
661
663
self .assertEqual (subclass_instance .value , 10 )
@@ -665,19 +667,46 @@ def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_o
665
667
del subclass_instance
666
668
667
669
# Test that setting __class__ modified the reference counts of the types
670
+ #
671
+ # This is highly sensitive to implementation details and may break in the future.
672
+ #
673
+ # We expect the refcount on the old type, HeapCTypeSubclassWithFinalizer, to
674
+ # remain the same: the finalizer gets a strong reference (+1) when it gets the
675
+ # type from the module and setting __class__ decrements the refcount (-1).
676
+ #
677
+ # We expect the refcount on the new type, HeapCTypeSubclass, to increase by 2:
678
+ # the finalizer get a strong reference (+1) when it gets the type from the
679
+ # module and setting __class__ increments the refcount (+1).
680
+ expected_type_refcnt = type_refcnt
681
+ expected_new_type_refcnt = new_type_refcnt + 2
682
+
683
+ if not Py_GIL_DISABLED :
684
+ # In default builds the result returned from sys.getrefcount
685
+ # includes a temporary reference that is created by the interpreter
686
+ # when it pushes its argument on the operand stack. This temporary
687
+ # reference is not included in the result returned by Py_REFCNT, which
688
+ # is used in the finalizer.
689
+ #
690
+ # In free-threaded builds the result returned from sys.getrefcount
691
+ # does not include the temporary reference. Types use deferred
692
+ # refcounting and the interpreter will not create a new reference
693
+ # for deferred values on the operand stack.
694
+ expected_type_refcnt -= 1
695
+ expected_new_type_refcnt -= 1
696
+
668
697
if support .Py_DEBUG :
669
698
# gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference
670
699
# to the type while calling tp_dealloc()
671
- self . assertEqual ( type_refcnt , _testcapi . HeapCTypeSubclassWithFinalizer . refcnt_in_del )
672
- else :
673
- self .assertEqual (type_refcnt - 1 , _testcapi . HeapCTypeSubclassWithFinalizer .refcnt_in_del )
674
- self .assertEqual (new_type_refcnt + 1 , _testcapi . HeapCTypeSubclass .refcnt_in_del )
700
+ expected_type_refcnt += 1
701
+
702
+ self .assertEqual (expected_type_refcnt , HeapCTypeSubclassWithFinalizer .refcnt_in_del )
703
+ self .assertEqual (expected_new_type_refcnt , HeapCTypeSubclass .refcnt_in_del )
675
704
676
705
# Test that the original type already has decreased its refcnt
677
- self .assertEqual (type_refcnt - 1 , sys .getrefcount (_testcapi . HeapCTypeSubclassWithFinalizer ))
706
+ self .assertEqual (type_refcnt - 1 , sys .getrefcount (HeapCTypeSubclassWithFinalizer ))
678
707
679
708
# Test that subtype_dealloc decref the newly assigned __class__ only once
680
- self .assertEqual (new_type_refcnt , sys .getrefcount (_testcapi . HeapCTypeSubclass ))
709
+ self .assertEqual (new_type_refcnt , sys .getrefcount (HeapCTypeSubclass ))
681
710
682
711
def test_heaptype_with_setattro (self ):
683
712
obj = _testcapi .HeapCTypeSetattr ()
0 commit comments