@@ -644,7 +644,9 @@ def test_directive_output_invalid_command(self):
644644 - 'methoddef_define'
645645 - 'impl_prototype'
646646 - 'parser_prototype'
647+ - 'parser_helper'
647648 - 'parser_definition'
649+ - 'vectorcall_definition'
648650 - 'cpp_endif'
649651 - 'methoddef_ifndef'
650652 - 'impl_definition'
@@ -2887,6 +2889,112 @@ def test_duplicate_coexist(self):
28872889 """
28882890 self .expect_failure (block , err , lineno = 2 )
28892891
2892+ def test_duplicate_vectorcall (self ):
2893+ err = "Called @vectorcall twice"
2894+ block = """
2895+ module m
2896+ class Foo "FooObject *" ""
2897+ @vectorcall
2898+ @vectorcall
2899+ Foo.__init__
2900+ """
2901+ self .expect_failure (block , err , lineno = 3 )
2902+
2903+ def test_vectorcall_on_regular_method (self ):
2904+ err = "@vectorcall can only be used with __init__ and __new__ methods"
2905+ block = """
2906+ module m
2907+ class Foo "FooObject *" ""
2908+ @vectorcall
2909+ Foo.some_method
2910+ """
2911+ self .expect_failure (block , err , lineno = 3 )
2912+
2913+ def test_vectorcall_on_module_function (self ):
2914+ err = "@vectorcall can only be used with __init__ and __new__ methods"
2915+ block = """
2916+ module m
2917+ @vectorcall
2918+ m.fn
2919+ """
2920+ self .expect_failure (block , err , lineno = 2 )
2921+
2922+ def test_vectorcall_on_init (self ):
2923+ block = """
2924+ module m
2925+ class Foo "FooObject *" "Foo_Type"
2926+ @vectorcall
2927+ Foo.__init__
2928+ iterable: object = NULL
2929+ /
2930+ """
2931+ func = self .parse_function (block , signatures_in_block = 3 ,
2932+ function_index = 2 )
2933+ self .assertTrue (func .vectorcall )
2934+
2935+ def test_vectorcall_on_new (self ):
2936+ block = """
2937+ module m
2938+ class Foo "FooObject *" "Foo_Type"
2939+ @classmethod
2940+ @vectorcall
2941+ Foo.__new__
2942+ x: object = NULL
2943+ /
2944+ """
2945+ func = self .parse_function (block , signatures_in_block = 3 ,
2946+ function_index = 2 )
2947+ self .assertTrue (func .vectorcall )
2948+
2949+ def test_vectorcall_takes_no_arguments (self ):
2950+ err = "at_vectorcall() takes 1 positional argument but 2 were given"
2951+ block = """
2952+ module m
2953+ class Foo "FooObject *" "Foo_Type"
2954+ @vectorcall bogus=True
2955+ Foo.__init__
2956+ """
2957+ self .expect_failure (block , err , lineno = 2 )
2958+
2959+ def test_vectorcall_without_type_object (self ):
2960+ err = "@vectorcall requires the type object of 'Foo'"
2961+ block = """
2962+ module m
2963+ class Foo "FooObject *" ""
2964+ @vectorcall
2965+ Foo.__init__
2966+ """
2967+ self .expect_failure (block , err , lineno = 3 )
2968+
2969+ def test_vectorcall_unsupported_converter (self ):
2970+ # str(encoding=...) has no parse_arg() implementation.
2971+ err = ("@vectorcall requires all converters to support "
2972+ "parse_arg(); parameter 's' does not" )
2973+ block = """
2974+ module m
2975+ class Foo "FooObject *" "Foo_Type"
2976+ @classmethod
2977+ @vectorcall
2978+ Foo.__new__
2979+ s: str(encoding="utf-8")
2980+ /
2981+ """
2982+ self .expect_failure (block , err , lineno = 6 )
2983+
2984+ def test_vectorcall_with_option_groups (self ):
2985+ err = "@vectorcall does not support optional groups"
2986+ block = """
2987+ module m
2988+ class Foo "FooObject *" "Foo_Type"
2989+ @vectorcall
2990+ Foo.__init__
2991+ [
2992+ a: object
2993+ ]
2994+ /
2995+ """
2996+ self .expect_failure (block , err , lineno = 7 )
2997+
28902998 def test_unused_param (self ):
28912999 block = self .parse ("""
28923000 module foo
@@ -5020,6 +5128,105 @@ def test_kwds_with_pos_only_and_stararg(self):
50205128 self .assertEqual (ac_tester .kwds_with_pos_only_and_stararg (1 , 2 , * args , ** kwds ), (1 , 2 , args , kwds ))
50215129
50225130
5131+ @unittest .skipIf (ac_tester is None , "_testclinic is missing" )
5132+ class VectorcallFunctionalTest (unittest .TestCase ):
5133+ """Runtime tests for @vectorcall exemplar types."""
5134+
5135+ def test_vc_new (self ):
5136+ self .assertIsInstance (ac_tester .VcNew (), ac_tester .VcNew )
5137+ self .assertIsInstance (ac_tester .VcNew (1 ), ac_tester .VcNew )
5138+ self .assertIsInstance (ac_tester .VcNew (a = 1 ), ac_tester .VcNew )
5139+
5140+ def test_vc_new_rejects_extra_args (self ):
5141+ with self .assertRaises (TypeError ):
5142+ ac_tester .VcNew (1 , 2 )
5143+
5144+ def test_vc_init (self ):
5145+ self .assertIsInstance (ac_tester .VcInit (1 ), ac_tester .VcInit )
5146+ self .assertIsInstance (ac_tester .VcInit (1 , 2 ), ac_tester .VcInit )
5147+ self .assertIsInstance (ac_tester .VcInit (1 , b = 2 ), ac_tester .VcInit )
5148+
5149+ def test_vc_init_missing_required (self ):
5150+ with self .assertRaises (TypeError ):
5151+ ac_tester .VcInit ()
5152+
5153+ def test_vc_init_rejects_a_as_keyword (self ):
5154+ # 'a' is positional-only
5155+ with self .assertRaises (TypeError ):
5156+ ac_tester .VcInit (a = 1 )
5157+
5158+ def test_vc_new_base (self ):
5159+ self .assertIsInstance (ac_tester .VcNewBase (1 ), ac_tester .VcNewBase )
5160+ self .assertIsInstance (ac_tester .VcNewBase (1 , 2 ), ac_tester .VcNewBase )
5161+ self .assertIsInstance (ac_tester .VcNewBase (1 , b = 2 ), ac_tester .VcNewBase )
5162+
5163+ def test_vc_new_base_missing_required (self ):
5164+ with self .assertRaises (TypeError ):
5165+ ac_tester .VcNewBase ()
5166+
5167+ def test_vc_new_base_subclass (self ):
5168+ # tp_vectorcall is not inherited, so the subclass is constructed
5169+ # through tp_new. The generated vectorcall asserts on that, so a
5170+ # debug build aborts here if that ever stops holding.
5171+ Sub = type ('Sub' , (ac_tester .VcNewBase ,), {})
5172+ obj = Sub (1 )
5173+ self .assertIsInstance (obj , Sub )
5174+ self .assertIsInstance (obj , ac_tester .VcNewBase )
5175+
5176+ def test_vc_kwonly (self ):
5177+ # keyword-only 'b': vectorcall has no kwnames==NULL fast path,
5178+ # so every call goes through the helper.
5179+ self .assertIsInstance (ac_tester .VcKwOnly (1 ), ac_tester .VcKwOnly )
5180+ self .assertIsInstance (ac_tester .VcKwOnly (1 , b = 2 ), ac_tester .VcKwOnly )
5181+ self .assertIsInstance (ac_tester .VcKwOnly (a = 1 , b = 2 ), ac_tester .VcKwOnly )
5182+
5183+ def test_vc_kwonly_b_as_positional (self ):
5184+ with self .assertRaises (TypeError ):
5185+ ac_tester .VcKwOnly (1 , 2 )
5186+
5187+ def test_vc_kwonly_missing_required (self ):
5188+ with self .assertRaises (TypeError ):
5189+ ac_tester .VcKwOnly ()
5190+
5191+ def test_parse_errors_match_slot (self ):
5192+ # tp_vectorcall and tp_new/tp_init slot should match in argument parsing
5193+ # error messages. Explicit calls to __new__ and __init__, as well as
5194+ # subtype calls, will not hit the vectorcall slot. Test errors match.
5195+ def error (func , args , kwargs ):
5196+ try :
5197+ func (* args , ** kwargs )
5198+ except TypeError as exc :
5199+ return str (exc )
5200+ return None
5201+
5202+ def through_new (cls ):
5203+ return cls , partial (cls .__new__ , cls )
5204+
5205+ def through_init (cls ):
5206+ # Not subclassable, and tp_new is PyType_GenericNew, so reach
5207+ # tp_init through the __init__ slot wrapper on an instance.
5208+ return cls , partial (cls .__init__ , cls (1 ))
5209+
5210+ entry_points = [
5211+ through_new (enumerate ), # the only non-test @vectorcall function
5212+ through_new (ac_tester .VcNew ),
5213+ through_new (ac_tester .VcNewBase ),
5214+ through_new (ac_tester .VcKwOnly ),
5215+ through_init (ac_tester .VcInit ),
5216+ ]
5217+ invalid_calls = [
5218+ ((), {}), # too few positional arguments
5219+ ((1 , 2 , 3 ), {}), # too many positional arguments
5220+ ((), {'zz' : 1 }), # unknown keyword argument
5221+ ]
5222+
5223+ for direct , slot in entry_points :
5224+ for args , kwargs in invalid_calls :
5225+ with self .subTest (cls = direct , args = args , kwargs = kwargs ):
5226+ self .assertEqual (error (direct , args , kwargs ),
5227+ error (slot , args , kwargs ))
5228+
5229+
50235230class LimitedCAPIOutputTests (unittest .TestCase ):
50245231
50255232 def setUp (self ):
0 commit comments