mirror of
https://github.com/python/cpython.git
synced 2026-02-22 15:10:47 +00:00
When integrating slots-based module creation is with the inittab, which currently requires PyModuleDef, it would be convenient to reuse the the same slots array for the MethodDef. Allow slots that match what's already present in the PyModuleDef.
446 lines
12 KiB
C
446 lines
12 KiB
C
#include "parts.h"
|
|
#include "util.h"
|
|
#include <stdbool.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..."},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{0},
|
|
};
|
|
return PyModule_FromSlotsAndSpec(slots, spec);
|
|
}
|
|
|
|
static PyObject *
|
|
module_from_slots_doc(PyObject *self, PyObject *spec)
|
|
{
|
|
PyModuleDef_Slot slots[] = {
|
|
{Py_mod_doc, "the docstring"},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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, PyModuleDef *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},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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"},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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"},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{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 const char parrot_name[] = "test_capi/parrot";
|
|
static const char parrot_doc[] = "created from redundant information";
|
|
static PyModuleDef parrot_def = {
|
|
PyModuleDef_HEAD_INIT,
|
|
.m_name = (void*)parrot_name,
|
|
.m_doc = (void*)parrot_doc,
|
|
.m_size = 123,
|
|
.m_methods = a_methoddef_array,
|
|
.m_traverse = noop_traverse,
|
|
.m_clear = noop_clear,
|
|
.m_free = (void*)noop_free,
|
|
.m_slots = NULL /* set below */,
|
|
};
|
|
static PyModuleDef_Slot parrot_slots[] = {
|
|
{Py_mod_name, (void*)parrot_name},
|
|
{Py_mod_doc, (void*)parrot_doc},
|
|
{Py_mod_state_size, (void*)123},
|
|
{Py_mod_methods, a_methoddef_array},
|
|
{Py_mod_state_traverse, noop_traverse},
|
|
{Py_mod_state_clear, noop_clear},
|
|
{Py_mod_state_free, (void*)noop_free},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_token, &parrot_def},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{0},
|
|
};
|
|
|
|
static PyObject *
|
|
module_from_def_slot_parrot(PyObject *self, PyObject *spec)
|
|
{
|
|
parrot_def.m_slots = parrot_slots;
|
|
PyObject *module = PyModule_FromDefAndSpec(&parrot_def, spec);
|
|
if (!module || (PyModule_Exec(module) < 0)) {
|
|
return NULL;
|
|
}
|
|
Py_ssize_t size;
|
|
assert(PyModule_GetStateSize(module, &size) == 0);
|
|
assert(size == 123);
|
|
PyModuleDef *def = PyModule_GetDef(module);
|
|
assert(def == &parrot_def);
|
|
return module;
|
|
}
|
|
|
|
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_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{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},
|
|
{"module_from_def_slot_parrot", module_from_def_slot_parrot, 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);
|
|
}
|