]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-100997: Implement Multi-Phase Init for the _testinternalcapi Module (gh-100998)
authorEric Snow <ericsnowcurrently@gmail.com>
Thu, 12 Jan 2023 20:42:03 +0000 (13:42 -0700)
committerGitHub <noreply@github.com>
Thu, 12 Jan 2023 20:42:03 +0000 (13:42 -0700)
_testinternalcapi is an internal module used for testing.

https://github.com/python/cpython/issues/100997

Modules/_testinternalcapi.c
Tools/c-analyzer/cpython/globals-to-fix.tsv
Tools/c-analyzer/cpython/ignored.tsv

index b14b8ac3c7405465f21cc94ab034ae7d01489a9e..f53929f80f8adf73b5655c274d299646d4690e27 100644 (file)
 
 #include "clinic/_testinternalcapi.c.h"
 
+
+#define MODULE_NAME "_testinternalcapi"
+
+
+static PyObject *
+_get_current_module(void)
+{
+    // We ensured it was imported in _run_script().
+    PyObject *name = PyUnicode_FromString(MODULE_NAME);
+    if (name == NULL) {
+        return NULL;
+    }
+    PyObject *mod = PyImport_GetModule(name);
+    Py_DECREF(name);
+    if (mod == NULL) {
+        return NULL;
+    }
+    assert(mod != Py_None);
+    return mod;
+}
+
+
+/* module state *************************************************************/
+
+typedef struct {
+    PyObject *record_list;
+} module_state;
+
+static inline module_state *
+get_module_state(PyObject *mod)
+{
+    assert(mod != NULL);
+    module_state *state = PyModule_GetState(mod);
+    assert(state != NULL);
+    return state;
+}
+
+static int
+traverse_module_state(module_state *state, visitproc visit, void *arg)
+{
+    Py_VISIT(state->record_list);
+    return 0;
+}
+
+static int
+clear_module_state(module_state *state)
+{
+    Py_CLEAR(state->record_list);
+    return 0;
+}
+
+
+/* module functions *********************************************************/
+
 /*[clinic input]
 module _testinternalcapi
 [clinic start generated code]*/
@@ -496,13 +550,12 @@ decode_locale_ex(PyObject *self, PyObject *args)
     return res;
 }
 
-static PyObject *record_list = NULL;
-
 static PyObject *
 set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args))
 {
+    module_state *state = get_module_state(self);
     _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), _PyEval_EvalFrameDefault);
-    Py_CLEAR(record_list);
+    Py_CLEAR(state->record_list);
     Py_RETURN_NONE;
 }
 
@@ -510,7 +563,10 @@ static PyObject *
 record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
 {
     if (PyFunction_Check(f->f_funcobj)) {
-        PyList_Append(record_list, ((PyFunctionObject *)f->f_funcobj)->func_name);
+        PyObject *module = _get_current_module();
+        assert(module != NULL);
+        module_state *state = get_module_state(module);
+        PyList_Append(state->record_list, ((PyFunctionObject *)f->f_funcobj)->func_name);
     }
     return _PyEval_EvalFrameDefault(tstate, f, exc);
 }
@@ -519,11 +575,12 @@ record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
 static PyObject *
 set_eval_frame_record(PyObject *self, PyObject *list)
 {
+    module_state *state = get_module_state(self);
     if (!PyList_Check(list)) {
         PyErr_SetString(PyExc_TypeError, "argument must be a list");
         return NULL;
     }
-    Py_XSETREF(record_list, Py_NewRef(list));
+    Py_XSETREF(state->record_list, Py_NewRef(list));
     _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), record_eval);
     Py_RETURN_NONE;
 }
@@ -613,7 +670,7 @@ get_interp_settings(PyObject *self, PyObject *args)
 }
 
 
-static PyMethodDef TestMethods[] = {
+static PyMethodDef module_functions[] = {
     {"get_configs", get_configs, METH_NOARGS},
     {"get_recursion_depth", get_recursion_depth, METH_NOARGS},
     {"test_bswap", test_bswap, METH_NOARGS},
@@ -638,35 +695,65 @@ static PyMethodDef TestMethods[] = {
 };
 
 
-static struct PyModuleDef _testcapimodule = {
-    PyModuleDef_HEAD_INIT,
-    "_testinternalcapi",
-    NULL,
-    -1,
-    TestMethods,
-    NULL,
-    NULL,
-    NULL,
-    NULL
-};
-
+/* initialization function */
 
-PyMODINIT_FUNC
-PyInit__testinternalcapi(void)
+static int
+module_exec(PyObject *module)
 {
-    PyObject *module = PyModule_Create(&_testcapimodule);
-    if (module == NULL) {
-        return NULL;
-    }
-
     if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD",
                            PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
-        goto error;
+        return 1;
     }
 
-    return module;
+    return 0;
+}
 
-error:
-    Py_DECREF(module);
-    return NULL;
+static struct PyModuleDef_Slot module_slots[] = {
+    {Py_mod_exec, module_exec},
+    {0, NULL},
+};
+
+static int
+module_traverse(PyObject *module, visitproc visit, void *arg)
+{
+    module_state *state = get_module_state(module);
+    assert(state != NULL);
+    traverse_module_state(state, visit, arg);
+    return 0;
+}
+
+static int
+module_clear(PyObject *module)
+{
+    module_state *state = get_module_state(module);
+    assert(state != NULL);
+    (void)clear_module_state(state);
+    return 0;
+}
+
+static void
+module_free(void *module)
+{
+    module_state *state = get_module_state(module);
+    assert(state != NULL);
+    (void)clear_module_state(state);
+}
+
+static struct PyModuleDef _testcapimodule = {
+    .m_base = PyModuleDef_HEAD_INIT,
+    .m_name = MODULE_NAME,
+    .m_doc = NULL,
+    .m_size = sizeof(module_state),
+    .m_methods = module_functions,
+    .m_slots = module_slots,
+    .m_traverse = module_traverse,
+    .m_clear = module_clear,
+    .m_free = (freefunc)module_free,
+};
+
+
+PyMODINIT_FUNC
+PyInit__testinternalcapi(void)
+{
+    return PyModuleDef_Init(&_testcapimodule);
 }
index 479221cbd4b68286b5ee351c6854635461b32df1..cd08782edce484661e5346254958684e8108de59 100644 (file)
@@ -523,7 +523,6 @@ Modules/_asynciomodule.c    -       all_tasks       -
 Modules/_asynciomodule.c       -       current_tasks   -
 Modules/_asynciomodule.c       -       iscoroutine_typecache   -
 Modules/_ctypes/_ctypes.c      -       _ctypes_ptrtype_cache   -
-Modules/_testinternalcapi.c    -       record_list     -
 Modules/_tkinter.c     -       tcl_lock        -
 Modules/_tkinter.c     -       excInCmd        -
 Modules/_tkinter.c     -       valInCmd        -
index 02531692a884472ee68c1e45c3651fe4bd87fbc2..849e20a1b0f4ebec130ceffa2afd99f9d1018e2f 100644 (file)
@@ -483,8 +483,6 @@ Modules/_testcapimodule.c   -       g_type_watchers_installed       -
 Modules/_testimportmultiple.c  -       _barmodule      -
 Modules/_testimportmultiple.c  -       _foomodule      -
 Modules/_testimportmultiple.c  -       _testimportmultiple     -
-Modules/_testinternalcapi.c    -       TestMethods     -
-Modules/_testinternalcapi.c    -       _testcapimodule -
 Modules/_testmultiphase.c      -       Example_Type_slots      -
 Modules/_testmultiphase.c      -       Example_Type_spec       -
 Modules/_testmultiphase.c      -       Example_methods -