@@ -606,9 +606,19 @@ def test_vectorcall_flag(self):
606
606
self .assertFalse (_testcapi .MethodDescriptorNopGet .__flags__ & Py_TPFLAGS_HAVE_VECTORCALL )
607
607
self .assertTrue (_testcapi .MethodDescriptor2 .__flags__ & Py_TPFLAGS_HAVE_VECTORCALL )
608
608
609
- # Mutable heap types should not inherit Py_TPFLAGS_HAVE_VECTORCALL
609
+ # Mutable heap types should inherit Py_TPFLAGS_HAVE_VECTORCALL,
610
+ # but should lose it when __call__ is overridden
610
611
class MethodDescriptorHeap (_testcapi .MethodDescriptorBase ):
611
612
pass
613
+ self .assertTrue (MethodDescriptorHeap .__flags__ & Py_TPFLAGS_HAVE_VECTORCALL )
614
+ MethodDescriptorHeap .__call__ = print
615
+ self .assertFalse (MethodDescriptorHeap .__flags__ & Py_TPFLAGS_HAVE_VECTORCALL )
616
+
617
+ # Mutable heap types should not inherit Py_TPFLAGS_HAVE_VECTORCALL if
618
+ # they define __call__ directly
619
+ class MethodDescriptorHeap (_testcapi .MethodDescriptorBase ):
620
+ def __call__ (self ):
621
+ pass
612
622
self .assertFalse (MethodDescriptorHeap .__flags__ & Py_TPFLAGS_HAVE_VECTORCALL )
613
623
614
624
def test_vectorcall_override (self ):
@@ -621,6 +631,58 @@ def test_vectorcall_override(self):
621
631
f = _testcapi .MethodDescriptorNopGet ()
622
632
self .assertIs (f (* args ), args )
623
633
634
+ def test_vectorcall_override_on_mutable_class (self ):
635
+ """Setting __call__ should disable vectorcall"""
636
+ TestType = _testcapi .make_vectorcall_class ()
637
+ instance = TestType ()
638
+ self .assertEqual (instance (), "tp_call" )
639
+ instance .set_vectorcall (TestType )
640
+ self .assertEqual (instance (), "vectorcall" ) # assume vectorcall is used
641
+ TestType .__call__ = lambda self : "custom"
642
+ self .assertEqual (instance (), "custom" )
643
+
644
+ def test_vectorcall_override_with_subclass (self ):
645
+ """Setting __call__ on a superclass should disable vectorcall"""
646
+ SuperType = _testcapi .make_vectorcall_class ()
647
+ class DerivedType (SuperType ):
648
+ pass
649
+
650
+ instance = DerivedType ()
651
+
652
+ # Derived types with its own vectorcall should be unaffected
653
+ UnaffectedType1 = _testcapi .make_vectorcall_class (DerivedType )
654
+ UnaffectedType2 = _testcapi .make_vectorcall_class (SuperType )
655
+
656
+ # Aside: Quickly check that the C helper actually made derived types
657
+ self .assertTrue (issubclass (UnaffectedType1 , DerivedType ))
658
+ self .assertTrue (issubclass (UnaffectedType2 , SuperType ))
659
+
660
+ # Initial state: tp_call
661
+ self .assertEqual (instance (), "tp_call" )
662
+ self .assertEqual (_testcapi .has_vectorcall_flag (SuperType ), True )
663
+ self .assertEqual (_testcapi .has_vectorcall_flag (DerivedType ), True )
664
+ self .assertEqual (_testcapi .has_vectorcall_flag (UnaffectedType1 ), True )
665
+ self .assertEqual (_testcapi .has_vectorcall_flag (UnaffectedType2 ), True )
666
+
667
+ # Setting the vectorcall function
668
+ instance .set_vectorcall (SuperType )
669
+
670
+ self .assertEqual (instance (), "vectorcall" )
671
+ self .assertEqual (_testcapi .has_vectorcall_flag (SuperType ), True )
672
+ self .assertEqual (_testcapi .has_vectorcall_flag (DerivedType ), True )
673
+ self .assertEqual (_testcapi .has_vectorcall_flag (UnaffectedType1 ), True )
674
+ self .assertEqual (_testcapi .has_vectorcall_flag (UnaffectedType2 ), True )
675
+
676
+ # Setting __call__ should remove vectorcall from all subclasses
677
+ SuperType .__call__ = lambda self : "custom"
678
+
679
+ self .assertEqual (instance (), "custom" )
680
+ self .assertEqual (_testcapi .has_vectorcall_flag (SuperType ), False )
681
+ self .assertEqual (_testcapi .has_vectorcall_flag (DerivedType ), False )
682
+ self .assertEqual (_testcapi .has_vectorcall_flag (UnaffectedType1 ), True )
683
+ self .assertEqual (_testcapi .has_vectorcall_flag (UnaffectedType2 ), True )
684
+
685
+
624
686
def test_vectorcall (self ):
625
687
# Test a bunch of different ways to call objects:
626
688
# 1. vectorcall using PyVectorcall_Call()
0 commit comments