Files
cpython/Modules/_testcapi/module.c
Petr Viktorin 589a03a8ce gh-140550: Initial implementation of PEP 793 – PyModExport (GH-140556)
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
2025-11-05 12:31:42 +01:00

379 lines
9.5 KiB
C

#include "parts.h"
#include "util.h"
// Test PyModule_* API
/* unittest Cases that use these functions are in:
* Lib/test/test_capi/test_module.py
*/
static PyObject *
module_from_slots_empty(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{0},
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
static PyObject *
module_from_slots_null(PyObject *self, PyObject *spec)
{
return PyModule_FromSlotsAndSpec(NULL, spec);
}
static PyObject *
module_from_slots_name(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_name, "currently ignored..."},
{0},
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
static PyObject *
module_from_slots_doc(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_doc, "the docstring"},
{0},
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
static PyObject *
module_from_slots_size(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_state_size, (void*)123},
{0},
};
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
if (!mod) {
return NULL;
}
return mod;
}
static PyObject *
a_method(PyObject *self, PyObject *arg)
{
return PyTuple_Pack(2, self, arg);
}
static PyMethodDef a_methoddef_array[] = {
{"a_method", a_method, METH_O},
{0},
};
static PyObject *
module_from_slots_methods(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_methods, a_methoddef_array},
{0},
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
static int noop_traverse(PyObject *self, visitproc visit, void *arg) {
return 0;
}
static int noop_clear(PyObject *self) { return 0; }
static void noop_free(void *self) { }
static PyObject *
module_from_slots_gc(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_state_traverse, noop_traverse},
{Py_mod_state_clear, noop_clear},
{Py_mod_state_free, noop_free},
{0},
};
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
if (!mod) {
return NULL;
}
if (PyModule_Add(mod, "traverse", PyLong_FromVoidPtr(&noop_traverse)) < 0) {
Py_DECREF(mod);
return NULL;
}
if (PyModule_Add(mod, "clear", PyLong_FromVoidPtr(&noop_clear)) < 0) {
Py_DECREF(mod);
return NULL;
}
if (PyModule_Add(mod, "free", PyLong_FromVoidPtr(&noop_free)) < 0) {
Py_DECREF(mod);
return NULL;
}
return mod;
}
static const char test_token;
static PyObject *
module_from_slots_token(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_token, (void*)&test_token},
{0},
};
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
if (!mod) {
return NULL;
}
void *got_token;
if (PyModule_GetToken(mod, &got_token) < 0) {
Py_DECREF(mod);
return NULL;
}
assert(got_token == &test_token);
return mod;
}
static int
simple_exec(PyObject *module)
{
return PyModule_AddIntConstant(module, "a_number", 456);
}
static PyObject *
module_from_slots_exec(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_exec, simple_exec},
{0},
};
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
if (!mod) {
return NULL;
}
int res = PyObject_HasAttrStringWithError(mod, "a_number");
if (res < 0) {
Py_DECREF(mod);
return NULL;
}
assert(res == 0);
if (PyModule_Exec(mod) < 0) {
Py_DECREF(mod);
return NULL;
}
return mod;
}
static PyObject *
create_attr_from_spec(PyObject *spec, PyObject *def)
{
assert(!def);
return PyObject_GetAttrString(spec, "_gimme_this");
}
static PyObject *
module_from_slots_create(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
{Py_mod_create, create_attr_from_spec},
{0},
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
static int
slot_from_object(PyObject *obj)
{
PyObject *slot_id_obj = PyObject_GetAttrString(obj, "_test_slot_id");
if (slot_id_obj == NULL) {
return -1;
}
int slot_id = PyLong_AsInt(slot_id_obj);
if (PyErr_Occurred()) {
return -1;
}
return slot_id;
}
static PyObject *
module_from_slots_repeat_slot(PyObject *self, PyObject *spec)
{
int slot_id = slot_from_object(spec);
if (slot_id < 0) {
return NULL;
}
PyModuleDef_Slot slots[] = {
{slot_id, "anything"},
{slot_id, "anything else"},
{0},
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
static PyObject *
module_from_slots_null_slot(PyObject *self, PyObject *spec)
{
int slot_id = slot_from_object(spec);
if (slot_id < 0) {
return NULL;
}
PyModuleDef_Slot slots[] = {
{slot_id, NULL},
{0},
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
static PyObject *
module_from_def_slot(PyObject *self, PyObject *spec)
{
int slot_id = slot_from_object(spec);
if (slot_id < 0) {
return NULL;
}
PyModuleDef_Slot slots[] = {
{slot_id, "anything"},
{0},
};
PyModuleDef def = {
PyModuleDef_HEAD_INIT,
.m_name = "currently ignored",
.m_slots = slots,
};
// PyModuleDef is normally static; the real requirement is that it
// must outlive its module.
// Here, module creation fails, so it's fine on the stack.
PyObject *result = PyModule_FromDefAndSpec(&def, spec);
assert(result == NULL);
return result;
}
static int
another_exec(PyObject *module)
{
/* Make sure simple_exec was called */
assert(PyObject_HasAttrString(module, "a_number"));
/* Add or negate a global called 'another_number' */
PyObject *another_number;
if (PyObject_GetOptionalAttrString(module, "another_number",
&another_number) < 0) {
return -1;
}
if (!another_number) {
return PyModule_AddIntConstant(module, "another_number", 789);
}
PyObject *neg_number = PyNumber_Negative(another_number);
Py_DECREF(another_number);
if (!neg_number) {
return -1;
}
int result = PyObject_SetAttrString(module, "another_number",
neg_number);
Py_DECREF(neg_number);
return result;
}
static PyObject *
module_from_def_multiple_exec(PyObject *self, PyObject *spec)
{
static PyModuleDef_Slot slots[] = {
{Py_mod_exec, simple_exec},
{Py_mod_exec, another_exec},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
{0},
};
static PyModuleDef def = {
PyModuleDef_HEAD_INIT,
.m_name = "currently ignored",
.m_slots = slots,
};
return PyModule_FromDefAndSpec(&def, spec);
}
static PyObject *
pymodule_exec(PyObject *self, PyObject *module)
{
if (PyModule_Exec(module) < 0) {
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
pymodule_get_token(PyObject *self, PyObject *module)
{
void *token;
if (PyModule_GetToken(module, &token) < 0) {
return NULL;
}
return PyLong_FromVoidPtr(token);
}
static PyObject *
pymodule_get_def(PyObject *self, PyObject *module)
{
PyModuleDef *def = PyModule_GetDef(module);
if (!def && PyErr_Occurred()) {
return NULL;
}
return PyLong_FromVoidPtr(def);
}
static PyObject *
pymodule_get_state_size(PyObject *self, PyObject *module)
{
Py_ssize_t size;
if (PyModule_GetStateSize(module, &size) < 0) {
return NULL;
}
return PyLong_FromSsize_t(size);
}
static PyMethodDef test_methods[] = {
{"module_from_slots_empty", module_from_slots_empty, METH_O},
{"module_from_slots_null", module_from_slots_null, METH_O},
{"module_from_slots_name", module_from_slots_name, METH_O},
{"module_from_slots_doc", module_from_slots_doc, METH_O},
{"module_from_slots_size", module_from_slots_size, METH_O},
{"module_from_slots_methods", module_from_slots_methods, METH_O},
{"module_from_slots_gc", module_from_slots_gc, METH_O},
{"module_from_slots_token", module_from_slots_token, METH_O},
{"module_from_slots_exec", module_from_slots_exec, METH_O},
{"module_from_slots_create", module_from_slots_create, METH_O},
{"module_from_slots_repeat_slot", module_from_slots_repeat_slot, METH_O},
{"module_from_slots_null_slot", module_from_slots_null_slot, METH_O},
{"module_from_def_multiple_exec", module_from_def_multiple_exec, METH_O},
{"module_from_def_slot", module_from_def_slot, METH_O},
{"pymodule_get_token", pymodule_get_token, METH_O},
{"pymodule_get_def", pymodule_get_def, METH_O},
{"pymodule_get_state_size", pymodule_get_state_size, METH_O},
{"pymodule_exec", pymodule_exec, METH_O},
{NULL},
};
int
_PyTestCapi_Init_Module(PyObject *m)
{
#define ADD_INT_MACRO(C) if (PyModule_AddIntConstant(m, #C, C) < 0) return -1;
ADD_INT_MACRO(Py_mod_create);
ADD_INT_MACRO(Py_mod_exec);
ADD_INT_MACRO(Py_mod_multiple_interpreters);
ADD_INT_MACRO(Py_mod_gil);
ADD_INT_MACRO(Py_mod_name);
ADD_INT_MACRO(Py_mod_doc);
ADD_INT_MACRO(Py_mod_state_size);
ADD_INT_MACRO(Py_mod_methods);
ADD_INT_MACRO(Py_mod_state_traverse);
ADD_INT_MACRO(Py_mod_state_clear);
ADD_INT_MACRO(Py_mod_state_free);
ADD_INT_MACRO(Py_mod_token);
#undef ADD_INT_MACRO
if (PyModule_Add(m, "module_test_token",
PyLong_FromVoidPtr((void*)&test_token)) < 0)
{
return -1;
}
return PyModule_AddFunctions(m, test_methods);
}