Skip to content

Commit 3698a32

Browse files
committed
Add _testclinic vectorcall sample + testing
1 parent a243517 commit 3698a32

5 files changed

Lines changed: 763 additions & 3 deletions

File tree

Lib/test/test_clinic.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4293,6 +4293,89 @@ def test_kwds_with_pos_only_and_stararg(self):
42934293
self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, *args, **kwds), (1, 2, args, kwds))
42944294

42954295

4296+
@unittest.skipIf(ac_tester is None, "_testclinic is missing")
4297+
class VectorcallFunctionalTest(unittest.TestCase):
4298+
"""Runtime tests for @vectorcall exemplar types."""
4299+
4300+
def test_vc_new_no_args(self):
4301+
obj = ac_tester.VcNew()
4302+
self.assertIsInstance(obj, ac_tester.VcNew)
4303+
4304+
def test_vc_new_with_arg(self):
4305+
obj = ac_tester.VcNew(1)
4306+
self.assertIsInstance(obj, ac_tester.VcNew)
4307+
4308+
def test_vc_new_with_kwarg(self):
4309+
obj = ac_tester.VcNew(a=1)
4310+
self.assertIsInstance(obj, ac_tester.VcNew)
4311+
4312+
def test_vc_new_rejects_extra_args(self):
4313+
with self.assertRaises(TypeError):
4314+
ac_tester.VcNew(1, 2)
4315+
4316+
def test_vc_init_required_pos_only(self):
4317+
obj = ac_tester.VcInit(1)
4318+
self.assertIsInstance(obj, ac_tester.VcInit)
4319+
4320+
def test_vc_init_with_keyword(self):
4321+
obj = ac_tester.VcInit(1, b=2)
4322+
self.assertIsInstance(obj, ac_tester.VcInit)
4323+
4324+
def test_vc_init_with_positional_optional(self):
4325+
obj = ac_tester.VcInit(1, 2)
4326+
self.assertIsInstance(obj, ac_tester.VcInit)
4327+
4328+
def test_vc_init_missing_required(self):
4329+
with self.assertRaises(TypeError):
4330+
ac_tester.VcInit()
4331+
4332+
def test_vc_init_rejects_a_as_keyword(self):
4333+
# 'a' is positional-only
4334+
with self.assertRaises(TypeError):
4335+
ac_tester.VcInit(a=1)
4336+
4337+
def test_vc_new_exact_one_arg(self):
4338+
obj = ac_tester.VcNewExact(1)
4339+
self.assertIsInstance(obj, ac_tester.VcNewExact)
4340+
4341+
def test_vc_new_exact_two_args(self):
4342+
obj = ac_tester.VcNewExact(1, 2)
4343+
self.assertIsInstance(obj, ac_tester.VcNewExact)
4344+
4345+
def test_vc_new_exact_missing_required(self):
4346+
with self.assertRaises(TypeError):
4347+
ac_tester.VcNewExact()
4348+
4349+
def test_vc_new_exact_subclass(self):
4350+
# exact_only: subclass goes through non-vectorcall (tp_new) path
4351+
Sub = type('Sub', (ac_tester.VcNewExact,), {})
4352+
obj = Sub(1)
4353+
self.assertIsInstance(obj, Sub)
4354+
self.assertIsInstance(obj, ac_tester.VcNewExact)
4355+
4356+
def test_vc_new_zeroarg_no_args(self):
4357+
# zero_arg returns Py_None when called with no arguments
4358+
result = ac_tester.VcNewZeroArg()
4359+
self.assertIs(result, None)
4360+
4361+
def test_vc_new_zeroarg_with_pos(self):
4362+
obj = ac_tester.VcNewZeroArg(1)
4363+
self.assertIsInstance(obj, ac_tester.VcNewZeroArg)
4364+
4365+
def test_vc_new_zeroarg_with_kwonly(self):
4366+
obj = ac_tester.VcNewZeroArg(b=2)
4367+
self.assertIsInstance(obj, ac_tester.VcNewZeroArg)
4368+
4369+
def test_vc_new_zeroarg_with_both(self):
4370+
obj = ac_tester.VcNewZeroArg(1, b=2)
4371+
self.assertIsInstance(obj, ac_tester.VcNewZeroArg)
4372+
4373+
def test_vc_new_zeroarg_rejects_a_as_keyword(self):
4374+
# 'a' is positional-only
4375+
with self.assertRaises(TypeError):
4376+
ac_tester.VcNewZeroArg(a=1)
4377+
4378+
42964379
class LimitedCAPIOutputTests(unittest.TestCase):
42974380

42984381
def setUp(self):

Modules/_testclinic.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ custom_converter(PyObject *obj, custom_t *val)
2121
}
2222

2323

24+
/* Forward declarations for vectorcall exemplar types, needed because
25+
* clinic/_testclinic.c.h is included before the type definitions. */
26+
static PyTypeObject VcNew_Type;
27+
static PyTypeObject VcInit_Type;
28+
static PyTypeObject VcNewExact_Type;
29+
static PyTypeObject VcNewZeroArg_Type;
30+
2431
#include "clinic/_testclinic.c.h"
2532

2633

@@ -2314,6 +2321,133 @@ output pop
23142321
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e7c7c42daced52b0]*/
23152322

23162323

2324+
/*
2325+
* Vectorcall exemplars.
2326+
*
2327+
* Each type exercises a different @vectorcall code-generation path.
2328+
* There is one type per exemplar because tp_vectorcall is a single slot.
2329+
*/
2330+
2331+
2332+
/* --- VcNew: @vectorcall on __new__, single pos-or-kw optional
2333+
* (general + kw fast-path, METHOD_NEW finale) --- */
2334+
2335+
/*[clinic input]
2336+
class _testclinic.VcNew "PyObject *" "&VcNew_Type"
2337+
@classmethod
2338+
@vectorcall
2339+
_testclinic.VcNew.__new__ as vc_plain_new
2340+
a: object = None
2341+
[clinic start generated code]*/
2342+
2343+
static PyObject *
2344+
vc_plain_new_impl(PyTypeObject *type, PyObject *a)
2345+
/*[clinic end generated code: output=55b273e9797a3013 input=e15d88606280badc]*/
2346+
{
2347+
return type->tp_alloc(type, 0);
2348+
}
2349+
2350+
static PyTypeObject VcNew_Type = {
2351+
PyVarObject_HEAD_INIT(NULL, 0)
2352+
.tp_name = "_testclinic.VcNew",
2353+
.tp_basicsize = sizeof(PyObject),
2354+
.tp_flags = Py_TPFLAGS_DEFAULT,
2355+
.tp_new = vc_plain_new,
2356+
.tp_vectorcall = vc_plain_vectorcall,
2357+
};
2358+
2359+
2360+
/* --- VcInit: @vectorcall on __init__, pos-only + pos-or-kw optional
2361+
* (general + kw fast-path) --- */
2362+
2363+
/*[clinic input]
2364+
class _testclinic.VcInit "PyObject *" "&VcInit_Type"
2365+
@vectorcall
2366+
_testclinic.VcInit.__init__ as vc_posorkw_init
2367+
a: object
2368+
/
2369+
b: object = None
2370+
[clinic start generated code]*/
2371+
2372+
static int
2373+
vc_posorkw_init_impl(PyObject *self, PyObject *a, PyObject *b)
2374+
/*[clinic end generated code: output=6018424ba9fb0744 input=25e4c2b792040c31]*/
2375+
{
2376+
return 0;
2377+
}
2378+
2379+
static PyTypeObject VcInit_Type = {
2380+
PyVarObject_HEAD_INIT(NULL, 0)
2381+
.tp_name = "_testclinic.VcInit",
2382+
.tp_basicsize = sizeof(PyObject),
2383+
.tp_flags = Py_TPFLAGS_DEFAULT,
2384+
.tp_new = PyType_GenericNew,
2385+
.tp_init = vc_posorkw_init,
2386+
.tp_vectorcall = vc_posorkw_vectorcall,
2387+
};
2388+
2389+
2390+
/* --- VcNewExact: @vectorcall exact_only on __new__, pos-only required +
2391+
* pos-or-kw optional (general + kw fast-path + exact_only guard) --- */
2392+
2393+
/*[clinic input]
2394+
class _testclinic.VcNewExact "PyObject *" "&VcNewExact_Type"
2395+
@classmethod
2396+
@vectorcall exact_only
2397+
_testclinic.VcNewExact.__new__ as vc_exact_new
2398+
a: object
2399+
/
2400+
b: object = None
2401+
[clinic start generated code]*/
2402+
2403+
static PyObject *
2404+
vc_exact_new_impl(PyTypeObject *type, PyObject *a, PyObject *b)
2405+
/*[clinic end generated code: output=e88217e36443b698 input=ea86a1ab634c93a6]*/
2406+
{
2407+
return type->tp_alloc(type, 0);
2408+
}
2409+
2410+
static PyTypeObject VcNewExact_Type = {
2411+
PyVarObject_HEAD_INIT(NULL, 0)
2412+
.tp_name = "_testclinic.VcNewExact",
2413+
.tp_basicsize = sizeof(PyObject),
2414+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
2415+
.tp_new = vc_exact_new,
2416+
.tp_vectorcall = vc_exact_vectorcall,
2417+
};
2418+
2419+
2420+
/* --- VcNewZeroArg: @vectorcall zero_arg on __new__, pos-only optional +
2421+
* kw-only optional (general path, no kw fast-path + zero_arg) --- */
2422+
2423+
/*[clinic input]
2424+
class _testclinic.VcNewZeroArg "PyObject *" "&VcNewZeroArg_Type"
2425+
@classmethod
2426+
@vectorcall zero_arg=Py_NewRef(Py_None)
2427+
_testclinic.VcNewZeroArg.__new__ as vc_zeroarg_new
2428+
a: object = None
2429+
/
2430+
*
2431+
b: object = None
2432+
[clinic start generated code]*/
2433+
2434+
static PyObject *
2435+
vc_zeroarg_new_impl(PyTypeObject *type, PyObject *a, PyObject *b)
2436+
/*[clinic end generated code: output=6425b64d61c6317a input=f3d3ba860fc40034]*/
2437+
{
2438+
return type->tp_alloc(type, 0);
2439+
}
2440+
2441+
static PyTypeObject VcNewZeroArg_Type = {
2442+
PyVarObject_HEAD_INIT(NULL, 0)
2443+
.tp_name = "_testclinic.VcNewZeroArg",
2444+
.tp_basicsize = sizeof(PyObject),
2445+
.tp_flags = Py_TPFLAGS_DEFAULT,
2446+
.tp_new = vc_zeroarg_new,
2447+
.tp_vectorcall = vc_zeroarg_vectorcall,
2448+
};
2449+
2450+
23172451
/*[clinic input]
23182452
output push
23192453
destination kwarg new file '{dirname}/clinic/_testclinic_kwds.c.h'
@@ -2533,6 +2667,18 @@ PyInit__testclinic(void)
25332667
if (PyModule_AddType(m, &DeprKwdInitNoInline) < 0) {
25342668
goto error;
25352669
}
2670+
if (PyModule_AddType(m, &VcNew_Type) < 0) {
2671+
goto error;
2672+
}
2673+
if (PyModule_AddType(m, &VcInit_Type) < 0) {
2674+
goto error;
2675+
}
2676+
if (PyModule_AddType(m, &VcNewExact_Type) < 0) {
2677+
goto error;
2678+
}
2679+
if (PyModule_AddType(m, &VcNewZeroArg_Type) < 0) {
2680+
goto error;
2681+
}
25362682
return m;
25372683

25382684
error:

0 commit comments

Comments
 (0)