4848# Skip this test if the _testcapi module isn't available.
4949_testcapi = import_helper .import_module ('_testcapi' )
5050
51+ from _testcapi import HeapCTypeSubclass , HeapCTypeSubclassWithFinalizer
52+
5153import _testlimitedcapi
5254import _testinternalcapi
5355
@@ -653,9 +655,9 @@ def test_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self):
653655 self .assertEqual (type_refcnt - 1 , sys .getrefcount (_testcapi .HeapCTypeSubclass ))
654656
655657 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 )
659661
660662 # Test that subclass instance was fully created
661663 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
665667 del subclass_instance
666668
667669 # 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+
668697 if support .Py_DEBUG :
669698 # gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference
670699 # 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 )
675704
676705 # 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 ))
678707
679708 # 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 ))
681710
682711 def test_heaptype_with_setattro (self ):
683712 obj = _testcapi .HeapCTypeSetattr ()
0 commit comments