#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..."}, {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, 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}, {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 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}, {"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); }