mirror of
https://github.com/python/cpython.git
synced 2025-12-23 09:19:18 +00:00
bpo-38787: C API for module state access from extension methods (PEP 573) (GH-19936)
Module C state is now accessible from C-defined heap type methods (PEP 573). Patch by Marcel Plch and Petr Viktorin. Co-authored-by: Marcel Plch <mplch@redhat.com> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
4638c64295
commit
e1becf46b4
19 changed files with 797 additions and 51 deletions
|
|
@ -4,6 +4,19 @@
|
|||
|
||||
#include "Python.h"
|
||||
|
||||
/* State for testing module state access from methods */
|
||||
|
||||
typedef struct {
|
||||
int counter;
|
||||
} meth_state;
|
||||
|
||||
/*[clinic input]
|
||||
module _testmultiphase
|
||||
|
||||
class _testmultiphase.StateAccessType "StateAccessTypeObject *" "!StateAccessType"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bab9f2fe3bd312ff]*/
|
||||
|
||||
/* Example objects */
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
|
@ -14,6 +27,10 @@ typedef struct {
|
|||
PyObject *integer;
|
||||
} testmultiphase_state;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
} StateAccessTypeObject;
|
||||
|
||||
/* Example methods */
|
||||
|
||||
static int
|
||||
|
|
@ -42,6 +59,7 @@ Example_demo(ExampleObject *self, PyObject *args)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#include "clinic/_testmultiphase.c.h"
|
||||
|
||||
static PyMethodDef Example_methods[] = {
|
||||
{"demo", (PyCFunction)Example_demo, METH_VARARGS,
|
||||
|
|
@ -102,6 +120,150 @@ static PyType_Spec Example_Type_spec = {
|
|||
Example_Type_slots
|
||||
};
|
||||
|
||||
|
||||
/*[clinic input]
|
||||
_testmultiphase.StateAccessType.get_defining_module
|
||||
|
||||
cls: defining_class
|
||||
|
||||
Return the module of the defining class.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_get_defining_module_impl(StateAccessTypeObject *self,
|
||||
PyTypeObject *cls)
|
||||
/*[clinic end generated code: output=ba2a14284a5d0921 input=946149f91cf72c0d]*/
|
||||
{
|
||||
PyObject *retval;
|
||||
retval = PyType_GetModule(cls);
|
||||
if (retval == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
Py_INCREF(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_testmultiphase.StateAccessType.increment_count_clinic
|
||||
|
||||
cls: defining_class
|
||||
/
|
||||
n: int = 1
|
||||
*
|
||||
twice: bool = False
|
||||
|
||||
Add 'n' from the module-state counter.
|
||||
|
||||
Pass 'twice' to double that amount.
|
||||
|
||||
This tests Argument Clinic support for defining_class.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_increment_count_clinic_impl(StateAccessTypeObject *self,
|
||||
PyTypeObject *cls,
|
||||
int n, int twice)
|
||||
/*[clinic end generated code: output=3b34f86bc5473204 input=551d482e1fe0b8f5]*/
|
||||
{
|
||||
meth_state *m_state = PyType_GetModuleState(cls);
|
||||
if (twice) {
|
||||
n *= 2;
|
||||
}
|
||||
m_state->counter += n;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_StateAccessType_decrement_count__doc__,
|
||||
"decrement_count($self, /, n=1, *, twice=None)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Add 'n' from the module-state counter.\n"
|
||||
"Pass 'twice' to double that amount.\n"
|
||||
"(This is to test both positional and keyword arguments.");
|
||||
|
||||
// Intentionally does not use Argument Clinic
|
||||
static PyObject *
|
||||
_StateAccessType_increment_count_noclinic(StateAccessTypeObject *self,
|
||||
PyTypeObject *defining_class,
|
||||
PyObject *const *args,
|
||||
Py_ssize_t nargs,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
if (!_PyArg_CheckPositional("StateAccessTypeObject.decrement_count", nargs, 0, 1)) {
|
||||
return NULL;
|
||||
}
|
||||
long n = 1;
|
||||
if (nargs) {
|
||||
n = PyLong_AsLong(args[0]);
|
||||
if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (kwnames && PyTuple_Check(kwnames)) {
|
||||
if (PyTuple_GET_SIZE(kwnames) > 1 ||
|
||||
PyUnicode_CompareWithASCIIString(
|
||||
PyTuple_GET_ITEM(kwnames, 0),
|
||||
"twice"
|
||||
)) {
|
||||
PyErr_SetString(
|
||||
PyExc_TypeError,
|
||||
"decrement_count only takes 'twice' keyword argument"
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
n *= 2;
|
||||
}
|
||||
meth_state *m_state = PyType_GetModuleState(defining_class);
|
||||
m_state->counter += n;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_testmultiphase.StateAccessType.get_count
|
||||
|
||||
cls: defining_class
|
||||
|
||||
Return the value of the module-state counter.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_get_count_impl(StateAccessTypeObject *self,
|
||||
PyTypeObject *cls)
|
||||
/*[clinic end generated code: output=64600f95b499a319 input=d5d181f12384849f]*/
|
||||
{
|
||||
meth_state *m_state = PyType_GetModuleState(cls);
|
||||
return PyLong_FromLong(m_state->counter);
|
||||
}
|
||||
|
||||
static PyMethodDef StateAccessType_methods[] = {
|
||||
_TESTMULTIPHASE_STATEACCESSTYPE_GET_DEFINING_MODULE_METHODDEF
|
||||
_TESTMULTIPHASE_STATEACCESSTYPE_GET_COUNT_METHODDEF
|
||||
_TESTMULTIPHASE_STATEACCESSTYPE_INCREMENT_COUNT_CLINIC_METHODDEF
|
||||
{
|
||||
"increment_count_noclinic",
|
||||
(PyCFunction)(void(*)(void))_StateAccessType_increment_count_noclinic,
|
||||
METH_METHOD|METH_FASTCALL|METH_KEYWORDS,
|
||||
_StateAccessType_decrement_count__doc__
|
||||
},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static PyType_Slot StateAccessType_Type_slots[] = {
|
||||
{Py_tp_doc, "Type for testing per-module state access from methods."},
|
||||
{Py_tp_methods, StateAccessType_methods},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static PyType_Spec StateAccessType_spec = {
|
||||
"_testimportexec.StateAccessType",
|
||||
sizeof(StateAccessTypeObject),
|
||||
0,
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE | Py_TPFLAGS_BASETYPE,
|
||||
StateAccessType_Type_slots
|
||||
};
|
||||
|
||||
/* Function of two integers returning integer */
|
||||
|
||||
PyDoc_STRVAR(testexport_foo_doc,
|
||||
|
|
@ -193,30 +355,39 @@ static int execfunc(PyObject *m)
|
|||
|
||||
/* Add a custom type */
|
||||
temp = PyType_FromSpec(&Example_Type_spec);
|
||||
if (temp == NULL)
|
||||
if (temp == NULL) {
|
||||
goto fail;
|
||||
if (PyModule_AddObject(m, "Example", temp) != 0)
|
||||
}
|
||||
if (PyModule_AddObject(m, "Example", temp) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* Add an exception type */
|
||||
temp = PyErr_NewException("_testimportexec.error", NULL, NULL);
|
||||
if (temp == NULL)
|
||||
if (temp == NULL) {
|
||||
goto fail;
|
||||
if (PyModule_AddObject(m, "error", temp) != 0)
|
||||
}
|
||||
if (PyModule_AddObject(m, "error", temp) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Add Str */
|
||||
temp = PyType_FromSpec(&Str_Type_spec);
|
||||
if (temp == NULL)
|
||||
if (temp == NULL) {
|
||||
goto fail;
|
||||
if (PyModule_AddObject(m, "Str", temp) != 0)
|
||||
}
|
||||
if (PyModule_AddObject(m, "Str", temp) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (PyModule_AddIntConstant(m, "int_const", 1969) != 0)
|
||||
if (PyModule_AddIntConstant(m, "int_const", 1969) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (PyModule_AddStringConstant(m, "str_const", "something different") != 0)
|
||||
if (PyModule_AddStringConstant(m, "str_const", "something different") != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
|
|
@ -620,6 +791,54 @@ PyInit__testmultiphase_exec_unreported_exception(PyObject *spec)
|
|||
return PyModuleDef_Init(&def_exec_unreported_exception);
|
||||
}
|
||||
|
||||
static int
|
||||
meth_state_access_exec(PyObject *m)
|
||||
{
|
||||
PyObject *temp;
|
||||
meth_state *m_state;
|
||||
|
||||
m_state = PyModule_GetState(m);
|
||||
if (m_state == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
temp = PyType_FromModuleAndSpec(m, &StateAccessType_spec, NULL);
|
||||
if (temp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (PyModule_AddObject(m, "StateAccessType", temp) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyModuleDef_Slot meth_state_access_slots[] = {
|
||||
{Py_mod_exec, meth_state_access_exec},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static PyModuleDef def_meth_state_access = {
|
||||
PyModuleDef_HEAD_INIT, /* m_base */
|
||||
"_testmultiphase_meth_state_access", /* m_name */
|
||||
PyDoc_STR("Module testing access"
|
||||
" to state from methods."),
|
||||
sizeof(meth_state), /* m_size */
|
||||
NULL, /* m_methods */
|
||||
meth_state_access_slots, /* m_slots */
|
||||
0, /* m_traverse */
|
||||
0, /* m_clear */
|
||||
0, /* m_free */
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__testmultiphase_meth_state_access(PyObject *spec)
|
||||
{
|
||||
return PyModuleDef_Init(&def_meth_state_access);
|
||||
}
|
||||
|
||||
|
||||
/*** Helper for imp test ***/
|
||||
|
||||
static PyModuleDef imp_dummy_def = TEST_MODULE_DEF("imp_dummy", main_slots, testexport_methods);
|
||||
|
|
|
|||
101
Modules/clinic/_testmultiphase.c.h
generated
Normal file
101
Modules/clinic/_testmultiphase.c.h
generated
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*[clinic input]
|
||||
preserve
|
||||
[clinic start generated code]*/
|
||||
|
||||
PyDoc_STRVAR(_testmultiphase_StateAccessType_get_defining_module__doc__,
|
||||
"get_defining_module($self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return the module of the defining class.");
|
||||
|
||||
#define _TESTMULTIPHASE_STATEACCESSTYPE_GET_DEFINING_MODULE_METHODDEF \
|
||||
{"get_defining_module", (PyCFunction)(void(*)(void))_testmultiphase_StateAccessType_get_defining_module, METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _testmultiphase_StateAccessType_get_defining_module__doc__},
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_get_defining_module_impl(StateAccessTypeObject *self,
|
||||
PyTypeObject *cls);
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_get_defining_module(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
static const char * const _keywords[] = { NULL};
|
||||
static _PyArg_Parser _parser = {":get_defining_module", _keywords, 0};
|
||||
|
||||
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser
|
||||
)) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = _testmultiphase_StateAccessType_get_defining_module_impl(self, cls);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_testmultiphase_StateAccessType_increment_count_clinic__doc__,
|
||||
"increment_count_clinic($self, /, n=1, *, twice=False)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Add \'n\' from the module-state counter.\n"
|
||||
"\n"
|
||||
"Pass \'twice\' to double that amount.\n"
|
||||
"\n"
|
||||
"This tests Argument Clinic support for defining_class.");
|
||||
|
||||
#define _TESTMULTIPHASE_STATEACCESSTYPE_INCREMENT_COUNT_CLINIC_METHODDEF \
|
||||
{"increment_count_clinic", (PyCFunction)(void(*)(void))_testmultiphase_StateAccessType_increment_count_clinic, METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _testmultiphase_StateAccessType_increment_count_clinic__doc__},
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_increment_count_clinic_impl(StateAccessTypeObject *self,
|
||||
PyTypeObject *cls,
|
||||
int n, int twice);
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_increment_count_clinic(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
static const char * const _keywords[] = {"n", "twice", NULL};
|
||||
static _PyArg_Parser _parser = {"|i$p:increment_count_clinic", _keywords, 0};
|
||||
int n = 1;
|
||||
int twice = 0;
|
||||
|
||||
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
|
||||
&n, &twice)) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = _testmultiphase_StateAccessType_increment_count_clinic_impl(self, cls, n, twice);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_testmultiphase_StateAccessType_get_count__doc__,
|
||||
"get_count($self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return the value of the module-state counter.");
|
||||
|
||||
#define _TESTMULTIPHASE_STATEACCESSTYPE_GET_COUNT_METHODDEF \
|
||||
{"get_count", (PyCFunction)(void(*)(void))_testmultiphase_StateAccessType_get_count, METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _testmultiphase_StateAccessType_get_count__doc__},
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_get_count_impl(StateAccessTypeObject *self,
|
||||
PyTypeObject *cls);
|
||||
|
||||
static PyObject *
|
||||
_testmultiphase_StateAccessType_get_count(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
static const char * const _keywords[] = { NULL};
|
||||
static _PyArg_Parser _parser = {":get_count", _keywords, 0};
|
||||
|
||||
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser
|
||||
)) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = _testmultiphase_StateAccessType_get_count_impl(self, cls);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=39eea487e94e7f5d input=a9049054013a1b77]*/
|
||||
Loading…
Add table
Add a link
Reference in a new issue