PEP 489: Multi-phase extension module initialization
Known limitations of the current implementation:
- documentation changes are incomplete
- there's a reference leak I haven't tracked down yet
The leak is most visible by running:
./python -m test -R3:3 test_importlib
However, you can also see it by running:
./python -X showrefcount
Importing the array or _testmultiphase modules, and
then deleting them from both sys.modules and the local
namespace shows significant increases in the total
number of active references each cycle. By contrast,
with _testcapi (which continues to use single-phase
initialisation) the global refcounts stabilise after
a couple of cycles.
diff --git a/Modules/xxsubtype.c b/Modules/xxsubtype.c
index 6944e37..8d0d6ae 100644
--- a/Modules/xxsubtype.c
+++ b/Modules/xxsubtype.c
@@ -257,13 +257,50 @@
{NULL, NULL} /* sentinel */
};
+static int
+xxsubtype_exec(PyObject* m)
+{
+ /* Fill in deferred data addresses. This must be done before
+ PyType_Ready() is called. Note that PyType_Ready() automatically
+ initializes the ob.ob_type field to &PyType_Type if it's NULL,
+ so it's not necessary to fill in ob_type first. */
+ spamdict_type.tp_base = &PyDict_Type;
+ if (PyType_Ready(&spamdict_type) < 0)
+ return -1;
+
+ spamlist_type.tp_base = &PyList_Type;
+ if (PyType_Ready(&spamlist_type) < 0)
+ return -1;
+
+ if (PyType_Ready(&spamlist_type) < 0)
+ return -1;
+ if (PyType_Ready(&spamdict_type) < 0)
+ return -1;
+
+ Py_INCREF(&spamlist_type);
+ if (PyModule_AddObject(m, "spamlist",
+ (PyObject *) &spamlist_type) < 0)
+ return -1;
+
+ Py_INCREF(&spamdict_type);
+ if (PyModule_AddObject(m, "spamdict",
+ (PyObject *) &spamdict_type) < 0)
+ return -1;
+ return 0;
+}
+
+static struct PyModuleDef_Slot xxsubtype_slots[] = {
+ {Py_mod_exec, xxsubtype_exec},
+ {0, NULL},
+};
+
static struct PyModuleDef xxsubtypemodule = {
PyModuleDef_HEAD_INIT,
"xxsubtype",
xxsubtype__doc__,
- -1,
+ 0,
xxsubtype_functions,
- NULL,
+ xxsubtype_slots,
NULL,
NULL,
NULL
@@ -273,37 +310,5 @@
PyMODINIT_FUNC
PyInit_xxsubtype(void)
{
- PyObject *m;
-
- /* Fill in deferred data addresses. This must be done before
- PyType_Ready() is called. Note that PyType_Ready() automatically
- initializes the ob.ob_type field to &PyType_Type if it's NULL,
- so it's not necessary to fill in ob_type first. */
- spamdict_type.tp_base = &PyDict_Type;
- if (PyType_Ready(&spamdict_type) < 0)
- return NULL;
-
- spamlist_type.tp_base = &PyList_Type;
- if (PyType_Ready(&spamlist_type) < 0)
- return NULL;
-
- m = PyModule_Create(&xxsubtypemodule);
- if (m == NULL)
- return NULL;
-
- if (PyType_Ready(&spamlist_type) < 0)
- return NULL;
- if (PyType_Ready(&spamdict_type) < 0)
- return NULL;
-
- Py_INCREF(&spamlist_type);
- if (PyModule_AddObject(m, "spamlist",
- (PyObject *) &spamlist_type) < 0)
- return NULL;
-
- Py_INCREF(&spamdict_type);
- if (PyModule_AddObject(m, "spamdict",
- (PyObject *) &spamdict_type) < 0)
- return NULL;
- return m;
+ return PyModuleDef_Init(&xxsubtypemodule);
}