Skip to content

Commit b511d35

Browse files
GH-100997: Implement Multi-Phase Init for the _testinternalcapi Module (gh-100998)
_testinternalcapi is an internal module used for testing. #100997
1 parent 005e694 commit b511d35

File tree

3 files changed

+117
-33
lines changed

3 files changed

+117
-33
lines changed

Modules/_testinternalcapi.c

+117-30
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,60 @@
2828

2929
#include "clinic/_testinternalcapi.c.h"
3030

31+
32+
#define MODULE_NAME "_testinternalcapi"
33+
34+
35+
static PyObject *
36+
_get_current_module(void)
37+
{
38+
// We ensured it was imported in _run_script().
39+
PyObject *name = PyUnicode_FromString(MODULE_NAME);
40+
if (name == NULL) {
41+
return NULL;
42+
}
43+
PyObject *mod = PyImport_GetModule(name);
44+
Py_DECREF(name);
45+
if (mod == NULL) {
46+
return NULL;
47+
}
48+
assert(mod != Py_None);
49+
return mod;
50+
}
51+
52+
53+
/* module state *************************************************************/
54+
55+
typedef struct {
56+
PyObject *record_list;
57+
} module_state;
58+
59+
static inline module_state *
60+
get_module_state(PyObject *mod)
61+
{
62+
assert(mod != NULL);
63+
module_state *state = PyModule_GetState(mod);
64+
assert(state != NULL);
65+
return state;
66+
}
67+
68+
static int
69+
traverse_module_state(module_state *state, visitproc visit, void *arg)
70+
{
71+
Py_VISIT(state->record_list);
72+
return 0;
73+
}
74+
75+
static int
76+
clear_module_state(module_state *state)
77+
{
78+
Py_CLEAR(state->record_list);
79+
return 0;
80+
}
81+
82+
83+
/* module functions *********************************************************/
84+
3185
/*[clinic input]
3286
module _testinternalcapi
3387
[clinic start generated code]*/
@@ -496,21 +550,23 @@ decode_locale_ex(PyObject *self, PyObject *args)
496550
return res;
497551
}
498552

499-
static PyObject *record_list = NULL;
500-
501553
static PyObject *
502554
set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args))
503555
{
556+
module_state *state = get_module_state(self);
504557
_PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), _PyEval_EvalFrameDefault);
505-
Py_CLEAR(record_list);
558+
Py_CLEAR(state->record_list);
506559
Py_RETURN_NONE;
507560
}
508561

509562
static PyObject *
510563
record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
511564
{
512565
if (PyFunction_Check(f->f_funcobj)) {
513-
PyList_Append(record_list, ((PyFunctionObject *)f->f_funcobj)->func_name);
566+
PyObject *module = _get_current_module();
567+
assert(module != NULL);
568+
module_state *state = get_module_state(module);
569+
PyList_Append(state->record_list, ((PyFunctionObject *)f->f_funcobj)->func_name);
514570
}
515571
return _PyEval_EvalFrameDefault(tstate, f, exc);
516572
}
@@ -519,11 +575,12 @@ record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
519575
static PyObject *
520576
set_eval_frame_record(PyObject *self, PyObject *list)
521577
{
578+
module_state *state = get_module_state(self);
522579
if (!PyList_Check(list)) {
523580
PyErr_SetString(PyExc_TypeError, "argument must be a list");
524581
return NULL;
525582
}
526-
Py_XSETREF(record_list, Py_NewRef(list));
583+
Py_XSETREF(state->record_list, Py_NewRef(list));
527584
_PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), record_eval);
528585
Py_RETURN_NONE;
529586
}
@@ -613,7 +670,7 @@ get_interp_settings(PyObject *self, PyObject *args)
613670
}
614671

615672

616-
static PyMethodDef TestMethods[] = {
673+
static PyMethodDef module_functions[] = {
617674
{"get_configs", get_configs, METH_NOARGS},
618675
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
619676
{"test_bswap", test_bswap, METH_NOARGS},
@@ -638,35 +695,65 @@ static PyMethodDef TestMethods[] = {
638695
};
639696

640697

641-
static struct PyModuleDef _testcapimodule = {
642-
PyModuleDef_HEAD_INIT,
643-
"_testinternalcapi",
644-
NULL,
645-
-1,
646-
TestMethods,
647-
NULL,
648-
NULL,
649-
NULL,
650-
NULL
651-
};
652-
698+
/* initialization function */
653699

654-
PyMODINIT_FUNC
655-
PyInit__testinternalcapi(void)
700+
static int
701+
module_exec(PyObject *module)
656702
{
657-
PyObject *module = PyModule_Create(&_testcapimodule);
658-
if (module == NULL) {
659-
return NULL;
660-
}
661-
662703
if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD",
663704
PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
664-
goto error;
705+
return 1;
665706
}
666707

667-
return module;
708+
return 0;
709+
}
668710

669-
error:
670-
Py_DECREF(module);
671-
return NULL;
711+
static struct PyModuleDef_Slot module_slots[] = {
712+
{Py_mod_exec, module_exec},
713+
{0, NULL},
714+
};
715+
716+
static int
717+
module_traverse(PyObject *module, visitproc visit, void *arg)
718+
{
719+
module_state *state = get_module_state(module);
720+
assert(state != NULL);
721+
traverse_module_state(state, visit, arg);
722+
return 0;
723+
}
724+
725+
static int
726+
module_clear(PyObject *module)
727+
{
728+
module_state *state = get_module_state(module);
729+
assert(state != NULL);
730+
(void)clear_module_state(state);
731+
return 0;
732+
}
733+
734+
static void
735+
module_free(void *module)
736+
{
737+
module_state *state = get_module_state(module);
738+
assert(state != NULL);
739+
(void)clear_module_state(state);
740+
}
741+
742+
static struct PyModuleDef _testcapimodule = {
743+
.m_base = PyModuleDef_HEAD_INIT,
744+
.m_name = MODULE_NAME,
745+
.m_doc = NULL,
746+
.m_size = sizeof(module_state),
747+
.m_methods = module_functions,
748+
.m_slots = module_slots,
749+
.m_traverse = module_traverse,
750+
.m_clear = module_clear,
751+
.m_free = (freefunc)module_free,
752+
};
753+
754+
755+
PyMODINIT_FUNC
756+
PyInit__testinternalcapi(void)
757+
{
758+
return PyModuleDef_Init(&_testcapimodule);
672759
}

Tools/c-analyzer/cpython/globals-to-fix.tsv

-1
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,6 @@ Modules/_asynciomodule.c - all_tasks -
523523
Modules/_asynciomodule.c - current_tasks -
524524
Modules/_asynciomodule.c - iscoroutine_typecache -
525525
Modules/_ctypes/_ctypes.c - _ctypes_ptrtype_cache -
526-
Modules/_testinternalcapi.c - record_list -
527526
Modules/_tkinter.c - tcl_lock -
528527
Modules/_tkinter.c - excInCmd -
529528
Modules/_tkinter.c - valInCmd -

Tools/c-analyzer/cpython/ignored.tsv

-2
Original file line numberDiff line numberDiff line change
@@ -483,8 +483,6 @@ Modules/_testcapimodule.c - g_type_watchers_installed -
483483
Modules/_testimportmultiple.c - _barmodule -
484484
Modules/_testimportmultiple.c - _foomodule -
485485
Modules/_testimportmultiple.c - _testimportmultiple -
486-
Modules/_testinternalcapi.c - TestMethods -
487-
Modules/_testinternalcapi.c - _testcapimodule -
488486
Modules/_testmultiphase.c - Example_Type_slots -
489487
Modules/_testmultiphase.c - Example_Type_spec -
490488
Modules/_testmultiphase.c - Example_methods -

0 commit comments

Comments
 (0)