Skip to content

Commit 67034c7

Browse files
committed
Convert multiarray to multi-phase init (PEP 489)
1 parent 0145d7c commit 67034c7

File tree

3 files changed

+76
-52
lines changed

3 files changed

+76
-52
lines changed

numpy/_core/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
except ImportError as exc:
2424
import sys
2525

26+
# Bypass for the module re-initialization opt-out
27+
if exc.msg == "cannot load module more than once per process":
28+
raise
29+
2630
# Basically always, the problem should be that the C module is wrong/missing...
2731
if (
2832
isinstance(exc, ModuleNotFoundError)

numpy/_core/src/multiarray/_multiarray_tests.c.src

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,41 +2413,56 @@ static PyMethodDef Multiarray_TestsMethods[] = {
24132413
};
24142414

24152415

2416-
static struct PyModuleDef moduledef = {
2417-
PyModuleDef_HEAD_INIT,
2418-
"_multiarray_tests",
2419-
NULL,
2420-
-1,
2421-
Multiarray_TestsMethods,
2422-
NULL,
2423-
NULL,
2424-
NULL,
2425-
NULL
2426-
};
2416+
static int module_loaded = 0;
24272417

2428-
PyMODINIT_FUNC PyInit__multiarray_tests(void)
2418+
static int
2419+
_multiarray_tests_exec(PyObject *m)
24292420
{
2430-
PyObject *m;
2421+
// https://docs.python.org/3/howto/isolating-extensions.html#opt-out-limiting-to-one-module-object-per-process
2422+
if (module_loaded) {
2423+
PyErr_SetString(PyExc_ImportError,
2424+
"cannot load module more than once per process");
2425+
return -1;
2426+
}
2427+
module_loaded = 1;
24312428

2432-
m = PyModule_Create(&moduledef);
2433-
if (m == NULL) {
2434-
return m;
2429+
if (PyArray_ImportNumPyAPI() < 0) {
2430+
return -1;
24352431
}
2436-
import_array();
24372432
if (init_argparse_mutex() < 0) {
2438-
return NULL;
2433+
return -1;
24392434
}
24402435
if (PyErr_Occurred()) {
24412436
PyErr_SetString(PyExc_RuntimeError,
24422437
"cannot load _multiarray_tests module.");
24432438
}
24442439

2445-
#if Py_GIL_DISABLED
2446-
// signal this module supports running with the GIL disabled
2447-
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
2440+
return 0;
2441+
}
2442+
2443+
static struct PyModuleDef_Slot _multiarray_tests_slots[] = {
2444+
{Py_mod_exec, _multiarray_tests_exec},
2445+
#if PY_VERSION_HEX >= 0x030c00f0 // Python 3.12+
2446+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
2447+
#endif
2448+
#if PY_VERSION_HEX >= 0x030d00f0 // Python 3.13+
2449+
// signal that this module supports running without an active GIL
2450+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
24482451
#endif
2452+
{0, NULL},
2453+
};
2454+
2455+
static struct PyModuleDef moduledef = {
2456+
.m_base = PyModuleDef_HEAD_INIT,
2457+
.m_name = "_multiarray_tests",
2458+
.m_size = 0,
2459+
.m_methods = Multiarray_TestsMethods,
2460+
.m_slots = _multiarray_tests_slots,
2461+
};
24492462

2450-
return m;
2463+
PyMODINIT_FUNC PyInit__multiarray_tests(void)
2464+
{
2465+
return PyModuleDef_Init(&moduledef);
24512466
}
24522467

24532468
NPY_NO_EXPORT int

numpy/_core/src/multiarray/multiarraymodule.c

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4773,28 +4773,19 @@ initialize_thread_unsafe_state(void) {
47734773
return 0;
47744774
}
47754775

4776-
static struct PyModuleDef moduledef = {
4777-
PyModuleDef_HEAD_INIT,
4778-
"_multiarray_umath",
4779-
NULL,
4780-
-1,
4781-
array_module_methods,
4782-
NULL,
4783-
NULL,
4784-
NULL,
4785-
NULL
4786-
};
4776+
static int module_loaded = 0;
47874777

4788-
/* Initialization function for the module */
4789-
PyMODINIT_FUNC PyInit__multiarray_umath(void) {
4790-
PyObject *m, *d, *s;
4791-
PyObject *c_api;
4778+
static int
4779+
_multiarray_umath_exec(PyObject *m) {
4780+
PyObject *d, *s, *c_api;
47924781

4793-
/* Create the module and add the functions */
4794-
m = PyModule_Create(&moduledef);
4795-
if (!m) {
4796-
return NULL;
4782+
// https://docs.python.org/3/howto/isolating-extensions.html#opt-out-limiting-to-one-module-object-per-process
4783+
if (module_loaded) {
4784+
PyErr_SetString(PyExc_ImportError,
4785+
"cannot load module more than once per process");
4786+
return -1;
47974787
}
4788+
module_loaded = 1;
47984789

47994790
/* Initialize CPU features */
48004791
if (npy_cpu_init() < 0) {
@@ -5135,18 +5126,32 @@ PyMODINIT_FUNC PyInit__multiarray_umath(void) {
51355126
goto err;
51365127
}
51375128

5138-
#if Py_GIL_DISABLED
5139-
// signal this module supports running with the GIL disabled
5140-
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
5129+
return 0;
5130+
5131+
err:
5132+
return -1;
5133+
}
5134+
5135+
static struct PyModuleDef_Slot _multiarray_umath_slots[] = {
5136+
{Py_mod_exec, _multiarray_umath_exec},
5137+
#if PY_VERSION_HEX >= 0x030c00f0 // Python 3.12+
5138+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
5139+
#endif
5140+
#if PY_VERSION_HEX >= 0x030d00f0 // Python 3.13+
5141+
// signal that this module supports running without an active GIL
5142+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
51415143
#endif
5144+
{0, NULL},
5145+
};
51425146

5143-
return m;
5147+
static struct PyModuleDef moduledef = {
5148+
.m_base = PyModuleDef_HEAD_INIT,
5149+
.m_name = "_multiarray_umath",
5150+
.m_size = 0,
5151+
.m_methods = array_module_methods,
5152+
.m_slots = _multiarray_umath_slots,
5153+
};
51445154

5145-
err:
5146-
if (!PyErr_Occurred()) {
5147-
PyErr_SetString(PyExc_RuntimeError,
5148-
"cannot load multiarray module.");
5149-
}
5150-
Py_DECREF(m);
5151-
return NULL;
5155+
PyMODINIT_FUNC PyInit__multiarray_umath(void) {
5156+
return PyModuleDef_Init(&moduledef);
51525157
}

0 commit comments

Comments
 (0)