forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pythongh-93649: Split vectorcall testing from _testcapimodule.c
The _testcapimodule.c file is getting too large to work with effectively. Vectorcall tests aren't the biggest issue -- it's just an area I want to work on next, so I started there. It does make it clear that MethodDescriptor2 is related to testing vectorcall, which wasn't clear before (the /* Test PEP 590 */ section had an ambiguous end). This PR lays out a general structure of how tests can be split up, with more splitting to come later if the structure is OK.
- Loading branch information
Showing
8 changed files
with
293 additions
and
254 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Tests in this directory are compiled into the _testcapi extension. | ||
The main file for the extension is Modules/_testcapimodule.c, which | ||
calls `_PyTestCapi_Init_*` from these functions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
#include "testcapimodule_parts.h" | ||
#include <stddef.h> // offsetof | ||
|
||
|
||
/* Test PEP 590 - Vectorcall */ | ||
|
||
static int | ||
fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs) | ||
{ | ||
if (args == Py_None) { | ||
*stack = NULL; | ||
*nargs = 0; | ||
} | ||
else if (PyTuple_Check(args)) { | ||
*stack = ((PyTupleObject *)args)->ob_item; | ||
*nargs = PyTuple_GET_SIZE(args); | ||
} | ||
else { | ||
PyErr_SetString(PyExc_TypeError, "args must be None or a tuple"); | ||
return -1; | ||
} | ||
return 0; | ||
} | ||
|
||
|
||
static PyObject * | ||
test_pyobject_fastcall(PyObject *self, PyObject *args) | ||
{ | ||
PyObject *func, *func_args; | ||
PyObject **stack; | ||
Py_ssize_t nargs; | ||
|
||
if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) { | ||
return NULL; | ||
} | ||
|
||
if (fastcall_args(func_args, &stack, &nargs) < 0) { | ||
return NULL; | ||
} | ||
return _PyObject_FastCall(func, stack, nargs); | ||
} | ||
|
||
static PyObject * | ||
test_pyobject_fastcalldict(PyObject *self, PyObject *args) | ||
{ | ||
PyObject *func, *func_args, *kwargs; | ||
PyObject **stack; | ||
Py_ssize_t nargs; | ||
|
||
if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) { | ||
return NULL; | ||
} | ||
|
||
if (fastcall_args(func_args, &stack, &nargs) < 0) { | ||
return NULL; | ||
} | ||
|
||
if (kwargs == Py_None) { | ||
kwargs = NULL; | ||
} | ||
else if (!PyDict_Check(kwargs)) { | ||
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict"); | ||
return NULL; | ||
} | ||
|
||
return PyObject_VectorcallDict(func, stack, nargs, kwargs); | ||
} | ||
|
||
static PyObject * | ||
test_pyobject_vectorcall(PyObject *self, PyObject *args) | ||
{ | ||
PyObject *func, *func_args, *kwnames = NULL; | ||
PyObject **stack; | ||
Py_ssize_t nargs, nkw; | ||
|
||
if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) { | ||
return NULL; | ||
} | ||
|
||
if (fastcall_args(func_args, &stack, &nargs) < 0) { | ||
return NULL; | ||
} | ||
|
||
if (kwnames == Py_None) { | ||
kwnames = NULL; | ||
} | ||
else if (PyTuple_Check(kwnames)) { | ||
nkw = PyTuple_GET_SIZE(kwnames); | ||
if (nargs < nkw) { | ||
PyErr_SetString(PyExc_ValueError, "kwnames longer than args"); | ||
return NULL; | ||
} | ||
nargs -= nkw; | ||
} | ||
else { | ||
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple"); | ||
return NULL; | ||
} | ||
return PyObject_Vectorcall(func, stack, nargs, kwnames); | ||
} | ||
|
||
static PyObject * | ||
test_pyvectorcall_call(PyObject *self, PyObject *args) | ||
{ | ||
PyObject *func; | ||
PyObject *argstuple; | ||
PyObject *kwargs = NULL; | ||
|
||
if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) { | ||
return NULL; | ||
} | ||
|
||
if (!PyTuple_Check(argstuple)) { | ||
PyErr_SetString(PyExc_TypeError, "args must be a tuple"); | ||
return NULL; | ||
} | ||
if (kwargs != NULL && !PyDict_Check(kwargs)) { | ||
PyErr_SetString(PyExc_TypeError, "kwargs must be a dict"); | ||
return NULL; | ||
} | ||
|
||
return PyVectorcall_Call(func, argstuple, kwargs); | ||
} | ||
|
||
static PyMethodDef TestMethods[] = { | ||
{"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS}, | ||
{"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS}, | ||
{"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS}, | ||
{"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS}, | ||
{NULL}, | ||
}; | ||
|
||
|
||
typedef struct { | ||
PyObject_HEAD | ||
vectorcallfunc vectorcall; | ||
} MethodDescriptorObject; | ||
|
||
static PyObject * | ||
MethodDescriptor_vectorcall(PyObject *callable, PyObject *const *args, | ||
size_t nargsf, PyObject *kwnames) | ||
{ | ||
/* True if using the vectorcall function in MethodDescriptorObject | ||
* but False for MethodDescriptor2Object */ | ||
MethodDescriptorObject *md = (MethodDescriptorObject *)callable; | ||
return PyBool_FromLong(md->vectorcall != NULL); | ||
} | ||
|
||
static PyObject * | ||
MethodDescriptor_new(PyTypeObject* type, PyObject* args, PyObject *kw) | ||
{ | ||
MethodDescriptorObject *op = (MethodDescriptorObject *)type->tp_alloc(type, 0); | ||
op->vectorcall = MethodDescriptor_vectorcall; | ||
return (PyObject *)op; | ||
} | ||
|
||
static PyObject * | ||
func_descr_get(PyObject *func, PyObject *obj, PyObject *type) | ||
{ | ||
if (obj == Py_None || obj == NULL) { | ||
Py_INCREF(func); | ||
return func; | ||
} | ||
return PyMethod_New(func, obj); | ||
} | ||
|
||
static PyObject * | ||
nop_descr_get(PyObject *func, PyObject *obj, PyObject *type) | ||
{ | ||
Py_INCREF(func); | ||
return func; | ||
} | ||
|
||
static PyObject * | ||
call_return_args(PyObject *self, PyObject *args, PyObject *kwargs) | ||
{ | ||
Py_INCREF(args); | ||
return args; | ||
} | ||
|
||
static PyTypeObject MethodDescriptorBase_Type = { | ||
PyVarObject_HEAD_INIT(NULL, 0) | ||
"MethodDescriptorBase", | ||
sizeof(MethodDescriptorObject), | ||
.tp_new = MethodDescriptor_new, | ||
.tp_call = PyVectorcall_Call, | ||
.tp_vectorcall_offset = offsetof(MethodDescriptorObject, vectorcall), | ||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | | ||
Py_TPFLAGS_METHOD_DESCRIPTOR | Py_TPFLAGS_HAVE_VECTORCALL, | ||
.tp_descr_get = func_descr_get, | ||
}; | ||
|
||
static PyTypeObject MethodDescriptorDerived_Type = { | ||
PyVarObject_HEAD_INIT(NULL, 0) | ||
"MethodDescriptorDerived", | ||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | ||
}; | ||
|
||
static PyTypeObject MethodDescriptorNopGet_Type = { | ||
PyVarObject_HEAD_INIT(NULL, 0) | ||
"MethodDescriptorNopGet", | ||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | ||
.tp_call = call_return_args, | ||
.tp_descr_get = nop_descr_get, | ||
}; | ||
|
||
typedef struct { | ||
MethodDescriptorObject base; | ||
vectorcallfunc vectorcall; | ||
} MethodDescriptor2Object; | ||
|
||
static PyObject * | ||
MethodDescriptor2_new(PyTypeObject* type, PyObject* args, PyObject *kw) | ||
{ | ||
MethodDescriptor2Object *op = PyObject_New(MethodDescriptor2Object, type); | ||
op->base.vectorcall = NULL; | ||
op->vectorcall = MethodDescriptor_vectorcall; | ||
return (PyObject *)op; | ||
} | ||
|
||
static PyTypeObject MethodDescriptor2_Type = { | ||
PyVarObject_HEAD_INIT(NULL, 0) | ||
"MethodDescriptor2", | ||
sizeof(MethodDescriptor2Object), | ||
.tp_new = MethodDescriptor2_new, | ||
.tp_call = PyVectorcall_Call, | ||
.tp_vectorcall_offset = offsetof(MethodDescriptor2Object, vectorcall), | ||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL, | ||
}; | ||
|
||
|
||
int | ||
_PyTestCapi_Init_Vectorcall(PyObject *m) { | ||
if (PyModule_AddFunctions(m, TestMethods) < 0) { | ||
return -1; | ||
} | ||
|
||
if (PyType_Ready(&MethodDescriptorBase_Type) < 0) { | ||
return -1; | ||
} | ||
if (PyModule_AddType(m, &MethodDescriptorBase_Type) < 0) { | ||
return -1; | ||
} | ||
|
||
MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type; | ||
if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) { | ||
return -1; | ||
} | ||
if (PyModule_AddType(m, &MethodDescriptorDerived_Type) < 0) { | ||
return -1; | ||
} | ||
|
||
MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type; | ||
if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) { | ||
return -1; | ||
} | ||
if (PyModule_AddType(m, &MethodDescriptorNopGet_Type) < 0) { | ||
return -1; | ||
} | ||
|
||
MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type; | ||
if (PyType_Ready(&MethodDescriptor2_Type) < 0) { | ||
return -1; | ||
} | ||
if (PyModule_AddType(m, &MethodDescriptor2_Type) < 0) { | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#include "Python.h" | ||
|
||
PyAPI_FUNC(int) _PyTestCapi_Init_Vectorcall(PyObject *module); |
Oops, something went wrong.