Co-authored-by: Victor Stinner <vstinner@python.org> Co-authored-by: Kumar Aditya <kumaraditya@python.org>
379 lines
9.5 KiB
C
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);
|
|
}
|